├── randao ├── .gitignore ├── deploy │ ├── constructor_args.txt │ └── deploy_Randao.sh ├── remappings.txt ├── foundry.toml ├── src │ ├── interfaces │ │ └── IRandao.sol │ └── Randao.sol └── test │ ├── data │ └── test.calldata │ └── Randao.t.sol ├── account-age ├── .gitignore ├── deploy │ ├── constructor_args.txt │ └── deploy_AccountAge.sh ├── remappings.txt ├── foundry.toml ├── README.md ├── test │ ├── AccountAge.t.sol │ └── data │ │ └── input.json └── src │ └── AccountAge.sol ├── uniswap-v2-twap ├── .gitignore ├── deploy │ ├── constructor_args.txt │ └── deploy_UniswapV2Twap.sh ├── remappings.txt ├── foundry.toml ├── README.md ├── test │ ├── UniswapV2Twap.t.sol │ └── data │ │ └── input.json └── src │ └── UniswapV2Twap.sol ├── uniswap-v3-twap ├── deploy │ ├── constructor_args.txt │ └── deploy_UniswapV3Oracle.sh ├── .env.example ├── .gitignore ├── remappings.txt ├── foundry.toml ├── README.md ├── LICENSE ├── test │ ├── UniswapV3Oracle.t.sol │ └── data │ │ └── input.json └── src │ ├── Oracle.sol │ ├── IUniswapV3Oracle.sol │ └── UniswapV3Oracle.sol ├── remappings.txt ├── .env.example ├── .gitignore ├── .gitmodules ├── lib └── utils │ ├── UQ112x112.sol │ ├── ReadQueryData.sol │ └── RLPReader.sol ├── README.md └── LICENSE /randao/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | broadcast/ 3 | cache/ 4 | out/ 5 | -------------------------------------------------------------------------------- /account-age/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | broadcast/ 3 | cache/ 4 | out/ 5 | -------------------------------------------------------------------------------- /uniswap-v2-twap/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | broadcast/ 3 | cache/ 4 | out/ 5 | -------------------------------------------------------------------------------- /account-age/deploy/constructor_args.txt: -------------------------------------------------------------------------------- 1 | 0xd617ab7f787adF64C2b5B920c251ea10Cd35a952 -------------------------------------------------------------------------------- /randao/deploy/constructor_args.txt: -------------------------------------------------------------------------------- 1 | 0x33ea514cc54b641aD8b84e4A31D311f3722D1BB5 15537393 -------------------------------------------------------------------------------- /uniswap-v2-twap/deploy/constructor_args.txt: -------------------------------------------------------------------------------- 1 | 0xd617ab7f787adF64C2b5B920c251ea10Cd35a952 -------------------------------------------------------------------------------- /uniswap-v3-twap/deploy/constructor_args.txt: -------------------------------------------------------------------------------- 1 | 0xd617ab7f787adF64C2b5B920c251ea10Cd35a952 -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/ 4 | yul/=lib/yul/ 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # export ANVIL_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 3 | export MAINNET_RPC_URL= 4 | export GOERLI_RPC_URL= -------------------------------------------------------------------------------- /uniswap-v3-twap/.env.example: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ANVIL_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 3 | INFURA_ID= 4 | ETH_RPC_URL="https://mainnet.infura.io/v3/$INFURA_ID" 5 | -------------------------------------------------------------------------------- /randao/remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=../lib/forge-std/lib/ds-test/src/ 2 | forge-std/=../lib/forge-std/src/ 3 | openzeppelin-contracts/=../lib/openzeppelin-contracts/contracts/ 4 | utils/=../lib/utils/ 5 | axiom-contracts/=../lib/axiom-v1-contracts/ -------------------------------------------------------------------------------- /account-age/remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=../lib/forge-std/lib/ds-test/src/ 2 | forge-std/=../lib/forge-std/src/ 3 | openzeppelin-contracts/=../lib/openzeppelin-contracts/contracts/ 4 | axiom-contracts/=../lib/axiom-v1-contracts/ 5 | utils/=../lib/utils/ -------------------------------------------------------------------------------- /uniswap-v3-twap/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | -------------------------------------------------------------------------------- /uniswap-v2-twap/remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=../lib/forge-std/lib/ds-test/src/ 2 | forge-std/=../lib/forge-std/src/ 3 | openzeppelin-contracts/=../lib/openzeppelin-contracts/contracts/ 4 | utils/=../lib/utils/ 5 | axiom-contracts/=../lib/axiom-v1-contracts/ 6 | -------------------------------------------------------------------------------- /uniswap-v3-twap/remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=../lib/forge-std/lib/ds-test/src/ 2 | forge-std/=../lib/forge-std/src/ 3 | openzeppelin-contracts/=../lib/openzeppelin-contracts/contracts/ 4 | utils/=../lib/utils/ 5 | axiom-contracts/=../lib/axiom-v1-contracts/ 6 | -------------------------------------------------------------------------------- /randao/deploy/deploy_Randao.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(git rev-parse --show-toplevel) 3 | source .env 4 | 5 | cd randao 6 | forge create --ledger --rpc-url $MAINNET_RPC_URL --constructor-args-path deploy/constructor_args.txt --verify --etherscan-api-key $ETHERSCAN_API_KEY --force src/Randao.sol:Randao -------------------------------------------------------------------------------- /uniswap-v2-twap/deploy/deploy_UniswapV2Twap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(git rev-parse --show-toplevel) 3 | source .env 4 | 5 | cd uniswap-v2-twap 6 | forge create --ledger --rpc-url $MAINNET_RPC_URL --constructor-args-path deploy/constructor_args.txt --verify --etherscan-api-key $ETHERSCAN_API_KEY --force src/UniswapV2Twap.sol:UniswapV2Twap -------------------------------------------------------------------------------- /uniswap-v3-twap/deploy/deploy_UniswapV3Oracle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(git rev-parse --show-toplevel) 3 | source .env 4 | 5 | cd uniswap-v3-twap 6 | forge create --ledger --rpc-url $MAINNET_RPC_URL --constructor-args-path deploy/constructor_args.txt --verify --etherscan-api-key $ETHERSCAN_API_KEY --force src/UniswapV3Oracle.sol:UniswapV3Oracle -------------------------------------------------------------------------------- /randao/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | ffi = true 5 | libs = ['../lib'] 6 | fs_permissions = [{ access = "read", path = "./test/data"}] 7 | solc = "0.8.19" 8 | 9 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 10 | [rpc_endpoints] 11 | mainnet = "${MAINNET_RPC_URL}" 12 | goerli = "${GOERLI_RPC_URL}" -------------------------------------------------------------------------------- /account-age/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | ffi = true 5 | libs = ['../lib'] 6 | fs_permissions = [{ access = "read", path = "./test/data"}] 7 | via_ir = true 8 | solc = "0.8.19" 9 | 10 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 11 | [rpc_endpoints] 12 | mainnet = "${MAINNET_RPC_URL}" 13 | goerli = "${GOERLI_RPC_URL}" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Env files 13 | **/.env 14 | -------------------------------------------------------------------------------- /uniswap-v2-twap/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | ffi = true 5 | libs = ['../lib'] 6 | fs_permissions = [{ access = "read", path = "./test/data"}] 7 | solc = "0.8.19" 8 | via_ir = true 9 | 10 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 11 | [rpc_endpoints] 12 | mainnet = "${MAINNET_RPC_URL}" 13 | goerli = "${GOERLI_RPC_URL}" 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/openzeppelin-contracts"] 2 | path = lib/openzeppelin-contracts 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 4 | [submodule "lib/forge-std"] 5 | path = lib/forge-std 6 | url = https://github.com/foundry-rs/forge-std 7 | [submodule "lib/forge-std/"] 8 | branch = v1.2.0 9 | [submodule "lib/axiom-v1-contracts"] 10 | path = lib/axiom-v1-contracts 11 | url = https://github.com/axiom-crypto/axiom-v1-contracts 12 | branch = main -------------------------------------------------------------------------------- /randao/src/interfaces/IRandao.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import {IAxiomV1} from "axiom-contracts/contracts/interfaces/IAxiomV1.sol"; 5 | 6 | interface IRandao { 7 | function prevRandaos(uint32 blockNumber) external view returns (uint256); 8 | function axiomAddress() external view returns (address); 9 | function mergeBlock() external view returns (uint32); 10 | 11 | function verifyRandao(IAxiomV1.BlockHashWitness calldata witness, bytes calldata header) external; 12 | } -------------------------------------------------------------------------------- /uniswap-v3-twap/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | ffi = true 5 | libs = ['../lib'] 6 | optimizer = true 7 | optimizer_runs = 999999 8 | fs_permissions = [{ access = "read", path = "./test/data"}] 9 | solc = "0.8.19" 10 | 11 | [profile.default.optimizer_details] 12 | constantOptimizer = true 13 | yul = true 14 | via_ir = true 15 | 16 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 17 | 18 | [rpc_endpoints] 19 | mainnet = "${MAINNET_RPC_URL}" 20 | goerli = "${GOERLI_RPC_URL}" 21 | -------------------------------------------------------------------------------- /account-age/README.md: -------------------------------------------------------------------------------- 1 | # Axiom Account Age App 2 | 3 | ## Smart Contract Testing 4 | 5 | We use [foundry](https://book.getfoundry.sh/) for smart contract development and testing. You can follow these [instructions](https://book.getfoundry.sh/getting-started/installation) to install it. 6 | We fork mainnet for tests, so make sure that `.env` variables have been [exported](../README.md#environmental-variables). 7 | 8 | After installing `foundry`, in the [`contracts`](contracts/) directory, run: 9 | 10 | ```bash 11 | forge install 12 | forge test 13 | ``` 14 | 15 | For verbose logging of events and gas tracking, run 16 | 17 | ```bash 18 | forge test -vvvv 19 | ``` 20 | -------------------------------------------------------------------------------- /uniswap-v2-twap/README.md: -------------------------------------------------------------------------------- 1 | # Trustless Uniswap V2 TWAP via ZK (Axiom) 2 | 3 | ## Smart Contract Testing 4 | 5 | We use [foundry](https://book.getfoundry.sh/) for smart contract development and testing. You can follow these [instructions](https://book.getfoundry.sh/getting-started/installation) to install it. 6 | We fork mainnet for tests, so make sure that `.env` variables have been [exported](../README.md#environmental-variables). 7 | 8 | After installing `foundry`, in the [`contracts`](contracts/) directory, run: 9 | 10 | ```bash 11 | forge install 12 | forge test 13 | ``` 14 | 15 | For verbose logging of events and gas tracking, run 16 | 17 | ```bash 18 | forge test -vvvv 19 | ``` 20 | -------------------------------------------------------------------------------- /uniswap-v3-twap/README.md: -------------------------------------------------------------------------------- 1 | # Trustless Uniswap V3 Oracles via ZK (Axiom) 2 | 3 | ## Smart Contract Testing 4 | 5 | We use [foundry](https://book.getfoundry.sh/) for smart contract development and testing. You can follow these [instructions](https://book.getfoundry.sh/getting-started/installation) to install it. 6 | We fork mainnet for tests, so make sure that `.env` variables have been [exported](../README.md#environmental-variables). 7 | 8 | After installing `foundry`, in the [`contracts`](contracts/) directory, run: 9 | 10 | ```bash 11 | forge install 12 | forge test 13 | ``` 14 | 15 | For verbose logging of events and gas tracking, run 16 | 17 | ```bash 18 | forge test -vvvv 19 | ``` 20 | -------------------------------------------------------------------------------- /lib/utils/UQ112x112.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) 5 | 6 | // range: [0, 2**112 - 1] 7 | // resolution: 1 / 2**112 8 | 9 | library UQ112x112 { 10 | uint224 constant Q112 = 2**112; 11 | 12 | // encode a uint112 as a UQ112x112 13 | function encode(uint112 y) internal pure returns (uint224 z) { 14 | z = uint224(y) * Q112; // never overflows 15 | } 16 | 17 | // divide a UQ112x112 by a uint112, returning a UQ112x112 18 | function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) { 19 | z = x / uint224(y); 20 | } 21 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axiom Apps 2 | 3 | Demo applications built using core Axiom functionality. These apps are live on mainnet and you can try them out at [demo.axiom.xyz](https://demo.axiom.xyz). 4 | 5 | ## Setup 6 | 7 | Clone this repository (and git submodule dependencies) with 8 | 9 | ```bash 10 | git clone --recurse-submodules -j8 https://github.com/axiom-crypto/axiom-apps.git 11 | cd axiom-apps 12 | ``` 13 | 14 | ### Environmental variables 15 | 16 | ```bash 17 | cp .env.example .env 18 | ``` 19 | 20 | Fill in `.env` with your RPC provider URLs. In order for Forge to access these endpoints for testing, we need to source `.env`: 21 | 22 | ```bash 23 | source .env 24 | ``` 25 | 26 | More detailed instructions are provided in each app's individual readme. 27 | -------------------------------------------------------------------------------- /account-age/deploy/deploy_AccountAge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(git rev-parse --show-toplevel) 3 | source .env 4 | 5 | cd account-age 6 | forge create --ledger --rpc-url $MAINNET_RPC_URL --constructor-args-path deploy/constructor_args.txt --verify --etherscan-api-key $ETHERSCAN_API_KEY --force src/AccountAge.sol:AccountAge 7 | 8 | # this actually had problems verifying because viaIR was turned on 9 | 10 | # verified later using 11 | # forge flatten src/AccountAge.sol > src/AccountAge.flatten.sol 12 | # forge verify-contract --constructor-args-path deploy/constructor_args.txt 0xDd215c64BB70868cC0D45bF3f7c3d97A074920b2 src/AccountAge.flatten.sol:AccountAge --etherscan-api-key $ETHERSCAN_API_KEY 13 | 14 | # using: https://github.com/foundry-rs/foundry/issues/3507#issuecomment-1465382107 15 | -------------------------------------------------------------------------------- /randao/test/data/test.calldata: -------------------------------------------------------------------------------- 1 | f9020fa03b4e49db58e4dab1931689ab67e24eb79660f2661034280bf8f4480071294456a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794690b9a9e9aa1c9db991c7721a92d351db4fac990a001a7d774a0b21acfca2e27b579fb77b4f55fb34c51eefccd7567b74e4e62397ba002113190ff830a3e9e66b1ff63624e428ef3c88b6864a0c03a6fdfc53d517923a0cecdb65e5f0622279897c52f6775e7e24ea7d0c622b222ba6257115416823140b901005821c486554080c1100aa04c8064182c0f4d0041092b049010e158c0022a1020708727868103028a1044408b1c880524da0102408a00e689225a34d0322ea08025021901001652186e03538c8e3070e4130c28880a408c0150a39b90c858080b922118d00b8bc0422b6a1a281c4108311600830280343405002011944108a13c0222e4680082006e427c2146431600ad80a021016174a4c9ae01076aa510008cab58a113198ae812805520f488108210c658e52a108850020a800d9d99060137009c300271911043880210060208a20f440182e830a9145901040966b0482500371d2488a055500a1008a38040021c1480304052012841600938b871d000ad288083fbe9758401c9c38083a305cd8463d5d0a38e6279206275696c64657230783639a0a97f47e048f1ddf11b7a8d2c265014cfcec2506d765a393406f6046960d059dc880000000000000000850315807ccb -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Axiom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /uniswap-v3-twap/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Axiom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /account-age/test/AccountAge.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | import "forge-std/Test.sol"; 5 | import {stdJson} from "forge-std/StdJson.sol"; 6 | import "axiom-contracts/contracts/interfaces/IAxiomV1Query.sol"; 7 | import "../src/AccountAge.sol"; 8 | import "forge-std/console.sol"; 9 | import "utils/ReadQueryData.sol"; 10 | 11 | contract AccountAgeTest is Test { 12 | address AXIOM_QUERY_ADDRESS = 0x82842F7a41f695320CC255B34F18769D68dD8aDF; 13 | 14 | function setUp() public { 15 | vm.createSelectFork("goerli", 9213668); 16 | } 17 | 18 | function getTestData() public view returns (IAxiomV1Query.AccountResponse[] memory, bytes32[3] memory) { 19 | string memory root = vm.projectRoot(); 20 | string memory path = string.concat(root, "/test/data/input.json"); 21 | string memory json = vm.readFile(path); 22 | 23 | ReadQueryData.QueryResponse memory qr = ReadQueryData.readQueryResponses(json); 24 | 25 | return (qr.accountResponses, qr.keccakResponses); 26 | } 27 | 28 | function testCheckAccountAge() public { 29 | vm.pauseGasMetering(); 30 | AccountAge accountAge = new AccountAge(AXIOM_QUERY_ADDRESS); 31 | (IAxiomV1Query.AccountResponse[] memory accountProofs, bytes32[3] memory keccakResponses) = getTestData(); 32 | vm.resumeGasMetering(); 33 | accountAge.verifyAge(accountProofs, keccakResponses); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /uniswap-v3-twap/test/UniswapV3Oracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | import "forge-std/Test.sol"; 5 | import "axiom-contracts/contracts/interfaces/IAxiomV1Query.sol"; 6 | import "../src/UniswapV3Oracle.sol"; 7 | import "forge-std/console.sol"; 8 | import "utils/ReadQueryData.sol"; 9 | 10 | contract UinswapV3OracleTest is Test { 11 | address AXIOM_QUERY_ADDRESS = 0x4Fb202140c5319106F15706b1A69E441c9536306; 12 | 13 | function setUp() public { 14 | vm.createSelectFork("goerli", 9290313); 15 | } 16 | 17 | function getTestData() public view returns (IAxiomV1Query.StorageResponse[] memory, bytes32[3] memory) { 18 | string memory root = vm.projectRoot(); 19 | //TODO: get working input data 20 | string memory path = string.concat(root, "/test/data/input.json"); 21 | string memory json = vm.readFile(path); 22 | 23 | ReadQueryData.QueryResponse memory qr = ReadQueryData.readQueryResponses(json); 24 | 25 | return (qr.storageResponses, qr.keccakResponses); 26 | } 27 | 28 | function testCalculateUniswapV3Twap() public { 29 | vm.pauseGasMetering(); 30 | UniswapV3Oracle uniswapV3Oracle = new UniswapV3Oracle(AXIOM_QUERY_ADDRESS); 31 | (IAxiomV1Query.StorageResponse[] memory storageResponses, bytes32[3] memory keccakResponses) = getTestData(); 32 | vm.resumeGasMetering(); 33 | uniswapV3Oracle.verifyUniswapV3TWAP(storageResponses, keccakResponses); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /uniswap-v3-twap/test/data/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "keccakResponses": { 3 | "keccakBlockResponse": "0x30030c909ace6c24bc3b827fd4abe1b7dd97a1f3ccebea956b2688f08f424a4d", 4 | "keccakAccountResponse": "0x139d6047b6cd1d6b9e11fdcee43bfcd8a7bbd4233e9abac910e4ba13ddc5f671", 5 | "keccakStorageResponse": "0x38fdfa52afbc5c8cc1f58045a26c75af66b568b6b8fcb0689b3324dabf58d458" 6 | }, 7 | "storageResponses": [ 8 | { 9 | "blockNumber": "0x67711f", 10 | "addr": "0x6337b3caf9c5236c7f3d1694410776119edaf9fa", 11 | "slot": "0x08", 12 | "value": "0x0100598e4d000000025d9579103e2bda07b596a74e00038a98c4518f625091c7", 13 | "leafIdx": "0x00", 14 | "proof": [ 15 | "0xfa0cd0e6d99d638e896cae9930fb5379117656c4f34f928438de4e0af8e45b1f", 16 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 17 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 18 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 19 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 20 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 21 | ] 22 | }, 23 | { 24 | "blockNumber": "0x76af77", 25 | "addr": "0x6337b3caf9c5236c7f3d1694410776119edaf9fa", 26 | "slot": "0x08", 27 | "value": "0x0100adfb9400000757513914dc4d218055ce3828e80006b73aaebf47634b9be8", 28 | "leafIdx": "0x01", 29 | "proof": [ 30 | "0x37767e528346db7c3394a3e45f95f0854287bbbdc2b8586d959642d00c5a5489", 31 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 32 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 33 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 34 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 35 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /uniswap-v2-twap/test/UniswapV2Twap.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | import "forge-std/Test.sol"; 5 | import {stdJson} from "forge-std/StdJson.sol"; 6 | import "axiom-contracts/contracts/interfaces/IAxiomV1Query.sol"; 7 | import "../src/UniswapV2Twap.sol"; 8 | import "forge-std/console.sol"; 9 | import "utils/ReadQueryData.sol"; 10 | 11 | contract UniswapV2TwapTest is Test { 12 | address AXIOM_QUERY_ADDRESS = 0x4Fb202140c5319106F15706b1A69E441c9536306; 13 | 14 | function setUp() public { 15 | vm.createSelectFork("goerli", 9295080); 16 | } 17 | 18 | function getTestData() 19 | public 20 | view 21 | returns ( 22 | IAxiomV1Query.StorageResponse[] memory, 23 | IAxiomV1Query.BlockResponse[] memory, 24 | bytes[2] memory, 25 | bytes32[3] memory 26 | ) 27 | { 28 | string memory root = vm.projectRoot(); 29 | string memory path = string.concat(root, "/test/data/input.json"); 30 | string memory json = vm.readFile(path); 31 | 32 | ReadQueryData.QueryResponse memory qr = ReadQueryData.readQueryResponses(json); 33 | 34 | bytes[] memory rlpHeaders = abi.decode(stdJson.parseRaw(json, ".blockHeaders"), (bytes[])); 35 | require(rlpHeaders.length == 2, "Invalid rlp headers length"); 36 | bytes[2] memory rlpEncodedHeaders = [rlpHeaders[0], rlpHeaders[1]]; 37 | 38 | return (qr.storageResponses, qr.blockResponses, rlpEncodedHeaders, qr.keccakResponses); 39 | } 40 | 41 | function testCalculateUniswapV2Twap() public { 42 | vm.pauseGasMetering(); 43 | UniswapV2Twap uniswapV2Twap = new UniswapV2Twap(AXIOM_QUERY_ADDRESS); 44 | ( 45 | IAxiomV1Query.StorageResponse[] memory storageResponses, 46 | IAxiomV1Query.BlockResponse[] memory blockResponses, 47 | bytes[2] memory rlpHeaders, 48 | bytes32[3] memory keccakResponses 49 | ) = getTestData(); 50 | vm.resumeGasMetering(); 51 | uniswapV2Twap.calculateUniswapV2Twap(storageResponses, blockResponses, rlpHeaders, keccakResponses); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /uniswap-v3-twap/src/Oracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Cannot direct import from @uniswap/v3-core since pragma needs to be changed from <0.8.0 to <0.9.0 for compatibility 3 | pragma solidity >=0.5.0 <0.9.0; 4 | 5 | /// @title Oracle 6 | /// @notice Provides price and liquidity data useful for a wide variety of system designs 7 | /// @dev Instances of stored oracle data, "observations", are collected in the oracle array 8 | /// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the 9 | /// maximum length of the oracle array. New slots will be added when the array is fully populated. 10 | /// Observations are overwritten when the full length of the oracle array is populated. 11 | /// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() 12 | library Oracle { 13 | /// @notice Taken from https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/Oracle.sol 14 | struct Observation { 15 | // the block timestamp of the observation 16 | uint32 blockTimestamp; 17 | // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized 18 | int56 tickCumulative; 19 | // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized 20 | uint160 secondsPerLiquidityCumulativeX128; 21 | // whether or not the observation is initialized 22 | bool initialized; 23 | } 24 | 25 | /// @dev For testing purposes only 26 | /// @notice Returns Observation as it is laid out in EVM storage: 27 | /// concatenation of `initialized . secondsPerLiquidityCumulativeX128 . tickCumulative . blockTimestamp` 28 | function pack(Observation memory observation) public pure returns (bytes32 packed) { 29 | packed = bytes32( 30 | bytes.concat( 31 | bytes1(observation.initialized ? 0x01 : 0x00), 32 | bytes20(observation.secondsPerLiquidityCumulativeX128), 33 | bytes7(uint56(observation.tickCumulative)), 34 | bytes4(observation.blockTimestamp) 35 | ) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /randao/src/Randao.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // WARNING! This smart contract has not been audited. 3 | // DO NOT USE THIS CONTRACT FOR PRODUCTION 4 | // This is an example contract to demonstrate how to integrate an application with the audited production release of AxiomV1. 5 | pragma solidity 0.8.19; 6 | 7 | import {IAxiomV1} from "axiom-contracts/contracts/interfaces/IAxiomV1.sol"; 8 | import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; 9 | import {RLPReader} from "utils/RLPReader.sol"; 10 | 11 | contract Randao is Ownable { 12 | using RLPReader for RLPReader.RLPItem; 13 | using RLPReader for bytes; 14 | 15 | address public axiomAddress; 16 | uint32 public mergeBlock; 17 | 18 | // mapping between blockNumber and prevRandao 19 | mapping(uint32 => uint256) public prevRandaos; 20 | 21 | event RandaoProof(uint32 blockNumber, uint256 prevRandao); 22 | 23 | event UpdateAxiomAddress(address newAddress); 24 | 25 | constructor(address _axiomAddress, uint32 _mergeBlock) { 26 | axiomAddress = _axiomAddress; 27 | mergeBlock = _mergeBlock; 28 | emit UpdateAxiomAddress(_axiomAddress); 29 | } 30 | 31 | function updateAxiomAddress(address _axiomAddress) external onlyOwner { 32 | axiomAddress = _axiomAddress; 33 | emit UpdateAxiomAddress(_axiomAddress); 34 | } 35 | 36 | function verifyRandao(IAxiomV1.BlockHashWitness calldata witness, bytes calldata header) external { 37 | if (block.number - witness.blockNumber <= 256) { 38 | require( 39 | IAxiomV1(axiomAddress).isRecentBlockHashValid(witness.blockNumber, witness.claimedBlockHash), 40 | "Block hash was not validated in cache" 41 | ); 42 | } else { 43 | require(IAxiomV1(axiomAddress).isBlockHashValid(witness), "Block hash was not validated in cache"); 44 | } 45 | 46 | require(witness.blockNumber > mergeBlock, "prevRandao is not valid before merge block"); 47 | require(witness.claimedBlockHash == keccak256(header), "Claimed block hash does not match header"); 48 | 49 | RLPReader.RLPItem[] memory headerItems = header.toRlpItem().toList(); 50 | uint256 prevRandao = headerItems[13].toUint(); 51 | 52 | prevRandaos[witness.blockNumber] = prevRandao; 53 | emit RandaoProof(witness.blockNumber, prevRandao); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /uniswap-v3-twap/src/IUniswapV3Oracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // WARNING! This smart contract has not been audited. 3 | // DO NOT USE THIS CONTRACT FOR PRODUCTION 4 | // This is an example contract to demonstrate how to integrate an application with the audited production release of AxiomV1 and AxiomV1Query. 5 | pragma solidity 0.8.19; 6 | 7 | import {IAxiomV1Query} from "axiom-contracts/contracts/AxiomV1Query.sol"; 8 | import {Oracle} from "./Oracle.sol"; 9 | 10 | interface IUniswapV3Oracle { 11 | /// @notice Mapping between abi.encodePacked(address poolAddress, uint32 startBlockNumber, uint32 endBlockNumber) 12 | /// => keccak(abi.encodePacked(bytes32 startObservationPacked, bytes32 endObservationPacked)) where observationPacked 13 | /// is the packing of Oracle.Observation observation into 32 bytes: 14 | /// bytes32(bytes1(0x0) . secondsPerLiquidityCumulativeX128 . tickCumulative . blockTimestamp) 15 | /// @dev This is the same as how Oracle.Observation is laid out in EVM storage EXCEPT that we set initialized = false (for some gas optimization reasons) 16 | function twapObservations(bytes28) external view returns (bytes32); 17 | 18 | event UniswapV3TwapProof( 19 | address poolAddress, 20 | uint32 startBlockNumber, 21 | uint32 endBlockNumber, 22 | Oracle.Observation startObservation, 23 | Oracle.Observation endObservation 24 | ); 25 | 26 | /// @notice Verify a ZK proof of a Uniswap V3 TWAP oracle observation and verifies the validity of checkpoint blockhashes using Axiom. 27 | /// Caches the [hash of] raw observations for future use. 28 | /// Returns the time (seconds) weighted average tick (geometric mean) and the time (seconds) weight average liquidity (harmonic mean). 29 | /// @dev We provide the time weighted average tick and time weighted average inverse liquidity for convenience, but return 30 | /// the full Observations in case developers want more fine-grained calculations of the oracle observations. 31 | /// For example the price can be calculated from the tick by P = 1.0001^tick 32 | function verifyUniswapV3TWAP( 33 | IAxiomV1Query.StorageResponse[] calldata storageProofs, 34 | bytes32[3] calldata keccakResponses 35 | ) 36 | external 37 | returns ( 38 | int56 twaTick, 39 | uint160 twaLiquidity, 40 | Oracle.Observation memory startObservation, 41 | Oracle.Observation memory endObservation 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /account-age/src/AccountAge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // WARNING! This smart contract has not been audited. 3 | // DO NOT USE THIS CONTRACT FOR PRODUCTION 4 | // This is an example contract to demonstrate how to integrate an application with the audited production release of AxiomV1 and AxiomV1Query. 5 | pragma solidity 0.8.19; 6 | 7 | import {IAxiomV1Query} from "axiom-contracts/contracts/interfaces/IAxiomV1Query.sol"; 8 | import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; 9 | 10 | contract AccountAge is Ownable { 11 | address public axiomQueryAddress; 12 | 13 | mapping(address => uint32) public birthBlocks; 14 | 15 | event UpdateAxiomQueryAddress(address newAddress); 16 | event AccountAgeVerified(address account, uint32 birthBlock); 17 | 18 | constructor(address _axiomQueryAddress) { 19 | axiomQueryAddress = _axiomQueryAddress; 20 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 21 | } 22 | 23 | function updateAxiomQueryAddress(address _axiomQueryAddress) external onlyOwner { 24 | axiomQueryAddress = _axiomQueryAddress; 25 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 26 | } 27 | 28 | function verifyAge(IAxiomV1Query.AccountResponse[] calldata accountProofs, bytes32[3] calldata keccakResponses) 29 | external 30 | { 31 | require(accountProofs.length == 2, "Too many account proofs"); 32 | address account = accountProofs[0].addr; 33 | require(account == accountProofs[1].addr, "Accounts are not the same"); 34 | require(accountProofs[0].blockNumber + 1 == accountProofs[1].blockNumber, "Block numbers are not consecutive"); 35 | require(accountProofs[0].nonce == 0, "Prev block nonce is not 0"); 36 | require(accountProofs[1].nonce > 0, "No account transactions in curr block"); 37 | uint256 addrSize; 38 | assembly { 39 | addrSize := extcodesize(account) 40 | } 41 | require(addrSize == 0, "Account is a contract"); 42 | 43 | require( 44 | IAxiomV1Query(axiomQueryAddress).areResponsesValid( 45 | keccakResponses[0], 46 | keccakResponses[1], 47 | keccakResponses[2], 48 | new IAxiomV1Query.BlockResponse[](0), 49 | accountProofs, 50 | new IAxiomV1Query.StorageResponse[](0) 51 | ), 52 | "Proof not valid" 53 | ); 54 | 55 | birthBlocks[account] = accountProofs[0].blockNumber; 56 | emit AccountAgeVerified(account, accountProofs[0].blockNumber); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /account-age/test/data/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "currBlock": { 3 | "blockNumber": "0x8bfaad", 4 | "addr": "0x897dDbe14c9C7736EbfDC58461355697FbF70048", 5 | "nonce": "0x1", 6 | "balance": "0x455899e612881000", 7 | "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 8 | "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 9 | "leafIdx": "0x1", 10 | "proof": [ 11 | "0x005fa7dafbc6cbfb8611b8832bd1e545820e4a6180087bcc8d31135d574c241e", 12 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 13 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 14 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 15 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 16 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 17 | ] 18 | }, 19 | "prevBlock": { 20 | "blockNumber": "0x8bfaac", 21 | "addr": "0x897dDbe14c9C7736EbfDC58461355697FbF70048", 22 | "nonce": "0x0", 23 | "balance": "0x4563918244f40000", 24 | "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 25 | "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 26 | "leafIdx": "0x0", 27 | "proof": [ 28 | "0x8527a467eca5669d12e2dfdd0191cf9a641815302363e78877964b0e030c5052", 29 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 30 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 31 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 32 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 33 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 34 | ] 35 | }, 36 | "accountResponses": [ 37 | { 38 | "blockNumber": "0x8bfaac", 39 | "addr": "0x897dDbe14c9C7736EbfDC58461355697FbF70048", 40 | "nonce": "0x0", 41 | "balance": "0x4563918244f40000", 42 | "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 43 | "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 44 | "leafIdx": "0x0", 45 | "proof": [ 46 | "0x8527a467eca5669d12e2dfdd0191cf9a641815302363e78877964b0e030c5052", 47 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 48 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 49 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 50 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 51 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 52 | ] 53 | }, 54 | { 55 | "blockNumber": "0x8bfaad", 56 | "addr": "0x897dDbe14c9C7736EbfDC58461355697FbF70048", 57 | "nonce": "0x1", 58 | "balance": "0x455899e612881000", 59 | "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 60 | "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 61 | "leafIdx": "0x1", 62 | "proof": [ 63 | "0x005fa7dafbc6cbfb8611b8832bd1e545820e4a6180087bcc8d31135d574c241e", 64 | "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", 65 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 66 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 67 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 68 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 69 | ] 70 | } 71 | ], 72 | "keccakResponses": { 73 | "keccakBlockResponse": "0xe30c61ab41963c072045193073ea3fe5dbb8277f632f724d5baf8dfbe27a4b07", 74 | "keccakAccountResponse": "0xb20db12ffe97503c747a1ce7ed61a867f1f83e34719f628c177711d1a7814c1d", 75 | "keccakStorageResponse": "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968" 76 | } 77 | } -------------------------------------------------------------------------------- /randao/test/Randao.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import "axiom-contracts/contracts/interfaces/core/IAxiomV1Verifier.sol"; 7 | import "../src/Randao.sol"; 8 | 9 | contract RandaoTest is Test { 10 | address AXIOM_ADDRESS = 0x01d5b501C1fc0121e1411970fb79c322737025c2; 11 | 12 | function testVerifyRandaoRecent() public { 13 | vm.createSelectFork("mainnet", 16_509_500); 14 | 15 | // Import test proof and instance calldata 16 | string[] memory inputs = new string[](2); 17 | inputs[0] = "cat"; 18 | inputs[1] = "test/data/test.calldata"; 19 | bytes memory proof = vm.ffi(inputs); 20 | 21 | IAxiomV1.BlockHashWitness memory testBlock = IAxiomV1Verifier.BlockHashWitness({ 22 | blockNumber: 16509301, 23 | claimedBlockHash: bytes32(0x034ca3921f2ab605c8681288ba4c9818978a12e69c57e82350301fb58e1a9a6b), 24 | prevHash: bytes32(0xf21f9ac46b21ce128bf245ac8c5dcd12ab1bf6a0cb0e3c7dc4d33cc8871d8ab3), 25 | numFinal: 1024, 26 | merkleProof: [ 27 | bytes32(0x3b4e49db58e4dab1931689ab67e24eb79660f2661034280bf8f4480071294456), 28 | bytes32(0xc7e1e977b5d68588e8643956ff13a20cf95c4a76e6ffb168d8faca06413d8c45), 29 | bytes32(0xaa558831681ec38ab0face0d6eab566ae490d8bdf00e92f27d992836c10372d3), 30 | bytes32(0x69384d455682d10dbc617296b17a8715d109d1b74ab74037d48e521122048810), 31 | bytes32(0x62d103378fc33e7f8578480846a0b5634c083167eb621bf0122cd312e34862df), 32 | bytes32(0x7f3a0dfb38decbb536032102f62f6786dc5c7ac3ed73b283bbc73ba3ea07406b), 33 | bytes32(0x8b3e437e20a1d5da7d018acfec8129a3071085fb2a14c48712e12d13ecb16014), 34 | bytes32(0xbe109e17256d615dc1fa6e241fa424247ede44bdadc36fa48fbfe71895f95d5d), 35 | bytes32(0x3afb75397f28a7fbd51498e7a109865e34edf93ecce9ba4686d7d0fb7b86c63b), 36 | bytes32(0x2953bc9d2dfc756b8d46e849da5971dbe1989603b06b7235627529d2e0e5df1d) 37 | ] 38 | }); 39 | uint256 prevRandao = 0xa97f47e048f1ddf11b7a8d2c265014cfcec2506d765a393406f6046960d059dc; 40 | 41 | Randao randao = new Randao(AXIOM_ADDRESS, 15537393); 42 | randao.verifyRandao(testBlock, proof); 43 | require(randao.prevRandaos(testBlock.blockNumber) == prevRandao, "prevRandao not verified"); 44 | } 45 | 46 | function testVerifyRandaoOld() public { 47 | vm.createSelectFork("mainnet", 16_513_500); 48 | 49 | // Import block header RLP as calldata 50 | string[] memory inputs = new string[](2); 51 | inputs[0] = "cat"; 52 | inputs[1] = "test/data/test.calldata"; 53 | bytes memory headerRlp = vm.ffi(inputs); 54 | 55 | IAxiomV1.BlockHashWitness memory testBlock = IAxiomV1Verifier.BlockHashWitness({ 56 | blockNumber: 16509301, 57 | claimedBlockHash: bytes32(0x034ca3921f2ab605c8681288ba4c9818978a12e69c57e82350301fb58e1a9a6b), 58 | prevHash: bytes32(0xf21f9ac46b21ce128bf245ac8c5dcd12ab1bf6a0cb0e3c7dc4d33cc8871d8ab3), 59 | numFinal: 1024, 60 | merkleProof: [ 61 | bytes32(0x3b4e49db58e4dab1931689ab67e24eb79660f2661034280bf8f4480071294456), 62 | bytes32(0xc7e1e977b5d68588e8643956ff13a20cf95c4a76e6ffb168d8faca06413d8c45), 63 | bytes32(0xaa558831681ec38ab0face0d6eab566ae490d8bdf00e92f27d992836c10372d3), 64 | bytes32(0x69384d455682d10dbc617296b17a8715d109d1b74ab74037d48e521122048810), 65 | bytes32(0x62d103378fc33e7f8578480846a0b5634c083167eb621bf0122cd312e34862df), 66 | bytes32(0x7f3a0dfb38decbb536032102f62f6786dc5c7ac3ed73b283bbc73ba3ea07406b), 67 | bytes32(0x8b3e437e20a1d5da7d018acfec8129a3071085fb2a14c48712e12d13ecb16014), 68 | bytes32(0xbe109e17256d615dc1fa6e241fa424247ede44bdadc36fa48fbfe71895f95d5d), 69 | bytes32(0x3afb75397f28a7fbd51498e7a109865e34edf93ecce9ba4686d7d0fb7b86c63b), 70 | bytes32(0x2953bc9d2dfc756b8d46e849da5971dbe1989603b06b7235627529d2e0e5df1d) 71 | ] 72 | }); 73 | uint256 prevRandao = 0xa97f47e048f1ddf11b7a8d2c265014cfcec2506d765a393406f6046960d059dc; 74 | 75 | Randao randao = new Randao(AXIOM_ADDRESS, 15537394); 76 | randao.verifyRandao(testBlock, headerRlp); 77 | require(randao.prevRandaos(testBlock.blockNumber) == prevRandao, "prevRandao not verified"); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /uniswap-v3-twap/src/UniswapV3Oracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // WARNING! This smart contract has not been audited. 3 | // DO NOT USE THIS CONTRACT FOR PRODUCTION 4 | // This is an example contract to demonstrate how to integrate an application with the audited production release of AxiomV1 and AxiomV1Query. 5 | pragma solidity 0.8.19; 6 | 7 | import "./Oracle.sol"; 8 | import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; 9 | import {IUniswapV3Oracle, IAxiomV1Query} from "./IUniswapV3Oracle.sol"; 10 | 11 | contract UniswapV3Oracle is Ownable, IUniswapV3Oracle { 12 | address private axiomQueryAddress; 13 | 14 | /// @notice Mapping between abi.encodePacked(address poolAddress, uint32 startBlockNumber, uint32 endBlockNumber) 15 | /// => keccak(abi.encodePacked(bytes32 startObservationPacked, bytes32 endObservationPacked)) where observationPacked 16 | /// is the packing of Oracle.Observation observation into 32 bytes: 17 | /// bytes32(bytes1(0x0) . secondsPerLiquidityCumulativeX128 . tickCumulative . blockTimestamp) 18 | /// @dev This is the same as how Oracle.Observation is laid out in EVM storage EXCEPT that we set initialized = false (for some gas optimization reasons) 19 | mapping(bytes28 => bytes32) public twapObservations; 20 | 21 | event UpdateAxiomQueryAddress(address newAddress); 22 | 23 | constructor(address _axiomQueryAddress) { 24 | axiomQueryAddress = _axiomQueryAddress; 25 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 26 | } 27 | 28 | function updateAxiomQueryAddress(address _axiomQueryAddress) external onlyOwner { 29 | axiomQueryAddress = _axiomQueryAddress; 30 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 31 | } 32 | 33 | function unpackObservation(uint256 observation) internal pure returns (Oracle.Observation memory) { 34 | // observation` (31 bytes) is single field element, concatenation of `secondsPerLiquidityCumulativeX128 . tickCumulative . blockTimestamp` 35 | return Oracle.Observation({ 36 | blockTimestamp: uint32(observation), 37 | tickCumulative: int56(uint56(observation >> 32)), 38 | secondsPerLiquidityCumulativeX128: uint160(observation >> 88), 39 | initialized: true 40 | }); 41 | } 42 | 43 | /// @notice Verify a ZK proof of a Uniswap V3 TWAP oracle observation and verifies the validity of checkpoint blockhashes using Axiom. 44 | /// Caches the [hash of] raw observations for future use. 45 | /// Returns the time (seconds) weighted average tick (geometric mean) and the time (seconds) weight average liquidity (harmonic mean). 46 | /// @dev We provide the time weighted average tick and time weighted average inverse liquidity for convenience, but return 47 | /// the full Observations in case developers want more fine-grained calculations of the oracle observations. 48 | /// For example the price can be calculated from the tick by P = 1.0001^tick 49 | function verifyUniswapV3TWAP( 50 | IAxiomV1Query.StorageResponse[] calldata storageProofs, 51 | bytes32[3] calldata keccakResponses 52 | ) 53 | external 54 | returns ( 55 | int56 twaTick, 56 | uint160 twaLiquidity, 57 | Oracle.Observation memory startObservation, 58 | Oracle.Observation memory endObservation 59 | ) 60 | { 61 | require(storageProofs[0].slot == 8 && storageProofs[1].slot == 8, "invalid reserve slot"); 62 | require(storageProofs[1].blockNumber > storageProofs[0].blockNumber, "end block must be after start block"); 63 | require(storageProofs[0].addr == storageProofs[1].addr, "inconsistent pool address"); 64 | require( 65 | IAxiomV1Query(axiomQueryAddress).areResponsesValid( 66 | keccakResponses[0], 67 | keccakResponses[1], 68 | keccakResponses[2], 69 | new IAxiomV1Query.BlockResponse[](0), 70 | new IAxiomV1Query.AccountResponse[](0), 71 | storageProofs 72 | ), 73 | "invalid proofs" 74 | ); 75 | 76 | startObservation = unpackObservation(storageProofs[0].value); 77 | endObservation = unpackObservation(storageProofs[1].value); 78 | 79 | twapObservations[bytes28( 80 | abi.encodePacked(storageProofs[0].addr, storageProofs[0].blockNumber, storageProofs[1].blockNumber) 81 | )] = keccak256(abi.encodePacked(storageProofs[0].value, storageProofs[1].value)); 82 | 83 | emit UniswapV3TwapProof( 84 | storageProofs[0].addr, 85 | storageProofs[0].blockNumber, 86 | storageProofs[1].blockNumber, 87 | startObservation, 88 | endObservation 89 | ); 90 | 91 | uint32 secondsElapsed = endObservation.blockTimestamp - startObservation.blockTimestamp; 92 | // floor division 93 | twaTick = (endObservation.tickCumulative - startObservation.tickCumulative) / int56(uint56(secondsElapsed)); 94 | // floor division 95 | twaLiquidity = ((uint160(1) << 128) * secondsElapsed) 96 | / (endObservation.secondsPerLiquidityCumulativeX128 - startObservation.secondsPerLiquidityCumulativeX128); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/utils/ReadQueryData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | import "../axiom-v1-contracts/contracts/interfaces/IAxiomV1Query.sol"; 5 | import "../forge-std/src/console.sol"; 6 | import "../forge-std/src/Vm.sol"; 7 | import {stdJson} from "forge-std/StdJson.sol"; 8 | 9 | library ReadQueryData { 10 | 11 | Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); 12 | 13 | struct RawBlockResponse { 14 | bytes32 blockHash; 15 | bytes blockNumber; 16 | bytes leafIdx; 17 | bytes32[] proof; 18 | } 19 | 20 | struct RawAccountResponse { 21 | address addr; 22 | bytes balance; 23 | bytes blockNumber; 24 | bytes32 codeHash; 25 | bytes leafIdx; 26 | bytes nonce; 27 | bytes32[] proof; 28 | bytes32 storageRoot; 29 | 30 | } 31 | 32 | struct RawStorageResponse { 33 | address addr; 34 | bytes blockNumber; 35 | bytes leafIdx; 36 | bytes32[] proof; 37 | bytes slot; 38 | bytes32 value; 39 | } 40 | 41 | struct KeccakResponses { 42 | bytes32 keccakAccountResponse; 43 | bytes32 keccakBlockResponse; 44 | bytes32 keccakStorageResponse; 45 | } 46 | 47 | struct QueryResponse { 48 | IAxiomV1Query.BlockResponse[] blockResponses; 49 | IAxiomV1Query.AccountResponse[] accountResponses; 50 | IAxiomV1Query.StorageResponse[] storageResponses; 51 | bytes32[3] keccakResponses; 52 | } 53 | 54 | function _bytesToUint(bytes memory b) private pure returns (uint256) { 55 | require(b.length <= 32, "StdCheats _bytesToUint(bytes): Bytes length exceeds 32."); 56 | return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); 57 | } 58 | 59 | function convertRawAccountResponse(RawAccountResponse memory raw) internal pure returns (IAxiomV1Query.AccountResponse memory) { 60 | require(raw.proof.length == 6, "Proof length is not 6"); 61 | bytes32[6] memory proof; 62 | for (uint i = 0; i < 6; i++) { 63 | proof[i] = raw.proof[i]; 64 | } 65 | return IAxiomV1Query.AccountResponse({ 66 | addr: raw.addr, 67 | balance: uint96(_bytesToUint(raw.balance)), 68 | blockNumber: uint32(_bytesToUint(raw.blockNumber)), 69 | codeHash: raw.codeHash, 70 | leafIdx: uint32(_bytesToUint(raw.leafIdx)), 71 | nonce: uint64(_bytesToUint(raw.nonce)), 72 | proof: proof, 73 | storageRoot: raw.storageRoot 74 | }); 75 | } 76 | 77 | function readAccountResponse(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.AccountResponse memory) { 78 | RawAccountResponse memory raw = abi.decode(jsonBytes, (RawAccountResponse)); 79 | return convertRawAccountResponse(raw); 80 | } 81 | 82 | function readAccountResponseArray(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.AccountResponse[] memory) { 83 | RawAccountResponse[] memory raw = abi.decode(jsonBytes, (RawAccountResponse[])); 84 | IAxiomV1Query.AccountResponse[] memory accountResponses = new IAxiomV1Query.AccountResponse[](raw.length); 85 | for (uint i = 0; i < raw.length; i++) { 86 | accountResponses[i] = convertRawAccountResponse(raw[i]); 87 | } 88 | return accountResponses; 89 | } 90 | 91 | function convertRawStorageResponse(RawStorageResponse memory raw) internal pure returns (IAxiomV1Query.StorageResponse memory){ 92 | require(raw.proof.length == 6, "Proof length is not 6"); 93 | bytes32[6] memory proof; 94 | for (uint i = 0; i < 6; i++) { 95 | proof[i] = raw.proof[i]; 96 | } 97 | return IAxiomV1Query.StorageResponse({ 98 | addr: raw.addr, 99 | blockNumber: uint32(_bytesToUint(raw.blockNumber)), 100 | leafIdx: uint32(_bytesToUint(raw.leafIdx)), 101 | proof: proof, 102 | slot: uint256(_bytesToUint(raw.slot)), 103 | value: uint256(raw.value) 104 | }); 105 | } 106 | 107 | function readStorageResponse(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.StorageResponse memory) { 108 | RawStorageResponse memory raw = abi.decode(jsonBytes, (RawStorageResponse)); 109 | return convertRawStorageResponse(raw); 110 | } 111 | 112 | function readStorageResponseArray(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.StorageResponse[] memory) { 113 | RawStorageResponse[] memory raw = abi.decode(jsonBytes, (RawStorageResponse[])); 114 | IAxiomV1Query.StorageResponse[] memory storageResponses = new IAxiomV1Query.StorageResponse[](raw.length); 115 | for (uint i = 0; i < raw.length; i++) { 116 | storageResponses[i] = convertRawStorageResponse(raw[i]); 117 | } 118 | return storageResponses; 119 | } 120 | 121 | function convertRawBlockResponse(RawBlockResponse memory raw) internal pure returns (IAxiomV1Query.BlockResponse memory) { 122 | require(raw.proof.length == 6, "Proof length is not 6"); 123 | bytes32[6] memory proof; 124 | for (uint i = 0; i < 6; i++) { 125 | proof[i] = raw.proof[i]; 126 | } 127 | return IAxiomV1Query.BlockResponse({ 128 | blockHash: raw.blockHash, 129 | blockNumber: uint32(_bytesToUint(raw.blockNumber)), 130 | leafIdx: uint32(_bytesToUint(raw.leafIdx)), 131 | proof: proof 132 | }); 133 | } 134 | 135 | function readBlockResponse(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.BlockResponse memory) { 136 | RawBlockResponse memory raw = abi.decode(jsonBytes, (RawBlockResponse)); 137 | return convertRawBlockResponse(raw); 138 | } 139 | 140 | function readBlockResponseArray(bytes memory jsonBytes) internal pure returns (IAxiomV1Query.BlockResponse[] memory) { 141 | RawBlockResponse[] memory raw = abi.decode(jsonBytes, (RawBlockResponse[])); 142 | IAxiomV1Query.BlockResponse[] memory blockResponses = new IAxiomV1Query.BlockResponse[](raw.length); 143 | for (uint i = 0; i < raw.length; i++) { 144 | blockResponses[i] = convertRawBlockResponse(raw[i]); 145 | } 146 | return blockResponses; 147 | } 148 | 149 | function readKeccakResponses(bytes memory jsonBytes) internal pure returns (bytes32[3] memory) { 150 | KeccakResponses memory keccakResponsesParsed = abi.decode(jsonBytes, (KeccakResponses)); 151 | bytes32[3] memory keccakResponses = [keccakResponsesParsed.keccakBlockResponse, keccakResponsesParsed.keccakAccountResponse, keccakResponsesParsed.keccakStorageResponse]; 152 | return keccakResponses; 153 | } 154 | 155 | function readQueryResponses(string memory json) internal pure returns (QueryResponse memory) { 156 | bytes32[3] memory keccakResponses = readKeccakResponses(stdJson.parseRaw(json, ".keccakResponses")); 157 | IAxiomV1Query.StorageResponse[] memory storageProofs = readStorageResponseArray(stdJson.parseRaw(json, ".storageResponses")); 158 | IAxiomV1Query.BlockResponse[] memory blockProofs = readBlockResponseArray(stdJson.parseRaw(json, ".blockResponses")); 159 | IAxiomV1Query.AccountResponse[] memory accountProofs = readAccountResponseArray(stdJson.parseRaw(json, ".accountResponses")); 160 | return QueryResponse({ 161 | blockResponses: blockProofs, 162 | accountResponses: accountProofs, 163 | storageResponses: storageProofs, 164 | keccakResponses: keccakResponses 165 | }); 166 | } 167 | 168 | } -------------------------------------------------------------------------------- /uniswap-v2-twap/test/data/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "keccakResponses": { 3 | "keccakBlockResponse": "0x99143e925ebff08715221b90d4458364276287e99b646a48feb30be1998fb73a", 4 | "keccakAccountResponse": "0x8d41ed806d08a92793fde04d0c0dee3e95da6d438fb9e133535e583d8e3c068c", 5 | "keccakStorageResponse": "0x5604f4b9d05c9d172cb44de2849c189f69f2c4a9d8455c2c4654c9eaf38b215f" 6 | }, 7 | "storageResponses": [ 8 | { 9 | "blockNumber": "0x6acfc0", 10 | "addr": "0x647595535c370f6092c6dae9d05a7ce9a8819f37", 11 | "slot": "0x8", 12 | "value": "0x6299792900000000000147239df09cd04a9800000000000000000000705f6474", 13 | "leafIdx": "0x0", 14 | "proof": [ 15 | "0x15030f3f6d4ba1046728903afa6676b7f21b0a6754ba9ffad82f3387cbbff5d3", 16 | "0x44f244930461b6ddc78e4b7688e864de53b85c75430852d359952f2f282f0e1f", 17 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 18 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 19 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 20 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 21 | ] 22 | }, 23 | { 24 | "blockNumber": "0x6acfc0", 25 | "addr": "0x647595535c370f6092c6dae9d05a7ce9a8819f37", 26 | "slot": "0xa", 27 | "value": "0x00000000000000000000000000000000000003a7ccc86c26f3b268b64980ca7d", 28 | "leafIdx": "0x1", 29 | "proof": [ 30 | "0x2d694fb5e1c25adce726c932cce8661c17e26bd8d8883b5767305186047448ae", 31 | "0x44f244930461b6ddc78e4b7688e864de53b85c75430852d359952f2f282f0e1f", 32 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 33 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 34 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 35 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 36 | ] 37 | }, 38 | { 39 | "blockNumber": "0x81b321", 40 | "addr": "0x647595535c370f6092c6dae9d05a7ce9a8819f37", 41 | "slot": "0x8", 42 | "value": "0x63edb7cc00000000000033f2c6e6ab8f968c000000000000000000147c11fb5f", 43 | "leafIdx": "0x2", 44 | "proof": [ 45 | "0xa3b1916ff562ca7136565adb68ea51ec0e23d79392f70a95af3cbd2703e82113", 46 | "0xc26198e6dc27efaf12cb734244e5561b5e4ca9697235be420e5df5e041cd764b", 47 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 48 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 49 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 50 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 51 | ] 52 | }, 53 | { 54 | "blockNumber": "0x81b321", 55 | "addr": "0x647595535c370f6092c6dae9d05a7ce9a8819f37", 56 | "slot": "0xa", 57 | "value": "0x0000000000000000000000000000000000043c7e651fd87d2bab8bf6da3dc412", 58 | "leafIdx": "0x3", 59 | "proof": [ 60 | "0x7c78b882e44decec2e1dfd14096a2e8a93f37c01745a0f0b573e2ed6a4eeffb5", 61 | "0xc26198e6dc27efaf12cb734244e5561b5e4ca9697235be420e5df5e041cd764b", 62 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 63 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 64 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 65 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 66 | ] 67 | } 68 | ], 69 | "blockResponses": [ 70 | { 71 | "blockNumber": "0x6acfc0", 72 | "blockHash": "0x9edca7753a3a64d8b828086252e908fc9b7e8cba7276b3a94c38bf106494eaa6", 73 | "leafIdx": "0x0", 74 | "proof": [ 75 | "0x1d8c4ed19306506494a43d13a7e4013ac071b6bb5313b5b05a100394caf36e1e", 76 | "0xf1732925f3dca183db05a34972104e0406087f287e593be1bc5219f2aaebdf72", 77 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 78 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 79 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 80 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 81 | ] 82 | }, 83 | { 84 | "blockNumber": "0x81b321", 85 | "blockHash": "0xbfd160b9b55aa6000e0a2a73d6a5c49a885611af4cca8731c9e96dce6d761815", 86 | "leafIdx": "0x2", 87 | "proof": [ 88 | "0x439dcdf275f5c5d1f819501652201e31093f829305a3a91ba8cc359324e1b10a", 89 | "0xf196613177f7776552b62828511e58ff6d45351dfa002b623712f41bc11aa6fd", 90 | "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", 91 | "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", 92 | "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", 93 | "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d" 94 | ] 95 | } 96 | ], 97 | "blockHeaders": [ 98 | "0xf9025ea0ea81273cbe7ac51ffcc24c7b93790e06b7d0f7c5ade53d5b57daa4ea58237deea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0ee1002f67f0308ab5b0fd9eb4d4c36d262a1293be8c2c3179543be5a356b7211a0d1bf1a0454c1adfebb2783bdad242359f01f7211012654abb7985ef49d7246c2a0b3580a73fe9dae3b7dad6674de728d372106bbaab5f6a98761f65e8d1586dca0b90100080040000000000004084002801020000000084000800000000080000000300000c844100400002000000100000000000002008000100000000004420020008002020040000000040400000801002040010040002000000000000080001001082000800202400801000000200000088000020030000800201001001000000042040060804000000000000000500000000010000800000000000100000000000403003020000040080400000000000000000000000020000100000080000800004000000300800000000008000000000000000000010200100000100004002080001084240000400000000000000001000000000004000000000004000000000202836acfc08401c951118329497584629a6d9fb861f09f928e20407072796c616273206e6f64652d3020f09f928e00000000000000a200d5ae17cfa660f5263f0f441372a93cc94ff9abbde1607260117c465cbee8406ffa1a664721f87ee5c20fc035e9d4d8b87f8ec36e97943283fa8525332e1e00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000008", 99 | "0xf90210a0dec9afb0fe694a05845e63a3a95daf352d82163d08e3c2b2a72a3fdaa84dcfd6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948dc847af872947ac18d5d63fa646eb65d4d99560a0da5cbea47e3203c693d10986fb1c038445358c429d978d36c6b630c926ba530da09225af52391f7290f5fde9726606e74d7e7bc18d26b1bd7342dbfa357850dbdea068e3ef8ab4a6c4f8cdc6a47945b1e42a87355a6f0cbc4ff0375b92d2b0dadae8b90100108082255120080000804801024068201314220820003880408005140c2000080014880000024210280000000006004001414000428720100282040602240080492010004140b41d9080000a102000882401030001d60300004000ec00348a4881241286420048014080258220100800812100020104040420041038809800441480002d82902088000040002290029010000c90000030062010000010028400022308001008000010020000084204000028058804070040009402480020a5003220000201100580090200604101400064b80420080c05814141281041807024a9301808000001030000141048110101c0000500402420010880428000048008808381b3218401c9c38083ac1cc28463edbce894506f776572656420627920626c6f58726f757465a0c01dfd12663a886207d2556bcb54ffd18b11c3bbeda6ac8d0fdbbb61da03893088000000000000000016" 100 | ] 101 | } -------------------------------------------------------------------------------- /uniswap-v2-twap/src/UniswapV2Twap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // WARNING! This smart contract has not been audited. 3 | // DO NOT USE THIS CONTRACT FOR PRODUCTION 4 | // This is an example contract to demonstrate how to integrate an application with the audited production release of AxiomV1 and AxiomV1Query. 5 | pragma solidity 0.8.19; 6 | 7 | import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; 8 | import {IAxiomV1Query} from "axiom-contracts/contracts/interfaces/IAxiomV1Query.sol"; 9 | import "utils/RLPReader.sol"; 10 | import "utils/UQ112x112.sol"; 11 | 12 | contract UniswapV2Twap is Ownable { 13 | using RLPReader for RLPReader.RLPItem; 14 | using RLPReader for RLPReader.Iterator; 15 | using RLPReader for bytes; 16 | using UQ112x112 for uint224; 17 | 18 | address public axiomQueryAddress; 19 | mapping(bytes28 => uint256) public twapPris; 20 | 21 | event UniswapV2TwapProof(address pairAddress, uint32 startBlockNumber, uint32 endBlockNumber, uint256 twapPri); 22 | 23 | event UpdateAxiomQueryAddress(address newAddress); 24 | 25 | constructor(address _axiomQueryAddress) { 26 | axiomQueryAddress = _axiomQueryAddress; 27 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 28 | } 29 | 30 | function updateAxiomQueryAddress(address _axiomQueryAddress) external onlyOwner { 31 | axiomQueryAddress = _axiomQueryAddress; 32 | emit UpdateAxiomQueryAddress(_axiomQueryAddress); 33 | } 34 | 35 | function _getTimestampFromBlock(uint32 blockNumber, bytes32 blockHash, bytes memory rlpEncodedHeader) 36 | internal 37 | pure 38 | returns (uint256) 39 | { 40 | require(keccak256(rlpEncodedHeader) == blockHash, "invalid blockhash"); 41 | 42 | RLPReader.RLPItem[] memory ls = rlpEncodedHeader.toRlpItem().toList(); 43 | require(blockNumber == ls[8].toUint(), "invalid block number"); 44 | uint256 timestamp = ls[11].toUint(); 45 | return timestamp; 46 | } 47 | 48 | function _currentCumulativePrice( 49 | uint112 reserve0, 50 | uint112 reserve1, 51 | uint256 blockTimestamp, 52 | uint32 blockTimestampLast, 53 | uint256 price1CumulativeLast 54 | ) internal pure returns (uint256) { 55 | unchecked { 56 | uint32 timeElapsed = uint32(blockTimestamp) - blockTimestampLast; // overflow is desired 57 | uint256 increment = 58 | uint256(UQ112x112.encode(reserve1).uqdiv(reserve0)) * timeElapsed; 59 | return increment + price1CumulativeLast; 60 | } 61 | } 62 | 63 | // slot is structured as follows: 64 | // blockTimestampLast (32) . reserves1 (112) . reserves0 (112) 65 | function _unpackReserveValues(uint256 slot) internal pure returns (uint112, uint112, uint32) { 66 | uint112 reserve0 = uint112(slot >> 144); 67 | uint112 reserve1 = uint112(slot >> 32); 68 | uint32 blockTimestampLast = uint32(slot); 69 | return (reserve0, reserve1, blockTimestampLast); 70 | } 71 | 72 | /* | Name | Type | Slot | Offset | Bytes | Contract | 73 | |----------------------|-------------------------------------------------|------|--------|-------|-------------------------------------------| 74 | | totalSupply | uint256 | 0 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 75 | | balanceOf | mapping(address => uint256) | 1 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 76 | | allowance | mapping(address => mapping(address => uint256)) | 2 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 77 | | DOMAIN_SEPARATOR | bytes32 | 3 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 78 | | nonces | mapping(address => uint256) | 4 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 79 | | factory | address | 5 | 0 | 20 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 80 | | token0 | address | 6 | 0 | 20 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 81 | | token1 | address | 7 | 0 | 20 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 82 | | reserve0 | uint112 | 8 | 0 | 14 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 83 | | reserve1 | uint112 | 8 | 14 | 14 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 84 | | blockTimestampLast | uint32 | 8 | 28 | 4 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 85 | | price0CumulativeLast | uint256 | 9 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 86 | | price1CumulativeLast | uint256 | 10 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 87 | | kLast | uint256 | 11 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 88 | | unlocked | uint256 | 12 | 0 | 32 | contracts/UniswapV2Pair.sol:UniswapV2Pair | 89 | */ 90 | 91 | function calculateUniswapV2Twap( 92 | IAxiomV1Query.StorageResponse[] calldata storageProofs, 93 | IAxiomV1Query.BlockResponse[] calldata blockProofs, 94 | bytes[2] calldata rlpEncodedHeaders, 95 | bytes32[3] calldata keccakResponses 96 | ) public returns (uint256) { 97 | require(storageProofs[0].slot == 8 && storageProofs[2].slot == 8, "invalid reserve slot"); 98 | require(storageProofs[1].slot == 10 && storageProofs[3].slot == 10, "invalid cumulative price slot"); 99 | require( 100 | storageProofs[0].blockNumber == storageProofs[1].blockNumber 101 | && storageProofs[0].blockNumber == blockProofs[0].blockNumber, 102 | "inconsistent block number" 103 | ); 104 | require( 105 | storageProofs[2].blockNumber == storageProofs[3].blockNumber 106 | && storageProofs[2].blockNumber == blockProofs[1].blockNumber, 107 | "inconsistent block number" 108 | ); 109 | require( 110 | storageProofs[0].addr == storageProofs[1].addr && storageProofs[0].addr == storageProofs[2].addr 111 | && storageProofs[0].addr == storageProofs[3].addr, 112 | "inconsistent pair address" 113 | ); 114 | require( 115 | IAxiomV1Query(axiomQueryAddress).areResponsesValid( 116 | keccakResponses[0], 117 | keccakResponses[1], 118 | keccakResponses[2], 119 | blockProofs, 120 | new IAxiomV1Query.AccountResponse[](0), 121 | storageProofs 122 | ), 123 | "invalid proofs" 124 | ); 125 | 126 | uint256 blockTimestamp_k1 = 127 | _getTimestampFromBlock(blockProofs[0].blockNumber, blockProofs[0].blockHash, rlpEncodedHeaders[0]); 128 | 129 | uint256 blockTimestamp_k2 = 130 | _getTimestampFromBlock(blockProofs[1].blockNumber, blockProofs[1].blockHash, rlpEncodedHeaders[1]); 131 | 132 | (uint112 reserve0_k1, uint112 reserve1_k1, uint32 blockTimestampLast_k1) = 133 | _unpackReserveValues(storageProofs[0].value); 134 | 135 | (uint112 reserve0_k2, uint112 reserve1_k2, uint32 blockTimestampLast_k2) = 136 | _unpackReserveValues(storageProofs[2].value); 137 | 138 | uint256 currentCumulativePrice_k1 = _currentCumulativePrice( 139 | reserve0_k1, reserve1_k1, blockTimestamp_k1, blockTimestampLast_k1, storageProofs[1].value 140 | ); 141 | 142 | uint256 currentCumulativePrice_k2 = _currentCumulativePrice( 143 | reserve0_k2, reserve1_k2, blockTimestamp_k2, blockTimestampLast_k2, storageProofs[3].value 144 | ); 145 | 146 | uint256 twap = (currentCumulativePrice_k2 - currentCumulativePrice_k1) / (blockTimestamp_k2 - blockTimestamp_k1); 147 | 148 | twapPris[bytes28( 149 | abi.encodePacked(storageProofs[0].addr, blockProofs[0].blockNumber, blockProofs[1].blockNumber) 150 | )] = twap; 151 | 152 | emit UniswapV2TwapProof(storageProofs[0].addr, blockProofs[0].blockNumber, blockProofs[1].blockNumber, twap); 153 | 154 | return twap; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/utils/RLPReader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Original source @ https://github.com/hamdiallam/Solidity-RLP. 4 | 5 | /** 6 | * @author Hamdi Allam hamdi.allam97@gmail.com 7 | * Please reach out with any questions or concerns 8 | */ 9 | pragma solidity ^0.8.16; 10 | 11 | library RLPReader { 12 | uint8 constant STRING_SHORT_START = 0x80; 13 | uint8 constant STRING_LONG_START = 0xb8; 14 | uint8 constant LIST_SHORT_START = 0xc0; 15 | uint8 constant LIST_LONG_START = 0xf8; 16 | uint8 constant WORD_SIZE = 32; 17 | 18 | struct RLPItem { 19 | uint256 len; 20 | uint256 memPtr; 21 | } 22 | 23 | struct Iterator { 24 | RLPItem item; // Item that's being iterated over. 25 | uint256 nextPtr; // Position of the next item in the list. 26 | } 27 | 28 | /* 29 | * @dev Returns the next element in the iteration. Reverts if it has not next element. 30 | * @param self The iterator. 31 | * @return The next element in the iteration. 32 | */ 33 | function next(Iterator memory self) internal pure returns (RLPItem memory) { 34 | require(hasNext(self)); 35 | 36 | uint256 ptr = self.nextPtr; 37 | uint256 itemLength = _itemLength(ptr); 38 | self.nextPtr = ptr + itemLength; 39 | 40 | return RLPItem(itemLength, ptr); 41 | } 42 | 43 | /* 44 | * @dev Returns true if the iteration has more elements. 45 | * @param self The iterator. 46 | * @return true if the iteration has more elements. 47 | */ 48 | function hasNext(Iterator memory self) internal pure returns (bool) { 49 | RLPItem memory item = self.item; 50 | return self.nextPtr < item.memPtr + item.len; 51 | } 52 | 53 | /* 54 | * @param item RLP encoded bytes 55 | */ 56 | function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { 57 | uint256 memPtr; 58 | assembly { 59 | memPtr := add(item, 0x20) 60 | } 61 | 62 | return RLPItem(item.length, memPtr); 63 | } 64 | 65 | /* 66 | * @dev Create an iterator. Reverts if item is not a list. 67 | * @param self The RLP item. 68 | * @return An 'Iterator' over the item. 69 | */ 70 | function iterator(RLPItem memory self) internal pure returns (Iterator memory) { 71 | require(isList(self)); 72 | 73 | uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); 74 | return Iterator(self, ptr); 75 | } 76 | 77 | /* 78 | * @param the RLP item. 79 | */ 80 | function rlpLen(RLPItem memory item) internal pure returns (uint256) { 81 | return item.len; 82 | } 83 | 84 | /* 85 | * @param the RLP item. 86 | * @return (memPtr, len) pair: location of the item's payload in memory. 87 | */ 88 | function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { 89 | uint256 offset = _payloadOffset(item.memPtr); 90 | uint256 memPtr = item.memPtr + offset; 91 | uint256 len = item.len - offset; // data length 92 | return (memPtr, len); 93 | } 94 | 95 | /* 96 | * @param the RLP item. 97 | */ 98 | function payloadLen(RLPItem memory item) internal pure returns (uint256) { 99 | (, uint256 len) = payloadLocation(item); 100 | return len; 101 | } 102 | 103 | /* 104 | * @param the RLP item containing the encoded list. 105 | */ 106 | function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { 107 | require(isList(item)); 108 | 109 | uint256 items = numItems(item); 110 | RLPItem[] memory result = new RLPItem[](items); 111 | 112 | uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); 113 | uint256 dataLen; 114 | for (uint256 i = 0; i < items; i++) { 115 | dataLen = _itemLength(memPtr); 116 | result[i] = RLPItem(dataLen, memPtr); 117 | memPtr = memPtr + dataLen; 118 | } 119 | 120 | return result; 121 | } 122 | 123 | // @return indicator whether encoded payload is a list. negate this function call for isData. 124 | function isList(RLPItem memory item) internal pure returns (bool) { 125 | if (item.len == 0) return false; 126 | 127 | uint8 byte0; 128 | uint256 memPtr = item.memPtr; 129 | assembly { 130 | byte0 := byte(0, mload(memPtr)) 131 | } 132 | 133 | if (byte0 < LIST_SHORT_START) return false; 134 | return true; 135 | } 136 | 137 | /* 138 | * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. 139 | * @return keccak256 hash of RLP encoded bytes. 140 | */ 141 | function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { 142 | uint256 ptr = item.memPtr; 143 | uint256 len = item.len; 144 | bytes32 result; 145 | assembly { 146 | result := keccak256(ptr, len) 147 | } 148 | return result; 149 | } 150 | 151 | /* 152 | * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. 153 | * @return keccak256 hash of the item payload. 154 | */ 155 | function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { 156 | (uint256 memPtr, uint256 len) = payloadLocation(item); 157 | bytes32 result; 158 | assembly { 159 | result := keccak256(memPtr, len) 160 | } 161 | return result; 162 | } 163 | 164 | /** 165 | * RLPItem conversions into data types * 166 | */ 167 | 168 | // @returns raw rlp encoding in bytes 169 | function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { 170 | bytes memory result = new bytes(item.len); 171 | if (result.length == 0) return result; 172 | 173 | uint256 ptr; 174 | assembly { 175 | ptr := add(0x20, result) 176 | } 177 | 178 | copy(item.memPtr, ptr, item.len); 179 | return result; 180 | } 181 | 182 | // any non-zero byte except "0x80" is considered true 183 | function toBoolean(RLPItem memory item) internal pure returns (bool) { 184 | require(item.len == 1); 185 | uint256 result; 186 | uint256 memPtr = item.memPtr; 187 | assembly { 188 | result := byte(0, mload(memPtr)) 189 | } 190 | 191 | // SEE Github Issue #5. 192 | // Summary: Most commonly used RLP libraries (i.e Geth) will encode 193 | // "0" as "0x80" instead of as "0". We handle this edge case explicitly 194 | // here. 195 | if (result == 0 || result == STRING_SHORT_START) { 196 | return false; 197 | } else { 198 | return true; 199 | } 200 | } 201 | 202 | function toUint(RLPItem memory item) internal pure returns (uint256) { 203 | require(item.len > 0 && item.len <= 33); 204 | 205 | (uint256 memPtr, uint256 len) = payloadLocation(item); 206 | 207 | uint256 result; 208 | assembly { 209 | result := mload(memPtr) 210 | 211 | // shfit to the correct location if neccesary 212 | if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } 213 | } 214 | 215 | return result; 216 | } 217 | 218 | // enforces 32 byte length 219 | function toUintStrict(RLPItem memory item) internal pure returns (uint256) { 220 | // one byte prefix 221 | require(item.len == 33); 222 | 223 | uint256 result; 224 | uint256 memPtr = item.memPtr + 1; 225 | assembly { 226 | result := mload(memPtr) 227 | } 228 | 229 | return result; 230 | } 231 | 232 | function toBytes(RLPItem memory item) internal pure returns (bytes memory) { 233 | require(item.len > 0); 234 | 235 | (uint256 memPtr, uint256 len) = payloadLocation(item); 236 | bytes memory result = new bytes(len); 237 | 238 | uint256 destPtr; 239 | assembly { 240 | destPtr := add(0x20, result) 241 | } 242 | 243 | copy(memPtr, destPtr, len); 244 | return result; 245 | } 246 | 247 | /* 248 | * Private Helpers 249 | */ 250 | 251 | // @return number of payload items inside an encoded list. 252 | function numItems(RLPItem memory item) private pure returns (uint256) { 253 | if (item.len == 0) return 0; 254 | 255 | uint256 count = 0; 256 | uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); 257 | uint256 endPtr = item.memPtr + item.len; 258 | while (currPtr < endPtr) { 259 | currPtr = currPtr + _itemLength(currPtr); // skip over an item 260 | count++; 261 | } 262 | 263 | return count; 264 | } 265 | 266 | // @return entire rlp item byte length 267 | function _itemLength(uint256 memPtr) private pure returns (uint256) { 268 | uint256 itemLen; 269 | uint256 byte0; 270 | assembly { 271 | byte0 := byte(0, mload(memPtr)) 272 | } 273 | 274 | if (byte0 < STRING_SHORT_START) { 275 | itemLen = 1; 276 | } else if (byte0 < STRING_LONG_START) { 277 | itemLen = byte0 - STRING_SHORT_START + 1; 278 | } else if (byte0 < LIST_SHORT_START) { 279 | assembly { 280 | let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is 281 | memPtr := add(memPtr, 1) // skip over the first byte 282 | 283 | /* 32 byte word size */ 284 | let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len 285 | itemLen := add(dataLen, add(byteLen, 1)) 286 | } 287 | } else if (byte0 < LIST_LONG_START) { 288 | itemLen = byte0 - LIST_SHORT_START + 1; 289 | } else { 290 | assembly { 291 | let byteLen := sub(byte0, 0xf7) 292 | memPtr := add(memPtr, 1) 293 | 294 | let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length 295 | itemLen := add(dataLen, add(byteLen, 1)) 296 | } 297 | } 298 | 299 | return itemLen; 300 | } 301 | 302 | // @return number of bytes until the data 303 | function _payloadOffset(uint256 memPtr) private pure returns (uint256) { 304 | uint256 byte0; 305 | assembly { 306 | byte0 := byte(0, mload(memPtr)) 307 | } 308 | 309 | if (byte0 < STRING_SHORT_START) { 310 | return 0; 311 | } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { 312 | return 1; 313 | } else if (byte0 < LIST_SHORT_START) { 314 | // being explicit 315 | return byte0 - (STRING_LONG_START - 1) + 1; 316 | } else { 317 | return byte0 - (LIST_LONG_START - 1) + 1; 318 | } 319 | } 320 | 321 | /* 322 | * @param src Pointer to source 323 | * @param dest Pointer to destination 324 | * @param len Amount of memory to copy from the source 325 | */ 326 | function copy(uint256 src, uint256 dest, uint256 len) private pure { 327 | if (len == 0) return; 328 | 329 | // copy as many word sizes as possible 330 | for (; len >= WORD_SIZE; len -= WORD_SIZE) { 331 | assembly { 332 | mstore(dest, mload(src)) 333 | } 334 | 335 | src += WORD_SIZE; 336 | dest += WORD_SIZE; 337 | } 338 | 339 | if (len > 0) { 340 | // left over bytes. Mask is used to remove unwanted bytes from the word 341 | uint256 mask = 256 ** (WORD_SIZE - len) - 1; 342 | assembly { 343 | let srcpart := and(mload(src), not(mask)) // zero out src 344 | let destpart := and(mload(dest), mask) // retrieve the bytes 345 | mstore(dest, or(destpart, srcpart)) 346 | } 347 | } 348 | } 349 | } --------------------------------------------------------------------------------