├── README.md ├── .dockerignore ├── slither.config.json ├── external ├── integrations │ ├── omnibridge │ │ ├── interfaces │ │ │ ├── ITelepathyValidator.sol │ │ │ └── IBasicHomeAMB.sol │ │ ├── deploy.sh │ │ ├── .env.example │ │ └── TelepathyValidator.s.sol │ ├── eigenlayer │ │ ├── EigenLayerBeaconOracleStorage.sol │ │ ├── EigenLayerBeaconOracle.sol │ │ └── EigenLayerBeaconOracleProxy.sol │ └── rocketpool │ │ └── RocketPoolBeaconOracle.sol └── examples │ ├── drills │ ├── DrillLightClient.sol │ └── DrillTelepathyRouter.sol │ ├── bridge │ ├── Tokens.t.sol │ ├── Tokens.sol │ └── Bridge.t.sol │ ├── counter │ └── CountMessenger.sol │ ├── example-counter │ ├── ExampleCounter.sol │ └── ExampleCounter.t.sol │ ├── uniswap │ ├── UniswapExample.t.sol │ └── UniswapExample.sol │ └── oracle │ └── NFTAirdrop.sol ├── src ├── oracle │ ├── interfaces │ │ └── IOracleCallbackReceiver.sol │ ├── TelepathyOracleFulfiller.sol │ └── TelepathyOracle.sol ├── libraries │ ├── Proxy.sol │ ├── Typecast.sol │ ├── Timelock.sol │ ├── BeaconChainForks.sol │ ├── MerkleProof.sol │ └── StateProofHelper.sol ├── pubsub │ ├── interfaces │ │ ├── ISubscriptionReceiver.sol │ │ ├── SubscriptionReceiver.sol │ │ ├── SubscriptionReceiverUpgradeable.sol │ │ └── IPubSub.sol │ ├── PubSubStorage.sol │ ├── TelepathyPubSub.sol │ ├── TelepathySubscriber.sol │ └── EventProof.sol ├── lightclient │ ├── interfaces │ │ └── ILightClient.sol │ └── LightClientMock.sol ├── amb-v2 │ ├── verifier │ │ ├── interfaces │ │ │ └── IMessageVerifier.sol │ │ ├── BeaconVerifierBase.sol │ │ └── TelepathyStorageVerifier.sol │ ├── interfaces │ │ ├── TelepathyHandler.sol │ │ ├── TelepathyHandlerUpgradeable.sol │ │ └── ITelepathy.sol │ ├── TelepathyRouter.sol │ ├── TelepathyStorage.sol │ └── TelepathyAccess.sol └── amb │ ├── interfaces │ ├── TelepathyHandler.sol │ ├── TelepathyHandlerUpgradeable.sol │ └── ITelepathy.sol │ ├── TelepathyRouter.sol │ └── TelepathyStorage.sol ├── misc └── CounterSimple.sol ├── .solhint.json ├── tsconfig.json ├── package.json ├── remappings.txt ├── .env.example ├── .gitignore ├── test ├── pubsub │ ├── TelepathyPublisher.t.sol │ ├── EventProofFixture.sol │ ├── fixtures │ │ ├── eventProof22.json │ │ ├── eventProof19.json │ │ ├── eventProof21.json │ │ ├── eventProof18.json │ │ └── eventProof20.json │ └── EventProof.t.sol ├── integrations │ ├── diva │ │ └── fixtures │ │ │ ├── diva_deposit_6308974.json │ │ │ └── diva_withdrawal_6358918.json │ └── eigenlayer │ │ └── fixtures │ │ ├── eigenlayer1.json │ │ └── eigenlayer2.json ├── libraries │ ├── StateProofFixture.sol │ └── fixtures │ │ ├── eventProof-type2.json │ │ ├── eventProof22.json │ │ ├── eventProof-type0.json │ │ ├── eventProof-type1.json │ │ ├── eventProof19.json │ │ ├── eventProof21.json │ │ ├── eventProof18.json │ │ └── eventProof20.json ├── amb │ └── Timelock.t.sol ├── amb-v2 │ ├── Timelock.t.sol │ ├── TestUtils.sol │ └── fixtures │ │ ├── eventSlotClose.json │ │ └── eventSlotSame.json └── lightclient │ ├── fixtures │ ├── slot6000991.json │ ├── slot6001055.json │ ├── slot6001087.json │ ├── periodBoundaryEarlySlot.json │ ├── periodBoundaryLateSlot.json │ ├── opt_slot6440799.json │ └── opt_slot6443999.json │ └── LightClientFixture.sol ├── foundry.toml ├── .gitmodules ├── SECURITY.md └── abi └── LightClientMock.json /README.md: -------------------------------------------------------------------------------- 1 | # telepathy-contracts 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | abi/ 3 | out/ 4 | .turbo/ -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter_paths": "lib|script|test|Mock*", 3 | "compile_force_framework": "foundry", 4 | "exclude_informational": true 5 | } 6 | -------------------------------------------------------------------------------- /external/integrations/omnibridge/interfaces/ITelepathyValidator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | enum ExecutionStatus { 4 | NONE, 5 | PENDING, 6 | EXECUTED 7 | } 8 | -------------------------------------------------------------------------------- /external/integrations/omnibridge/interfaces/IBasicHomeAMB.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | interface IBasicHomeAMB { 4 | function executeAffirmation(bytes calldata message) external; 5 | } -------------------------------------------------------------------------------- /src/oracle/interfaces/IOracleCallbackReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | interface IOracleCallbackReceiver { 4 | function handleOracleResponse(uint256 nonce, bytes memory responseData, bool responseSuccess) 5 | external; 6 | } 7 | -------------------------------------------------------------------------------- /src/libraries/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 4 | 5 | contract UUPSProxy is ERC1967Proxy { 6 | constructor(address implementation, bytes memory _data) ERC1967Proxy(implementation, _data) {} 7 | } 8 | -------------------------------------------------------------------------------- /external/integrations/omnibridge/deploy.sh: -------------------------------------------------------------------------------- 1 | source .env 2 | 3 | FOUNDRY_PROFILE=external forge script TelepathyValidator.s.sol:Deploy \ 4 | --rpc-url $RPC_100 \ 5 | --private-key $PRIVATE_KEY_100 \ 6 | --broadcast \ 7 | --verifier etherscan \ 8 | --verifier-url https://api.gnosisscan.io/api \ 9 | --etherscan-api-key $ETHERSCAN_API_KEY_100 \ 10 | --verify -------------------------------------------------------------------------------- /misc/CounterSimple.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | // To build this, run: FOUNDRY_PROFILE=misc forge build 4 | 5 | contract CounterSimple { 6 | uint256 public counter = 0; 7 | 8 | event Incremented(uint256 value, address indexed sender); 9 | 10 | function increment() public { 11 | counter++; 12 | emit Incremented(counter, msg.sender); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/pubsub/interfaces/ISubscriptionReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | interface ISubscriptionReceiver { 4 | function handlePublish( 5 | bytes32 subscriptionId, 6 | uint32 sourceChainId, 7 | address sourceAddress, 8 | uint64 slot, 9 | bytes32 publishKey, 10 | bytes32[] memory eventTopics, 11 | bytes memory eventData 12 | ) external returns (bytes4); 13 | } 14 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "code-complexity": ["error", 18], 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "max-line-length": ["warn", 100], 7 | "no-console": "warn", 8 | "var-name-mixedcase": "off", 9 | "func-name-mixedcase": "off", 10 | "avoid-low-level-calls": "off", 11 | "no-inline-assembly": "off" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/libraries/Typecast.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity 0.8.16; 3 | 4 | library Address { 5 | function fromBytes32(bytes32 buffer) internal pure returns (address) { 6 | return address(uint160(uint256(buffer))); 7 | } 8 | } 9 | 10 | library Bytes32 { 11 | function fromAddress(address addr) internal pure returns (bytes32) { 12 | return bytes32(uint256(uint160(addr))); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "es2020", 5 | "rootDir": ".", 6 | "outDir": "dist", 7 | "moduleResolution": "node", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "composite": true 13 | }, 14 | "exclude": ["dist/**/*", "lib/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /src/lightclient/interfaces/ILightClient.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface ILightClient { 4 | function consistent() external view returns (bool); 5 | 6 | function head() external view returns (uint256); 7 | 8 | function headers(uint256 slot) external view returns (bytes32); 9 | 10 | function executionStateRoots(uint256 slot) external view returns (bytes32); 11 | 12 | function timestamps(uint256 slot) external view returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@succinctlabs/telepathy-contracts", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "type": "module", 6 | "main": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "scripts": { 9 | "lint": "forge fmt --check src test script examples", 10 | "test": "forge test", 11 | "build": "bash build_with_abi.sh", 12 | "dev": "FOUNDRY_IGNORED_ERROR_CODES='[5740,1878]' forge build" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | Solidity-RLP/=lib/Solidity-RLP/contracts/ 2 | curve-merkle-oracle/=lib/curve-merkle-oracle/contracts/ 3 | ds-test/=lib/forge-std/lib/ds-test/src/ 4 | erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ 5 | forge-std/=lib/forge-std/src/ 6 | @openzeppelin/=lib/openzeppelin-contracts/ 7 | @openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/ 8 | v3-core/=lib/v3-core/ 9 | @uniswap=lib/ 10 | @optimism-bedrock=lib/optimism-bedrock-contracts/ 11 | @telepathy-v2/=lib/telepathy-v2/contracts/src/ 12 | @safe/=lib/safe-contracts/contracts -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY= 2 | WALLET_TYPE=PRIVATE_KEY # PRIVATE_KEY | LEDGER 3 | MNEMONIC_INDEX=0 # If WALLET_TYPE is LEDGER, this is the index of the account to use 4 | CREATE2_SALT= 5 | SOURCE_CHAIN_IDS= 6 | EOA_UPGRADE= 7 | 8 | # For Guardian and Timelock deployment 9 | GUARDIAN_OWNERS= 10 | MINIMUM_DELAY= 11 | 12 | RPC_1= 13 | RPC_5= 14 | RPC_137= 15 | RPC_420= 16 | RPC_42163= 17 | RPC_84531= 18 | 19 | ETHERSCAN_API_KEY_1= 20 | ETHERSCAN_API_KEY_5= 21 | ETHERSCAN_API_KEY_137= 22 | ETHERSCAN_API_KEY_420= 23 | ETHERSCAN_API_KEY_42163= 24 | ETHERSCAN_API_KEY_84531= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | broadcast/ 3 | cache/ 4 | logs/ 5 | lcov.info 6 | *foundryScriptOutput.json 7 | 8 | abi/* 9 | !abi/LightClient.json 10 | !abi/LightClientMock.json 11 | !abi/SourceAMB.json 12 | !abi/TargetAMB.json 13 | !abi/DrillLightClient.json 14 | !abi/DrillTelepathyRouter.json 15 | !abi/Guardian.json 16 | !abi/Timelock.json 17 | !abi/TelepathyRouter.json 18 | !abi/TelepathyPubSub.json 19 | !abi/TelepathyPublisher.json 20 | !abi/TelepathySubscriber.json 21 | !abi/TelepathyValidator.json 22 | !abi/EigenLayerBeaconOracle.json 23 | !abi/EigenLayerBeaconOracleProxy.json -------------------------------------------------------------------------------- /external/integrations/omnibridge/.env.example: -------------------------------------------------------------------------------- 1 | GUARDIAN_ADDRESS=0xc5B66fbB215c49938061EC2C0871018B262e28ce 2 | TIMELOCK_ADDRESS=0x6bba9da2eE1e812F30d78e87199F80035853774e 3 | LIGHT_CLIENT_ADDRESS=0xaa1383Ee33c81Ef2274419dd5e0Ea5cCe4baF6cC 4 | SOURCE_CHAIN_ID=1 5 | AMB_AFFIRMATION_SOURCE_ADDRESS=0x4C36d2919e407f0Cc2Ee3c993ccF8ac26d9CE64e 6 | START_SLOT=6762910 7 | END_SLOT=4294967295 8 | HOME_AMB_ADDRESS=0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59 9 | OWNER=0x6bba9da2eE1e812F30d78e87199F80035853774e 10 | 11 | RPC_100="" 12 | PRIVATE_KEY_100="" 13 | ETHERSCAN_API_KEY_100="" -------------------------------------------------------------------------------- /test/pubsub/TelepathyPublisher.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import "forge-std/Test.sol"; 4 | import "forge-std/console.sol"; 5 | import {MockTelepathy} from "src/amb/mocks/MockTelepathy.sol"; 6 | import { 7 | TelepathySubscriber, 8 | SubscriptionStatus, 9 | Subscription 10 | } from "src/pubsub/TelepathySubscriber.sol"; 11 | import {TelepathyPublisher} from "src/pubsub/TelepathyPublisher.sol"; 12 | import {TelepathyHandler} from "src/amb/interfaces/TelepathyHandler.sol"; 13 | 14 | contract TelepathyPublisherTest is Test { 15 | MockTelepathy mockTelepathy; 16 | 17 | function setUp() public { 18 | mockTelepathy = new MockTelepathy(1); 19 | } 20 | 21 | function test() public { 22 | // TODO after implementation is finalized 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/pubsub/EventProofFixture.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "forge-std/Base.sol"; 4 | 5 | contract EventProofFixture is CommonBase { 6 | struct Fixture { 7 | address claimedEmitter; 8 | string key; 9 | bytes logData; 10 | uint256 logIndex; 11 | address logSource; 12 | bytes32[] logTopics; 13 | bytes32 messageRoot; 14 | string[] proof; 15 | bytes32 receiptsRoot; 16 | } 17 | 18 | function buildProof(Fixture memory fixture) internal pure returns (bytes[] memory) { 19 | bytes[] memory proof = new bytes[](3); 20 | proof[0] = vm.parseBytes(fixture.proof[0]); 21 | proof[1] = vm.parseBytes(fixture.proof[1]); 22 | proof[2] = vm.parseBytes(fixture.proof[2]); 23 | return proof; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/libraries/Timelock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; 4 | 5 | contract Timelock is TimelockController { 6 | /** 7 | * @dev Initializes the contract with the following parameters: 8 | * 9 | * - `minDelay`: initial minimum delay for operations 10 | * - `proposers`: accounts to be granted proposer and canceller roles 11 | * - `executors`: accounts to be granted executor role 12 | * - `admin`: optional account to be granted admin role; disable with zero address 13 | */ 14 | constructor( 15 | uint256 minDelay, 16 | address[] memory proposers, 17 | address[] memory executors, 18 | address admin 19 | ) TimelockController(minDelay, proposers, executors, admin) {} 20 | } 21 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | solc_version = "0.8.16" 6 | verbosity = 2 7 | fs_permissions = [{ access = "read-write", path = "./"}] 8 | ffi = true 9 | 10 | [profile.external] 11 | src = 'external' 12 | out = 'out' 13 | libs = ['lib'] 14 | solc_version = "0.8.16" 15 | verbosity = 3 16 | fs_permissions = [{ access = "read-write", path = "./"}] 17 | 18 | [profile.misc] 19 | src = 'misc' 20 | out = 'out' 21 | libs = ['lib'] 22 | solc_version = "0.8.16" 23 | verbosity = 3 24 | fs_permissions = [{ access = "read-write", path = "./"}] 25 | 26 | # https://book.getfoundry.sh/reference/config/formatter#line_length 27 | [fmt] 28 | line_length = 100 29 | tab_width = 4 30 | func_attrs_with_params_multiline = true 31 | ignore = ["lib/**"] 32 | 33 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /external/examples/drills/DrillLightClient.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {LightClient} from "src/lightclient/LightClient.sol"; 4 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | /// @title DrillLightClient 7 | /// @dev This contract is used solely for testing purposes and should not be used by production contracts. 8 | contract DrillLightClient is LightClient, Ownable { 9 | constructor(address emitter) LightClient(0, 0, 0, 0, 0, 0, 0, 0) Ownable() { 10 | transferOwnership(emitter); 11 | } 12 | 13 | function emitFakeHeadUpdateEvent(uint256 _slot, bytes32 _root) external onlyOwner { 14 | emit HeadUpdate(_slot, _root); 15 | } 16 | 17 | function emitFakeSyncCommitteeUpdateEvent(uint256 _period, bytes32 _root) external onlyOwner { 18 | emit SyncCommitteeUpdate(_period, _root); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /external/integrations/eigenlayer/EigenLayerBeaconOracleStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 4 | 5 | contract EigenLayerBeaconOracleStorage { 6 | /// @notice The light client contract. 7 | ILightClient public lightclient; 8 | 9 | /// @notice The whitelist for the oracle updaters. 10 | mapping(address => bool) public whitelistedOracleUpdaters; 11 | 12 | /// @notice The block number to state root mapping. 13 | mapping(uint256 => bytes32) public timestampToBlockRoot; 14 | 15 | /// @dev This empty reserved space is put in place to allow future versions to add new variables 16 | /// without shifting down storage in the inheritance chain. 17 | /// See: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps 18 | uint256[50] private __gap; 19 | } 20 | -------------------------------------------------------------------------------- /src/libraries/BeaconChainForks.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | library BeaconChainForks { 4 | function getCapellaSlot(uint32 sourceChainId) internal pure returns (uint256) { 5 | // Returns CAPELLA_FORK_EPOCH * SLOTS_PER_EPOCH for the corresponding beacon chain. 6 | if (sourceChainId == 1) { 7 | // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/fork.md?plain=1#L30 8 | return 6209536; 9 | } else if (sourceChainId == 5) { 10 | // https://blog.ethereum.org/2023/03/08/goerli-shapella-announcement 11 | // https://github.com/eth-clients/goerli/blob/main/prater/config.yaml#L43 12 | return 5193728; 13 | } else { 14 | // We don't know the exact value for Gnosis Chain yet so we return max uint256 15 | // and fallback to bellatrix logic. 16 | return 2 ** 256 - 1; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/amb-v2/verifier/interfaces/IMessageVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | enum VerifierType { 5 | NULL, 6 | CUSTOM, 7 | ZK_EVENT, 8 | ZK_STORAGE, 9 | ATTESTATION_STATE_QUERY 10 | } 11 | 12 | /// @title IMessageVerifier 13 | /// @author Succinct Labs 14 | /// @notice Interface for a message verifier. 15 | interface IMessageVerifier { 16 | /// @notice Returns the type of the verifier. 17 | /// @dev This signals what type of proofData to include for the message. 18 | function verifierType() external view returns (VerifierType); 19 | 20 | /// @notice Verifies a message. 21 | /// @param proofData The packed proof data that the proves the message is valid. 22 | /// @param message The message contents. 23 | /// @return isValid Whether the message is valid. 24 | function verify(bytes calldata proofData, bytes calldata message) external returns (bool); 25 | } 26 | -------------------------------------------------------------------------------- /src/amb/interfaces/TelepathyHandler.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import {ITelepathyHandler} from "src/amb/interfaces/ITelepathy.sol"; 4 | 5 | abstract contract TelepathyHandler is ITelepathyHandler { 6 | error NotFromTelepathyRouter(address sender); 7 | 8 | address public telepathyRouter; 9 | 10 | constructor(address _telepathyRouter) { 11 | telepathyRouter = _telepathyRouter; 12 | } 13 | 14 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 15 | external 16 | override 17 | returns (bytes4) 18 | { 19 | if (msg.sender != telepathyRouter) { 20 | revert NotFromTelepathyRouter(msg.sender); 21 | } 22 | handleTelepathyImpl(_sourceChainId, _sourceAddress, _data); 23 | return ITelepathyHandler.handleTelepathy.selector; 24 | } 25 | 26 | function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 27 | internal 28 | virtual; 29 | } 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/openzeppelin/openzeppelin-contracts 7 | [submodule "lib/v3-core"] 8 | path = lib/v3-core 9 | url = https://github.com/succinctlabs/v3-core 10 | branch = main 11 | [submodule "lib/openzeppelin-contracts-upgradeable"] 12 | path = lib/openzeppelin-contracts-upgradeable 13 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 14 | branch = v4.8.1 15 | [submodule "lib/optimism-bedrock-contracts"] 16 | path = lib/optimism-bedrock-contracts 17 | url = https://github.com/succinctlabs/optimism-bedrock-contracts 18 | branch = main 19 | [submodule "lib/telepathy-v2"] 20 | path = lib/telepathy-v2 21 | url = https://github.com/succinctlabs/telepathy-v2 22 | branch = main 23 | [submodule "lib/safe-contracts"] 24 | path = lib/safe-contracts 25 | url = https://github.com/safe-global/safe-contracts 26 | -------------------------------------------------------------------------------- /external/examples/bridge/Tokens.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.16; 3 | 4 | import "forge-std/console.sol"; 5 | import "ds-test/test.sol"; 6 | import "forge-std/Vm.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | import "src/amb/mocks/MockTelepathy.sol"; 10 | 11 | import "./Tokens.sol"; 12 | 13 | contract TokenTest is Test { 14 | using stdStorage for StdStorage; 15 | 16 | function setUp() public {} 17 | 18 | // Test that we can mint an infinite amount of tokens 19 | function test_Mint_WhenConsecutive() public { 20 | InfiniteMintSuccincts mytoken = new InfiniteMintSuccincts(0, address(this)); 21 | uint256 balance = mytoken.balanceOf(address(this)); 22 | assertEq(balance, 0); 23 | mytoken.mint(address(this), 100); 24 | uint256 balance2 = mytoken.balanceOf(address(this)); 25 | assertEq(balance2, 100); 26 | mytoken.mint(address(this), 100); 27 | uint256 balance3 = mytoken.balanceOf(address(this)); 28 | assertEq(balance3, 200); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/amb-v2/interfaces/TelepathyHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {ITelepathyHandlerV2} from "src/amb-v2/interfaces/ITelepathy.sol"; 5 | 6 | abstract contract TelepathyHandlerV2 is ITelepathyHandlerV2 { 7 | error NotFromTelepathyRouterV2(address sender); 8 | 9 | address public telepathyRouter; 10 | 11 | constructor(address _telepathyRouter) { 12 | telepathyRouter = _telepathyRouter; 13 | } 14 | 15 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 16 | external 17 | override 18 | returns (bytes4) 19 | { 20 | if (msg.sender != telepathyRouter) { 21 | revert NotFromTelepathyRouterV2(msg.sender); 22 | } 23 | handleTelepathyImpl(_sourceChainId, _sourceAddress, _data); 24 | return ITelepathyHandlerV2.handleTelepathy.selector; 25 | } 26 | 27 | function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 28 | internal 29 | virtual; 30 | } 31 | -------------------------------------------------------------------------------- /external/examples/bridge/Tokens.sol: -------------------------------------------------------------------------------- 1 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 2 | 3 | // SPDX-License-Identifier: MIT 4 | pragma solidity 0.8.16; 5 | 6 | import "forge-std/console.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 9 | import "@openzeppelin/contracts/access/Ownable.sol"; 10 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 11 | 12 | interface IERC20Ownable is IERC20 { 13 | function mint(address to, uint256 amount) external; 14 | } 15 | 16 | contract Succincts is ERC20Burnable, Ownable, IERC20Ownable { 17 | constructor() ERC20("Succincts", "SUCC") {} 18 | 19 | function mint(address to, uint256 amount) public onlyOwner { 20 | _mint(to, amount); 21 | } 22 | } 23 | 24 | contract InfiniteMintSuccincts is ERC20 { 25 | constructor(uint256 initialSupply, address minter) ERC20("Succincts", "SUCC") { 26 | _mint(minter, initialSupply); 27 | } 28 | 29 | function mint(address to, uint256 amount) public { 30 | _mint(to, amount); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/oracle/TelepathyOracleFulfiller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {ITelepathyRouter} from "src/amb/interfaces/ITelepathy.sol"; 4 | import {RequestData} from "src/oracle/TelepathyOracle.sol"; 5 | 6 | contract TelepathyOracleFulfiller { 7 | ITelepathyRouter telepathyRouter; 8 | 9 | constructor(address _telepathyRouter) { 10 | telepathyRouter = ITelepathyRouter(_telepathyRouter); 11 | } 12 | 13 | function fulfillCrossChainRequest( 14 | uint32 _oracleChain, 15 | address _oracleAddress, 16 | RequestData calldata _requestData 17 | ) external { 18 | bool success = false; 19 | bytes memory resultData; 20 | if (_requestData.targetContract.code.length != 0) { 21 | (success, resultData) = _requestData.targetContract.call(_requestData.targetCalldata); 22 | } 23 | bytes32 requestHash = keccak256(abi.encode(_requestData)); 24 | bytes memory data = abi.encode( 25 | _requestData.nonce, requestHash, _requestData.callbackContract, resultData, success 26 | ); 27 | telepathyRouter.send(_oracleChain, _oracleAddress, data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /external/examples/drills/DrillTelepathyRouter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {TargetAMB} from "src/amb/TargetAMB.sol"; 4 | import {TelepathyStorage} from "src/amb/TelepathyStorage.sol"; 5 | import {TelepathyAccess} from "src/amb/TelepathyAccess.sol"; 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | /// @title DrillTelepathy 9 | /// @dev This contract is used solely for testing purposes and should not be used by production contracts. 10 | contract DrillTelepathyRouter is TelepathyStorage, TargetAMB { 11 | address guardian; 12 | address emitter; 13 | 14 | constructor(address _guardian, address _emitter) { 15 | guardian = _guardian; 16 | emitter = _emitter; 17 | } 18 | 19 | function freezeAll() external view { 20 | require(msg.sender == guardian); 21 | } 22 | 23 | function emitFakeExecutedMessageEvent( 24 | uint16 _chainId, 25 | uint64 _nonce, 26 | bytes32 _msgHash, 27 | bytes memory _message, 28 | bool _status 29 | ) external { 30 | require(msg.sender == emitter); 31 | emit ExecutedMessage(_chainId, _nonce, _msgHash, _message, _status); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/amb/interfaces/TelepathyHandlerUpgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import {ITelepathyHandler} from "src/amb/interfaces/ITelepathy.sol"; 4 | import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; 5 | 6 | abstract contract TelepathyHandlerUpgradeable is ITelepathyHandler, Initializable { 7 | error NotFromTelepathyRouter(address sender); 8 | 9 | address public telepathyRouter; 10 | 11 | function __TelepathyHandler_init(address _telepathyRouter) internal onlyInitializing { 12 | telepathyRouter = _telepathyRouter; 13 | } 14 | 15 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 16 | external 17 | override 18 | returns (bytes4) 19 | { 20 | if (msg.sender != telepathyRouter) { 21 | revert NotFromTelepathyRouter(msg.sender); 22 | } 23 | handleTelepathyImpl(_sourceChainId, _sourceAddress, _data); 24 | return ITelepathyHandler.handleTelepathy.selector; 25 | } 26 | 27 | function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 28 | internal 29 | virtual; 30 | } 31 | -------------------------------------------------------------------------------- /src/lightclient/LightClientMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | // We must import X as Y so that ILightClient doesn't conflict when we try to import 4 | // both LightClientMock and TargetAMB in the same test file. 5 | import {ILightClient as ILightClientMock} from "src/lightclient/interfaces/ILightClient.sol"; 6 | 7 | contract LightClientMock is ILightClientMock { 8 | bool public consistent = true; 9 | uint256 public head; 10 | mapping(uint256 => bytes32) public headers; 11 | mapping(uint256 => bytes32) public executionStateRoots; 12 | mapping(uint256 => uint256) public timestamps; 13 | 14 | event HeadUpdate(uint256 indexed slot, bytes32 indexed root); 15 | 16 | function setHeader(uint256 slot, bytes32 headerRoot) external { 17 | headers[slot] = headerRoot; 18 | timestamps[slot] = block.timestamp; 19 | head = slot; 20 | emit HeadUpdate(slot, headerRoot); 21 | } 22 | 23 | function setExecutionRoot(uint256 slot, bytes32 executionRoot) external { 24 | executionStateRoots[slot] = executionRoot; 25 | timestamps[slot] = block.timestamp; 26 | head = slot; 27 | emit HeadUpdate(slot, executionRoot); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/amb-v2/interfaces/TelepathyHandlerUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {ITelepathyHandlerV2} from "src/amb-v2/interfaces/ITelepathy.sol"; 5 | import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; 6 | 7 | abstract contract TelepathyHandlerV2UpgradeableV2 is ITelepathyHandlerV2, Initializable { 8 | error NotFromTelepathyRouterV2(address sender); 9 | 10 | address public telepathyRouter; 11 | 12 | function __TelepathyHandlerV2_init(address _telepathyRouter) internal onlyInitializing { 13 | telepathyRouter = _telepathyRouter; 14 | } 15 | 16 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 17 | external 18 | override 19 | returns (bytes4) 20 | { 21 | if (msg.sender != telepathyRouter) { 22 | revert NotFromTelepathyRouterV2(msg.sender); 23 | } 24 | handleTelepathyImpl(_sourceChainId, _sourceAddress, _data); 25 | return ITelepathyHandlerV2.handleTelepathy.selector; 26 | } 27 | 28 | function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 29 | internal 30 | virtual; 31 | } 32 | -------------------------------------------------------------------------------- /src/pubsub/PubSubStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {PublishStatus} from "src/pubsub/interfaces/IPubSub.sol"; 4 | 5 | import {SubscriptionStatus} from "src/pubsub/interfaces/IPubSub.sol"; 6 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 7 | import {TelepathyRouter} from "src/amb/TelepathyRouter.sol"; 8 | 9 | contract PubSubStorage { 10 | address public guardian; 11 | 12 | address public timelock; 13 | 14 | ILightClient public lightClient; 15 | 16 | bool public paused; 17 | 18 | /*////////////////////////////////////////////////////////////// 19 | PUBLISHER STORAGE 20 | //////////////////////////////////////////////////////////////*/ 21 | 22 | mapping(bytes32 => PublishStatus) public eventsPublished; 23 | 24 | /*////////////////////////////////////////////////////////////// 25 | SUBSCRIBER STORAGE 26 | //////////////////////////////////////////////////////////////*/ 27 | 28 | mapping(bytes32 => SubscriptionStatus) public subscriptions; 29 | 30 | /// @dev This empty reserved space is put in place to allow future versions to add new variables 31 | /// without shifting down storage in the inheritance chain. 32 | /// See: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps 33 | uint256[50] private __gap; // TODO reduce by 1 for each 34 | } 35 | -------------------------------------------------------------------------------- /external/examples/counter/CountMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {ITelepathyRouterV2} from "src/amb-v2/interfaces/ITelepathy.sol"; 5 | import {TelepathyHandlerV2} from "src/amb-v2/interfaces/TelepathyHandler.sol"; 6 | 7 | /// @notice Example Counter that uses messaging to make a cross-chain increment call. 8 | /// @dev Assumes that this contract is deployed at the same address on all chains. 9 | contract CountMessenger is TelepathyHandlerV2 { 10 | uint256 public count; 11 | 12 | event Incremented(uint32 srcChainId, address sender); 13 | 14 | error NotFromCountMessenger(address sender); 15 | 16 | constructor(address _telepathyRouter) TelepathyHandlerV2(_telepathyRouter) {} 17 | 18 | /// @notice Sends a cross-chain increment message. 19 | function sendIncrement(uint32 _dstChainId) external payable { 20 | ITelepathyRouterV2(telepathyRouter).send{value: msg.value}(_dstChainId, address(this), ""); 21 | } 22 | 23 | /// @notice Recieve a cross-chain increment message. 24 | function handleTelepathyImpl(uint32 _srcChainId, address _srcAddress, bytes memory) 25 | internal 26 | override 27 | { 28 | if (_srcAddress != address(this)) { 29 | revert NotFromCountMessenger(_srcAddress); 30 | } 31 | 32 | count++; 33 | 34 | emit Incremented(_srcChainId, _srcAddress); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /external/examples/example-counter/ExampleCounter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {ITelepathyRouterV2} from "src/amb-v2/interfaces/ITelepathy.sol"; 4 | import {TelepathyHandlerV2} from "src/amb-v2/interfaces/TelepathyHandler.sol"; 5 | 6 | contract SourceCounter { 7 | ITelepathyRouterV2 router; 8 | uint32 targetChainId; 9 | 10 | constructor(address _router, uint32 _targetChainId) { 11 | router = ITelepathyRouterV2(_router); 12 | targetChainId = _targetChainId; 13 | } 14 | 15 | // Increment counter on target chain by given amount 16 | function increment(uint256 amount, address targetCounter) external payable virtual { 17 | bytes memory msgData = abi.encode(amount); 18 | router.send{value: msg.value}(targetChainId, targetCounter, msgData); 19 | } 20 | } 21 | 22 | contract TargetCounter is TelepathyHandlerV2 { 23 | uint256 public counter = 0; 24 | 25 | event Incremented(uint32 sourceChainId, address sender, uint256 amount); 26 | 27 | constructor(address _router) TelepathyHandlerV2(_router) {} 28 | 29 | // Handle messages being sent and decoding 30 | function handleTelepathyImpl(uint32 sourceChainId, address sender, bytes memory msgData) 31 | internal 32 | override 33 | { 34 | (uint256 amount) = abi.decode(msgData, (uint256)); 35 | unchecked { 36 | counter = counter + amount; 37 | } 38 | emit Incremented(sourceChainId, sender, amount); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/integrations/diva/fixtures/diva_deposit_6308974.json: -------------------------------------------------------------------------------- 1 | { 2 | "depositIndex": 0, 3 | "headerRoot": "0x8ae7501389bf5039ecae495ef97e96ea75e4cef982c58ff348fc80dcf3b8ac58", 4 | "pubkeyHash": "0xa4e77f77152b8d18f0ddf12f4186f06e681eda3afa1639b784d8acb81f10a68d", 5 | "pubkeyProof": [ 6 | "0x0100000000000000000000004a93ab8b0103cc8bb2a712ef0241748ba0cad9ff", 7 | "0xfb930008e535309e229529006273c43c93caf3c403dbe38347979a3480c5913c", 8 | "0xaa3428e719175fbec375fcf914a297403e4c78dde3692070982e55a0fb1d9ee2", 9 | "0x044488d632d197e23911a12fce2599d7e288946a197d3fa3c03112f350972e23", 10 | "0x2fae92a292147846a7b9208112c518f1fe624acf9f1f2b79deb7d2184d4dadaa", 11 | "0x0f9b7ebafb9639e6e6f1945c100ce58a51a9e8520e5d08627fa1d406bdb73a93", 12 | "0xebccfe6ae52ce0028a1fd21026a55ba29172d21a8149ea304e463b0142c95406", 13 | "0x1000000000000000000000000000000000000000000000000000000000000000", 14 | "0x792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535", 15 | "0x0078dc18c92af3ecee78c7b4d569f455a33497362ca87d72e3444216a2266402", 16 | "0xef3bf7405b3440b68a288064dcf786cadc5ca426f204fffef1196c002b4ac404", 17 | "0x4467cb4d42ad8f8b9eca62ddc7666cfb8e1d0bb365e5f1245f109c6b1e5f50ad", 18 | "0x0000000000000000000000000000000000000000000000000000000000000000", 19 | "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", 20 | "0x86ba29fd0d93db71f9f08cd6a60a61f40fa8df59f2f791a57a37958950610ce4" 21 | ], 22 | "slot": 6308974 23 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Succinct Bug Bounty Program 2 | 3 | ## Overview 4 | 5 | This bug bounty program is specifically for [Telepathy](https://github.com/succinctlabs/telepathy-contracts)’s smart contract and circuit code. 6 | 7 | Our bug bounty security guidelines are based on [Immunefi’s vulnerability severity classification system](https://immunefi.com/immunefi-vulnerability-severity-classification-system-v2-2/), and are subject to change at any time. 8 | 9 | The bug bounty program is administered by Succinct. All bug bounty decisions made are final. An Immunefi-based bug bounty program is coming soon. 10 | 11 | ## Security Classifications and Bounty Amounts 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | 41 |
SeverityDescriptionBounty
Critical 23 |
    24 |
  • Incorrect light client state
  • 25 |
  • Incorrect message passing
  • 26 |
  • Permanent freezing of the protocol
  • 27 |
28 |
Up to $100,000
High 34 |
    35 |
  • Temporary freezing of the protocol
  • 36 |
37 |
Up to $25,000
42 | 43 | ## Report Submission 44 | 45 | You can contact us at bugs@succinct.xyz to provide a thorough account of the attack vector. Our team will respond within 24 hours and may ask for further details or provide information on the bug bounty's next steps. 46 | -------------------------------------------------------------------------------- /src/amb-v2/verifier/BeaconVerifierBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; 5 | 6 | /// @title BeaconVerifierBase 7 | /// @author Succinct Labs 8 | /// @notice Base contract for verifiers to use when verifying against the Beacon LightClient. 9 | contract BeaconVerifierBase is Initializable { 10 | /// @notice Source ChainId => LightClient address. 11 | mapping(uint32 => address) public lightClients; 12 | /// @notice Source ChainId => TelepathyRouter address. 13 | mapping(uint32 => address) public telepathyRouters; 14 | 15 | error InvalidSourceChainLength(uint256 length); 16 | error LightClientNotFound(uint32 sourceChainId); 17 | error TelepathyRouterNotFound(uint32 sourceChainId); 18 | 19 | function __BeaconVerifierBase_init( 20 | uint32[] memory _sourceChainIds, 21 | address[] memory _lightClients, 22 | address[] memory _telepathyRouters 23 | ) internal onlyInitializing { 24 | if (_sourceChainIds.length != _lightClients.length) { 25 | revert InvalidSourceChainLength(_sourceChainIds.length); 26 | } 27 | if (_sourceChainIds.length != _telepathyRouters.length) { 28 | revert InvalidSourceChainLength(_sourceChainIds.length); 29 | } 30 | for (uint32 i = 0; i < _sourceChainIds.length; i++) { 31 | lightClients[_sourceChainIds[i]] = _lightClients[i]; 32 | telepathyRouters[_sourceChainIds[i]] = _telepathyRouters[i]; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/pubsub/interfaces/SubscriptionReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {TelepathyPubSub} from "src/pubsub/TelepathyPubSub.sol"; 4 | import {ISubscriptionReceiver} from "src/pubsub/interfaces/ISubscriptionReceiver.sol"; 5 | 6 | abstract contract SubscriptionReceiver is ISubscriptionReceiver { 7 | TelepathyPubSub public telepathyPubSub; 8 | 9 | error NotFromTelepathyPubSub(address sender); 10 | 11 | constructor(address _telepathyPubSub) { 12 | telepathyPubSub = TelepathyPubSub(_telepathyPubSub); 13 | } 14 | 15 | function handlePublish( 16 | bytes32 _subscriptionId, 17 | uint32 _sourceChainId, 18 | address _sourceAddress, 19 | uint64 _slot, 20 | bytes32 _publishKey, 21 | bytes32[] memory _eventTopics, 22 | bytes memory _eventData 23 | ) external override returns (bytes4) { 24 | if (msg.sender != address(telepathyPubSub)) { 25 | revert NotFromTelepathyPubSub(msg.sender); 26 | } 27 | handlePublishImpl( 28 | _subscriptionId, 29 | _sourceChainId, 30 | _sourceAddress, 31 | _slot, 32 | _publishKey, 33 | _eventTopics, 34 | _eventData 35 | ); 36 | return ISubscriptionReceiver.handlePublish.selector; 37 | } 38 | 39 | function handlePublishImpl( 40 | bytes32 _subscriptionId, 41 | uint32 _sourceChainId, 42 | address _sourceAddress, 43 | uint64 _slot, 44 | bytes32 _publishKey, 45 | bytes32[] memory _eventTopics, 46 | bytes memory _eventData 47 | ) internal virtual; 48 | } 49 | -------------------------------------------------------------------------------- /src/amb-v2/interfaces/ITelepathy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | // A magic destinationChainId number to specify for messages that can be executed on any chain. 5 | // Check the doc for current set of chains where the message will be executed. If any are not 6 | // included in this set, it will still be possible to execute via self-relay. 7 | uint32 constant BROADCAST_ALL_CHAINS = uint32(0); 8 | 9 | enum MessageStatus { 10 | NOT_EXECUTED, 11 | EXECUTION_FAILED, // Deprecated in V2: failed handleTelepathy calls will cause the execute call to revert 12 | EXECUTION_SUCCEEDED 13 | } 14 | 15 | interface ITelepathyRouterV2 { 16 | event SentMessage(uint64 indexed nonce, bytes32 indexed msgHash, bytes message); 17 | 18 | function send(uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data) 19 | external 20 | payable 21 | returns (bytes32); 22 | 23 | function send(uint32 destinationChainId, address destinationAddress, bytes calldata data) 24 | external 25 | payable 26 | returns (bytes32); 27 | } 28 | 29 | interface ITelepathyReceiverV2 { 30 | event ExecutedMessage( 31 | uint32 indexed sourceChainId, 32 | uint64 indexed nonce, 33 | bytes32 indexed msgHash, 34 | bytes message, 35 | bool success 36 | ); 37 | 38 | function execute(bytes calldata _proof, bytes calldata _message) external; 39 | } 40 | 41 | interface ITelepathyHandlerV2 { 42 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 43 | external 44 | returns (bytes4); 45 | } 46 | -------------------------------------------------------------------------------- /src/pubsub/TelepathyPubSub.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {TelepathyPublisher} from "src/pubsub/TelepathyPublisher.sol"; 4 | import {TelepathySubscriber} from "src/pubsub/TelepathySubscriber.sol"; 5 | import {PubSubStorage} from "src/pubsub/PubSubStorage.sol"; 6 | import {TelepathyRouter} from "src/amb/TelepathyRouter.sol"; 7 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 8 | 9 | /// @title TelepathyPubSub 10 | /// @author Succinct Labs 11 | /// @notice This allows an on-chain Publisher-Suscriber model to be used for events. Contracts can subscribe to 12 | /// events emitted from a source contract, and it will be relayed these events through the publisher. Before 13 | /// the events are relayed, they are verified using the Telepathy Light Client for proof of consensus on the 14 | /// source chain. 15 | contract TelepathyPubSub is TelepathyPublisher, TelepathySubscriber { 16 | constructor(address _guardian, address _timelock, address _lightClient) { 17 | guardian = _guardian; 18 | timelock = _timelock; 19 | lightClient = ILightClient(_lightClient); 20 | paused = false; 21 | } 22 | 23 | modifier onlyGuardian() { 24 | require(msg.sender == guardian, "Only guardian"); 25 | _; 26 | } 27 | 28 | modifier onlyTimelock() { 29 | require(msg.sender == timelock, "Only timelock"); 30 | _; 31 | } 32 | 33 | function togglePause() external onlyGuardian { 34 | paused = !paused; 35 | } 36 | 37 | function setLightClient(address _lightClient) external onlyTimelock { 38 | lightClient = ILightClient(_lightClient); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /external/integrations/omnibridge/TelepathyValidator.s.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "forge-std/Script.sol"; 4 | import "forge-std/Vm.sol"; 5 | 6 | import {TelepathyValidator} from "./TelepathyValidator.sol"; 7 | import {TelepathyPubSub} from "src/pubsub/TelepathyPubSub.sol"; 8 | 9 | contract Deploy is Script { 10 | function run() public { 11 | address GUARDIAN_ADDRESS = vm.envAddress("GUARDIAN_ADDRESS"); 12 | address TIMELOCK_ADDRESS = vm.envAddress("TIMELOCK_ADDRESS"); 13 | address LIGHT_CLIENT_ADDRESS = vm.envAddress("LIGHT_CLIENT_ADDRESS"); 14 | 15 | vm.startBroadcast(); 16 | TelepathyPubSub pubsub = new TelepathyPubSub( 17 | GUARDIAN_ADDRESS, 18 | TIMELOCK_ADDRESS, 19 | LIGHT_CLIENT_ADDRESS 20 | ); 21 | vm.stopBroadcast(); 22 | 23 | uint32 SOURCE_CHAIN_ID = uint32(vm.envUint("SOURCE_CHAIN_ID")); 24 | address AMB_AFFIRMATION_SOURCE_ADDRESS = vm.envAddress("AMB_AFFIRMATION_SOURCE_ADDRESS"); 25 | uint64 START_SLOT = uint64(vm.envUint("START_SLOT")); 26 | uint64 END_SLOT = uint64(vm.envUint("END_SLOT")); 27 | address HOME_AMB_ADDRESS = vm.envAddress("HOME_AMB_ADDRESS"); 28 | address OWNER = vm.envAddress("OWNER"); 29 | 30 | vm.startBroadcast(); 31 | TelepathyValidator validator = new TelepathyValidator(); 32 | validator.initialize( 33 | address(pubsub), 34 | SOURCE_CHAIN_ID, 35 | AMB_AFFIRMATION_SOURCE_ADDRESS, 36 | START_SLOT, 37 | END_SLOT, 38 | HOME_AMB_ADDRESS, 39 | OWNER 40 | ); 41 | vm.stopBroadcast(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pubsub/interfaces/SubscriptionReceiverUpgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {TelepathyPubSub} from "src/pubsub/TelepathyPubSub.sol"; 4 | import {ISubscriptionReceiver} from "src/pubsub/interfaces/ISubscriptionReceiver.sol"; 5 | import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; 6 | 7 | abstract contract SubscriptionReceiverUpgradeable is ISubscriptionReceiver, Initializable { 8 | TelepathyPubSub public telepathyPubSub; 9 | 10 | error NotFromTelepathyPubSub(address sender); 11 | 12 | function __SubscriptionReceiver_init(address _telepathyPubSub) internal onlyInitializing { 13 | telepathyPubSub = TelepathyPubSub(_telepathyPubSub); 14 | } 15 | 16 | function handlePublish( 17 | bytes32 _subscriptionId, 18 | uint32 _sourceChainId, 19 | address _sourceAddress, 20 | uint64 _slot, 21 | bytes32 _publishKey, 22 | bytes32[] memory _eventTopics, 23 | bytes memory _eventData 24 | ) external override returns (bytes4) { 25 | if (msg.sender != address(telepathyPubSub)) { 26 | revert NotFromTelepathyPubSub(msg.sender); 27 | } 28 | handlePublishImpl( 29 | _subscriptionId, 30 | _sourceChainId, 31 | _sourceAddress, 32 | _slot, 33 | _publishKey, 34 | _eventTopics, 35 | _eventData 36 | ); 37 | return ISubscriptionReceiver.handlePublish.selector; 38 | } 39 | 40 | function handlePublishImpl( 41 | bytes32 _subscriptionId, 42 | uint32 _sourceChainId, 43 | address _sourceAddress, 44 | uint64 _slot, 45 | bytes32 _publishKey, 46 | bytes32[] memory _eventTopics, 47 | bytes memory _eventData 48 | ) internal virtual; 49 | } 50 | -------------------------------------------------------------------------------- /test/libraries/StateProofFixture.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "forge-std/Base.sol"; 4 | 5 | /// @notice Helper contract for parsing the JSON fixtures, and converting them to the correct types. 6 | /// @dev The weird ordering here is because vm.parseJSON require alphabetical ordering of the 7 | /// fields in the struct, and odd types with conversions are due to the way the JSON is 8 | /// handled. 9 | contract StateProofFixture is CommonBase { 10 | struct StorageProofFixture { 11 | address contractAddress; 12 | string[] proof; 13 | bytes32 stateRootHash; 14 | bytes32 storageRoot; 15 | } 16 | 17 | struct EventProofFixture { 18 | address claimedEmitter; 19 | string key; 20 | uint256 logIndex; 21 | bytes32 messageRoot; 22 | string[] proof; 23 | bytes32 receiptsRoot; 24 | } 25 | 26 | function buildStorageProof(StorageProofFixture memory fixture) 27 | internal 28 | pure 29 | returns (bytes[] memory) 30 | { 31 | bytes[] memory proof = new bytes[](8); 32 | proof[0] = vm.parseBytes(fixture.proof[0]); 33 | proof[1] = vm.parseBytes(fixture.proof[1]); 34 | proof[2] = vm.parseBytes(fixture.proof[2]); 35 | proof[3] = vm.parseBytes(fixture.proof[3]); 36 | proof[4] = vm.parseBytes(fixture.proof[4]); 37 | proof[5] = vm.parseBytes(fixture.proof[5]); 38 | proof[6] = vm.parseBytes(fixture.proof[6]); 39 | proof[7] = vm.parseBytes(fixture.proof[7]); 40 | return proof; 41 | } 42 | 43 | function buildEventProof(EventProofFixture memory fixture) 44 | internal 45 | pure 46 | returns (bytes[] memory) 47 | { 48 | bytes[] memory proof = new bytes[](3); 49 | proof[0] = vm.parseBytes(fixture.proof[0]); 50 | proof[1] = vm.parseBytes(fixture.proof[1]); 51 | proof[2] = vm.parseBytes(fixture.proof[2]); 52 | return proof; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /external/examples/example-counter/ExampleCounter.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.16; 3 | 4 | import "forge-std/console.sol"; 5 | import "ds-test/test.sol"; 6 | import "forge-std/Vm.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | import "src/amb/mocks/MockTelepathy.sol"; 10 | 11 | import "./ExampleCounter.sol"; 12 | 13 | contract CounterTest is Test { 14 | uint32 constant SOURCE_CHAIN = 1; 15 | uint32 constant TARGET_CHAIN = 100; 16 | 17 | MockTelepathy router; 18 | MockTelepathy receiver; 19 | SourceCounter source; 20 | TargetCounter target; 21 | 22 | function setUp() public { 23 | router = new MockTelepathy(SOURCE_CHAIN); 24 | receiver = new MockTelepathy(TARGET_CHAIN); 25 | router.addTelepathyReceiver(TARGET_CHAIN, receiver); 26 | 27 | source = new SourceCounter(address(router), TARGET_CHAIN); 28 | target = new TargetCounter(address(receiver)); 29 | } 30 | 31 | function test_IncrementOne() public { 32 | source.increment(1, address(target)); 33 | router.executeNextMessage(); 34 | require(target.counter() == 1); 35 | } 36 | 37 | function test_IncrementSeveral() public { 38 | source.increment(2, address(target)); 39 | router.executeNextMessage(); 40 | require(target.counter() == 2); 41 | source.increment(123456789, address(target)); 42 | router.executeNextMessage(); 43 | require(target.counter() == 123456791); 44 | } 45 | 46 | function test_IncrementOverflow() public { 47 | source.increment( 48 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, address(target) 49 | ); 50 | router.executeNextMessage(); 51 | require( 52 | target.counter() == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 53 | ); 54 | source.increment(2, address(target)); 55 | router.executeNextMessage(); 56 | require(target.counter() == 1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /external/examples/uniswap/UniswapExample.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "forge-std/Vm.sol"; 4 | import "forge-std/console.sol"; 5 | import "forge-std/Test.sol"; 6 | 7 | import {MockTelepathy} from "src/amb/mocks/MockTelepathy.sol"; 8 | 9 | import "./UniswapExample.sol"; 10 | 11 | contract UniswapTWAPTest is Test { 12 | uint32 constant SOURCE_CHAIN = 1; 13 | uint32 constant DEST_CHAIN = 100; 14 | 15 | bool skipTest; 16 | 17 | MockTelepathy router; 18 | MockTelepathy receiver; 19 | CrossChainTWAPRoute twapSender; 20 | CrossChainTWAPReceiver twapReceiver; 21 | 22 | function setUp() public { 23 | try vm.envString("MAINNET_RPC_URL") returns (string memory MAINNET_RPC_URL) { 24 | uint256 forkId = vm.createSelectFork(MAINNET_RPC_URL); 25 | if (forkId == 0) { 26 | console.log("Forking mainnet failed, skipping test"); 27 | skipTest = true; 28 | } 29 | } catch { 30 | console.log("MAINNET_RPC_URL not set, skipping test"); 31 | skipTest = true; 32 | } 33 | 34 | router = new MockTelepathy(SOURCE_CHAIN); 35 | receiver = new MockTelepathy(DEST_CHAIN); 36 | router.addTelepathyReceiver(DEST_CHAIN, receiver); 37 | twapSender = new CrossChainTWAPRoute(address(router)); 38 | twapReceiver = 39 | new CrossChainTWAPReceiver(SOURCE_CHAIN, address(twapSender), address(receiver)); 40 | twapSender.setDeliveringContract(uint32(DEST_CHAIN), address(twapReceiver)); 41 | } 42 | 43 | function test_UniswapBroadcast() public { 44 | if (skipTest) return; 45 | 46 | uint256 timestamp = 1641070800; 47 | vm.warp(timestamp); 48 | // ETH/USDC 0.3% pool on Eth mainnet 49 | address poolAddress = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; 50 | uint32 twapInterval = 60; // 60 seconds 51 | uint256 price = twapSender.routePrice(DEST_CHAIN, poolAddress, twapInterval); 52 | router.executeNextMessage(); 53 | (uint256 price_, uint256 timestamp_) = 54 | twapReceiver.getLatestPrice(poolAddress, twapInterval); 55 | require(price_ == price); 56 | require(timestamp_ == timestamp); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/amb/interfaces/ITelepathy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | enum MessageStatus { 4 | NOT_EXECUTED, 5 | EXECUTION_FAILED, 6 | EXECUTION_SUCCEEDED 7 | } 8 | 9 | struct Message { 10 | uint8 version; 11 | uint64 nonce; 12 | uint32 sourceChainId; 13 | address sourceAddress; 14 | uint32 destinationChainId; 15 | bytes32 destinationAddress; 16 | bytes data; 17 | } 18 | 19 | interface ITelepathyRouter { 20 | event SentMessage(uint64 indexed nonce, bytes32 indexed msgHash, bytes message); 21 | 22 | function send(uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data) 23 | external 24 | returns (bytes32); 25 | 26 | function send(uint32 destinationChainId, address destinationAddress, bytes calldata data) 27 | external 28 | returns (bytes32); 29 | 30 | function sendViaStorage( 31 | uint32 destinationChainId, 32 | bytes32 destinationAddress, 33 | bytes calldata data 34 | ) external returns (bytes32); 35 | 36 | function sendViaStorage( 37 | uint32 destinationChainId, 38 | address destinationAddress, 39 | bytes calldata data 40 | ) external returns (bytes32); 41 | } 42 | 43 | interface ITelepathyReceiver { 44 | event ExecutedMessage( 45 | uint32 indexed sourceChainId, 46 | uint64 indexed nonce, 47 | bytes32 indexed msgHash, 48 | bytes message, 49 | bool status 50 | ); 51 | 52 | function executeMessage( 53 | uint64 slot, 54 | bytes calldata message, 55 | bytes[] calldata accountProof, 56 | bytes[] calldata storageProof 57 | ) external; 58 | 59 | function executeMessageFromLog( 60 | bytes calldata srcSlotTxSlotPack, 61 | bytes calldata messageBytes, 62 | bytes32[] calldata receiptsRootProof, 63 | bytes32 receiptsRoot, 64 | bytes[] calldata receiptProof, // receipt proof against receipt root 65 | bytes memory txIndexRLPEncoded, 66 | uint256 logIndex 67 | ) external; 68 | } 69 | 70 | interface ITelepathyHandler { 71 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 72 | external 73 | returns (bytes4); 74 | } 75 | -------------------------------------------------------------------------------- /src/libraries/MerkleProof.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | library MerkleProof { 5 | function verifyProof(bytes32 _root, bytes32 _leaf, bytes32[] memory _proof, uint256 _index) 6 | public 7 | pure 8 | returns (bool) 9 | { 10 | bytes32 computedHash = _leaf; 11 | 12 | for (uint256 i = 0; i < _proof.length; i++) { 13 | if (_index % 2 == 0) { 14 | computedHash = keccak256(abi.encodePacked(computedHash, _proof[i])); 15 | } else { 16 | computedHash = keccak256(abi.encodePacked(_proof[i], computedHash)); 17 | } 18 | _index = _index / 2; 19 | } 20 | 21 | return computedHash == _root; 22 | } 23 | 24 | function getProof(bytes32[] memory nodes, uint256 index) 25 | public 26 | pure 27 | returns (bytes32[] memory) 28 | { 29 | // Build the tree 30 | uint256 treeHeight = ceilLog2(nodes.length); 31 | bytes32[][] memory tree = new bytes32[][](treeHeight + 1); 32 | tree[0] = nodes; 33 | 34 | for (uint256 i = 1; i <= treeHeight; i++) { 35 | uint256 previousLevelLength = tree[i - 1].length; 36 | bytes32[] memory currentLevel = new bytes32[](previousLevelLength / 2); 37 | 38 | for (uint256 j = 0; j < previousLevelLength; j += 2) { 39 | currentLevel[j / 2] = 40 | keccak256(abi.encodePacked(tree[i - 1][j], tree[i - 1][j + 1])); 41 | } 42 | 43 | tree[i] = currentLevel; 44 | } 45 | 46 | // Generate the proof 47 | bytes32[] memory proof = new bytes32[](treeHeight); 48 | for (uint256 i = 0; i < treeHeight; i++) { 49 | if (index % 2 == 0) { 50 | // sibling is on the right 51 | proof[i] = tree[i][index + 1]; 52 | } else { 53 | // sibling is on the left 54 | proof[i] = tree[i][index - 1]; 55 | } 56 | 57 | index = index / 2; 58 | } 59 | 60 | return proof; 61 | } 62 | 63 | function ceilLog2(uint256 _x) private pure returns (uint256 y) { 64 | require(_x != 0); 65 | y = (_x & (_x - 1)) == 0 ? 0 : 1; 66 | while (_x > 1) { 67 | _x >>= 1; 68 | y += 1; 69 | } 70 | return y; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/amb-v2/TelepathyRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 5 | import {TelepathyStorageV2} from "src/amb-v2/TelepathyStorage.sol"; 6 | import { 7 | ITelepathyReceiverV2, 8 | MessageStatus, 9 | ITelepathyHandlerV2, 10 | ITelepathyRouterV2 11 | } from "src/amb-v2/interfaces/ITelepathy.sol"; 12 | import {TargetAMBV2} from "src/amb-v2/TargetAMB.sol"; 13 | import {SourceAMBV2} from "src/amb-v2/SourceAMB.sol"; 14 | import {TelepathyAccessV2} from "src/amb-v2/TelepathyAccess.sol"; 15 | import {UUPSUpgradeable} from "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; 16 | 17 | /// @title Telepathy Router 18 | /// @author Succinct Labs 19 | /// @notice Send and receive arbitrary messages from other chains. 20 | contract TelepathyRouterV2 is SourceAMBV2, TargetAMBV2, TelepathyAccessV2, UUPSUpgradeable { 21 | /// @notice Returns current contract version. 22 | uint8 public constant VERSION = 2; 23 | 24 | /// @notice Prevents the implementation contract from being initialized outside of the upgradeable proxy. 25 | constructor() { 26 | _disableInitializers(); 27 | } 28 | 29 | /// @notice Initializes the contract and the parent contracts once. 30 | /// @param _sendingEnabled Whether the router is allowed to send messages. 31 | /// @param _executingEnabled Whether the router is allowed to execute messages. 32 | /// @param _feeVault The address of the IFeeVault. 33 | /// @param _timelock The address of the timelock. 34 | /// @param _guardian The address of the guardian. 35 | function initialize( 36 | bool _sendingEnabled, 37 | bool _executingEnabled, 38 | address _feeVault, 39 | address _timelock, 40 | address _guardian 41 | ) external initializer { 42 | __ReentrancyGuard_init(); 43 | __AccessControl_init(); 44 | _grantRole(GUARDIAN_ROLE, _guardian); 45 | _grantRole(TIMELOCK_ROLE, _timelock); 46 | _grantRole(DEFAULT_ADMIN_ROLE, _timelock); 47 | __UUPSUpgradeable_init(); 48 | 49 | sendingEnabled = _sendingEnabled; 50 | executingEnabled = _executingEnabled; 51 | feeVault = _feeVault; 52 | version = VERSION; 53 | } 54 | 55 | /// @notice Authorizes an upgrade for the implementation contract. 56 | function _authorizeUpgrade(address newImplementation) internal override onlyTimelock {} 57 | } 58 | -------------------------------------------------------------------------------- /src/amb/TelepathyRouter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {UUPSUpgradeable} from "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; 4 | 5 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 6 | 7 | import {TelepathyStorage} from "src/amb/TelepathyStorage.sol"; 8 | import { 9 | ITelepathyReceiver, 10 | Message, 11 | MessageStatus, 12 | ITelepathyHandler, 13 | ITelepathyRouter 14 | } from "src/amb/interfaces/ITelepathy.sol"; 15 | import {TargetAMB} from "src/amb/TargetAMB.sol"; 16 | import {SourceAMB} from "src/amb/SourceAMB.sol"; 17 | import {TelepathyAccess} from "src/amb/TelepathyAccess.sol"; 18 | 19 | /// @title Telepathy Router 20 | /// @author Succinct Labs 21 | /// @notice Send and receive arbitrary messages from other chains. 22 | contract TelepathyRouter is SourceAMB, TargetAMB, TelepathyAccess, UUPSUpgradeable { 23 | /// @notice Returns current contract version. 24 | uint8 public constant VERSION = 1; 25 | 26 | /// @notice Prevents the implementation contract from being initialized outside of the upgradeable proxy. 27 | constructor() { 28 | _disableInitializers(); 29 | } 30 | 31 | /// @notice Initializes the contract and the parent contracts once. 32 | function initialize( 33 | uint32[] memory _sourceChainIds, 34 | address[] memory _lightClients, 35 | address[] memory _broadcasters, 36 | address _timelock, 37 | address _guardian, 38 | bool _sendingEnabled 39 | ) external initializer { 40 | __ReentrancyGuard_init(); 41 | __AccessControl_init(); 42 | _grantRole(GUARDIAN_ROLE, _guardian); 43 | _grantRole(TIMELOCK_ROLE, _timelock); 44 | _grantRole(DEFAULT_ADMIN_ROLE, _timelock); 45 | __UUPSUpgradeable_init(); 46 | 47 | require(_sourceChainIds.length == _lightClients.length); 48 | require(_sourceChainIds.length == _broadcasters.length); 49 | 50 | sourceChainIds = _sourceChainIds; 51 | for (uint32 i = 0; i < sourceChainIds.length; i++) { 52 | lightClients[sourceChainIds[i]] = ILightClient(_lightClients[i]); 53 | broadcasters[sourceChainIds[i]] = _broadcasters[i]; 54 | } 55 | sendingEnabled = _sendingEnabled; 56 | version = VERSION; 57 | } 58 | 59 | /// @notice Authorizes an upgrade for the implementation contract. 60 | function _authorizeUpgrade(address newImplementation) internal override onlyTimelock {} 61 | } 62 | -------------------------------------------------------------------------------- /src/amb/TelepathyStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 4 | import {MessageStatus} from "src/amb/interfaces/ITelepathy.sol"; 5 | 6 | contract TelepathyStorage { 7 | /*////////////////////////////////////////////////////////////// 8 | BROADCASTER STORAGE 9 | //////////////////////////////////////////////////////////////*/ 10 | 11 | /// @notice Whether sending is enabled or not. 12 | bool public sendingEnabled; 13 | 14 | /// @notice Mapping between a nonce and a message root. 15 | mapping(uint64 => bytes32) public messages; 16 | 17 | /// @notice Keeps track of the next nonce to be used. 18 | uint64 public nonce; 19 | 20 | /*////////////////////////////////////////////////////////////// 21 | RECEIVER STORAGE 22 | //////////////////////////////////////////////////////////////*/ 23 | 24 | /// @notice All sourceChainIds. 25 | uint32[] public sourceChainIds; 26 | 27 | /// @notice Mapping between source chainId and the corresponding light client. 28 | mapping(uint32 => ILightClient) public lightClients; 29 | 30 | /// @notice Mapping between source chainId and the address of the Telepathy broadcaster on that chain. 31 | mapping(uint32 => address) public broadcasters; 32 | 33 | /// @notice Mapping between a source chainId and whether it's frozen. 34 | mapping(uint32 => bool) public frozen; 35 | 36 | /// @notice Mapping between a message root and its status. 37 | mapping(bytes32 => MessageStatus) public messageStatus; 38 | 39 | /*////////////////////////////////////////////////////////////// 40 | SHARED STORAGE 41 | //////////////////////////////////////////////////////////////*/ 42 | 43 | /// @notice Returns current contract version. 44 | uint8 public version; 45 | 46 | /*////////////////////////////////////////////////////////////// 47 | RECEIVER STORAGE V2 48 | //////////////////////////////////////////////////////////////*/ 49 | 50 | /// @notice Storage root cache. 51 | mapping(bytes32 => bytes32) public storageRootCache; 52 | 53 | /// @dev This empty reserved space is put in place to allow future versions to add new variables 54 | /// without shifting down storage in the inheritance chain. 55 | /// See: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps 56 | uint256[40] private __gap; 57 | } 58 | -------------------------------------------------------------------------------- /external/integrations/rocketpool/RocketPoolBeaconOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 4 | import {SSZ} from "src/libraries/SimpleSerialize.sol"; 5 | import {BeaconOracleHelper} from "external/integrations/libraries/BeaconOracleHelper.sol"; 6 | 7 | contract RocketPoolBeaconOracle { 8 | ILightClient lightclient; 9 | 10 | event BeaconOracleUpdate(uint256 validatorIndex); 11 | 12 | error InvalidLightClientAddress(); 13 | 14 | constructor(address _lightClient) { 15 | if (_lightClient == address(0)) { 16 | revert InvalidLightClientAddress(); 17 | } 18 | lightclient = ILightClient(_lightClient); 19 | } 20 | 21 | /// @notice Mapping from validator index to validator struct 22 | mapping(uint256 => BeaconOracleHelper.ValidatorStatus) public validatorState; 23 | 24 | /// @notice Prove the validatorData corresponds to the validator root at validatorIndex 25 | function proveValidatorRootFromFields( 26 | BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo, 27 | BeaconOracleHelper.ValidatorProofInfo calldata _validatorProofInfo, 28 | BeaconOracleHelper.Validator calldata validatorData, 29 | bytes32[] calldata _balanceProof, 30 | bytes32 _combinedBalance 31 | ) external { 32 | bytes32 blockHeaderRoot = ILightClient(lightclient).headers(_beaconStateRootProofInfo.slot); 33 | 34 | BeaconOracleHelper.verifyValidatorRoot( 35 | _beaconStateRootProofInfo, _validatorProofInfo, blockHeaderRoot 36 | ); 37 | 38 | BeaconOracleHelper.verifyCompleteValidatorStruct( 39 | _validatorProofInfo.validatorRoot, _validatorProofInfo.validatorIndex, validatorData 40 | ); 41 | 42 | uint256 balance = BeaconOracleHelper.proveValidatorBalance( 43 | _validatorProofInfo.validatorIndex, 44 | _beaconStateRootProofInfo.beaconStateRoot, 45 | _combinedBalance, 46 | _balanceProof 47 | ); 48 | 49 | validatorState[_validatorProofInfo.validatorIndex] = 50 | BeaconOracleHelper.ValidatorStatus(validatorData, balance, true); 51 | 52 | emit BeaconOracleUpdate(_validatorProofInfo.validatorIndex); 53 | } 54 | 55 | function getValidator(uint256 _validatorIndex) 56 | external 57 | view 58 | returns (BeaconOracleHelper.ValidatorStatus memory) 59 | { 60 | return validatorState[_validatorIndex]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof-type2.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "16", 4 | "logIndex": 0, 5 | "messageRoot": "0xb082aa08332937b8d6e577e14b8166e9adb846446d6c42a1ed9df836524d7ac4", 6 | "proof": [ 7 | "f871a08259e02495c503b65f674a383de00ca646131c4339c8cc3a7412c6f5cd6d8532a01d6f435328b1481498eb7b296347f07f2686b4c60912589c145b379811aed6a2808080808080a0e58215be848c1293dd381210359d84485553000a82b67410406d183b42adbbdd8080808080808080", 8 | "f90191a0841e41b77af537e63ff40016a9a73dd12ee3d29cb353c7077e5470ea857bb00ea0623c4a442183dfd6b448d957b0c1e9c4f66bda2cddac9c7d64cc040594f01ab2a0e577cbb2ccb8db88832069c284360ab707ae45dbf0e6a34caa1062bea7cc0b45a057ef80ef7b7e8f6423ac3668df3dc3707aeb426d38779baeee219df1c1a40c68a0eb39ad5c27c4b81a23ae5adb98187163739e20f1dd0525108a86d85873dcb141a0a16d6fe4c9696837c952828464cb5322aac6adfd46eb11be5bbc8c41334a881da0401767ce1e3ad76d31cf806db681c7302906c67ecf402ab19f5c591a66834521a03f3e5bb784c1fa405b41d1546fafdd887432b0f037abee593285df0a4ccec693a0742128ddc32a12912619a6ac0d55c3e400a9367976948c00c04ab77266498beca03e8a2bd5d60932fa23c56c814f51e3896125edead610ef38fd9a9ba4830ce849a0529365af4dee45aa844c1b1b5b435ebb40c2bddc748a11c37c15cd38cc21ea3aa0da8fc8fdef6ecc914e5e84fb36e353b12be714663b624f869daa4bdb3aafb56e8080808080", 9 | "f9027220b9026e02f9026a0183215342b9010002000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000002000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000065a0b082aa08332937b8d6e577e14b8166e9adb846446d6c42a1ed9df836524d7ac4b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000006500000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x9ff8106d4d1f5f2620985ff573c98daecbeccdb121d8a787a2cd16bf707a8ab9" 12 | } -------------------------------------------------------------------------------- /external/examples/oracle/NFTAirdrop.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 4 | import {TelepathyOracle} from "src/oracle/TelepathyOracle.sol"; 5 | import {IOracleCallbackReceiver} from "src/oracle/interfaces/IOracleCallbackReceiver.sol"; 6 | 7 | struct Claim { 8 | address sender; 9 | uint256 tokenId; 10 | } 11 | 12 | abstract contract NFTAirdrop is IOracleCallbackReceiver { 13 | event AirdropClaimed(address indexed sender, uint256 indexed tokenId); 14 | 15 | error NotFromOracle(address srcAddress); 16 | error NotOwnerOfToken(address owner, uint256 tokenId); 17 | error AlreadyClaimed(uint256 tokenId); 18 | error OracleQueryFailed(); 19 | 20 | address sourceNft; 21 | TelepathyOracle oracle; 22 | 23 | /// @notice Maps oracle nonce to the address that requested the claim 24 | mapping(uint256 => Claim) public claimRequests; 25 | /// @notice Maps token IDs to whether they have been claimed 26 | mapping(uint256 => bool) public claimed; 27 | 28 | constructor(address _sourceNft, address _oracle) { 29 | sourceNft = _sourceNft; 30 | oracle = TelepathyOracle(_oracle); 31 | } 32 | 33 | function claimAirdrop(uint256 _tokenId) external { 34 | if (claimed[_tokenId]) { 35 | revert AlreadyClaimed(_tokenId); 36 | } 37 | uint256 nonce = oracle.requestCrossChain( 38 | address(sourceNft), 39 | abi.encodeWithSelector(IERC721.ownerOf.selector, _tokenId), 40 | address(this) 41 | ); 42 | claimRequests[nonce] = Claim(msg.sender, _tokenId); 43 | } 44 | 45 | function handleOracleResponse(uint256 _nonce, bytes memory _responseData, bool _responseSuccess) 46 | external 47 | override 48 | { 49 | if (msg.sender != address(oracle)) { 50 | revert NotFromOracle(msg.sender); 51 | } 52 | if (!_responseSuccess) { 53 | revert OracleQueryFailed(); 54 | } 55 | address owner = abi.decode(_responseData, (address)); 56 | Claim storage claim = claimRequests[_nonce]; 57 | if (claimed[claim.tokenId]) { 58 | revert AlreadyClaimed(claim.tokenId); 59 | } 60 | if (owner != claim.sender) { 61 | revert NotOwnerOfToken(claim.sender, claim.tokenId); 62 | } 63 | delete claimRequests[_nonce]; 64 | claimed[claim.tokenId] = true; 65 | emit AirdropClaimed(owner, claim.tokenId); 66 | _giveAirdrop(owner, claim.tokenId); 67 | } 68 | 69 | function _giveAirdrop(address _owner, uint256 _tokenId) internal virtual; 70 | } 71 | -------------------------------------------------------------------------------- /src/pubsub/TelepathySubscriber.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {Subscription, SubscriptionStatus, ISubscriber} from "src/pubsub/interfaces/IPubSub.sol"; 4 | 5 | import {PubSubStorage} from "src/pubsub/PubSubStorage.sol"; 6 | 7 | /// @title TelepathySubscriber 8 | /// @author Succinct Labs 9 | /// @notice This allows contracts to subscribe to cross-chain events from a source contract. 10 | contract TelepathySubscriber is ISubscriber, PubSubStorage { 11 | error SubscriptionAlreadyActive(bytes32 subscriptionId); 12 | error SubscriptionNotActive(bytes32 subscriptionId); 13 | error InvalidSlotRange(uint64 startSlot, uint64 endSlot); 14 | 15 | /// @dev The block ranges use as a signal to off-chain, and are NOT enforced by the publisher. 16 | /// If events should only a certain range should be valid, the callbackAddress should do their 17 | /// own validation when handling the publish. 18 | function subscribe( 19 | uint32 _sourceChainId, 20 | address _sourceAddress, 21 | address _callbackAddress, 22 | bytes32 _eventSig, 23 | uint64 _startSlot, 24 | uint64 _endSlot 25 | ) external returns (bytes32) { 26 | Subscription memory subscription = 27 | Subscription(_sourceChainId, _sourceAddress, _callbackAddress, _eventSig); 28 | bytes32 subscriptionId = keccak256(abi.encode(subscription)); 29 | 30 | if (subscriptions[subscriptionId] == SubscriptionStatus.SUBSCRIBED) { 31 | revert SubscriptionAlreadyActive(subscriptionId); 32 | } 33 | subscriptions[subscriptionId] = SubscriptionStatus.SUBSCRIBED; 34 | 35 | // Either both block's slots are 0, or endSlot is must greater than startSlot. 36 | if (_endSlot < _startSlot) { 37 | revert InvalidSlotRange(_startSlot, _endSlot); 38 | } 39 | 40 | emit Subscribe(subscriptionId, _startSlot, _endSlot, subscription); 41 | 42 | return subscriptionId; 43 | } 44 | 45 | /// @dev Only the original callbackAddress contract will be able to unsubscribe. 46 | function unsubscribe(uint32 _sourceChainId, address _sourceAddress, bytes32 _eventSig) 47 | external 48 | returns (bytes32) 49 | { 50 | Subscription memory subscription = 51 | Subscription(_sourceChainId, _sourceAddress, msg.sender, _eventSig); 52 | bytes32 subscriptionId = keccak256(abi.encode(subscription)); 53 | 54 | if (subscriptions[subscriptionId] == SubscriptionStatus.UNSUBSCIBED) { 55 | revert SubscriptionNotActive(subscriptionId); 56 | } 57 | subscriptions[subscriptionId] = SubscriptionStatus.UNSUBSCIBED; 58 | 59 | emit Unsubscribe(subscriptionId, subscription); 60 | 61 | return subscriptionId; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/amb/Timelock.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "forge-std/Vm.sol"; 5 | import "forge-std/console.sol"; 6 | import "forge-std/Test.sol"; 7 | import {Timelock} from "src/libraries/Timelock.sol"; 8 | 9 | contract Simple { 10 | uint256 value; 11 | 12 | function setValue(uint256 _value) public { 13 | value = _value; 14 | } 15 | } 16 | 17 | struct Parameters { 18 | address target; 19 | uint256 value; 20 | bytes data; 21 | bytes32 predecessor; 22 | bytes32 salt; 23 | uint256 delay; 24 | } 25 | 26 | contract TimelockTest is Test { 27 | bytes32 SALT = 0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726; 28 | uint256 MIN_DELAY = 60 * 24 * 24; 29 | address bob = payable(address(uint160(uint256(keccak256(abi.encodePacked("bob")))))); 30 | Timelock timelock; 31 | 32 | event CallScheduled( 33 | bytes32 indexed id, 34 | uint256 indexed index, 35 | address target, 36 | uint256 value, 37 | bytes data, 38 | bytes32 predecessor, 39 | uint256 delay 40 | ); 41 | 42 | function setUp() public { 43 | address[] memory proposers = new address[](1); 44 | address[] memory executors = new address[](1); 45 | proposers[0] = bob; 46 | executors[0] = bob; 47 | 48 | vm.deal(bob, 100); 49 | 50 | timelock = new Timelock( 51 | MIN_DELAY, 52 | proposers, 53 | executors, 54 | address(0) 55 | ); 56 | } 57 | 58 | function test_Propose() public { 59 | Simple sample = new Simple(); 60 | 61 | Parameters memory parameters = Parameters( 62 | address(sample), 63 | 0, 64 | abi.encodeWithSelector(Simple.setValue.selector, 1), 65 | bytes32(0), 66 | SALT, 67 | MIN_DELAY 68 | ); 69 | 70 | bytes32 id = keccak256( 71 | abi.encode( 72 | parameters.target, 73 | parameters.value, 74 | parameters.data, 75 | parameters.predecessor, 76 | parameters.salt 77 | ) 78 | ); 79 | 80 | vm.expectEmit(true, true, false, true, address(timelock)); 81 | emit CallScheduled( 82 | id, 83 | 0, 84 | parameters.target, 85 | parameters.value, 86 | parameters.data, 87 | parameters.predecessor, 88 | parameters.delay 89 | ); 90 | 91 | vm.prank(bob); 92 | timelock.schedule( 93 | parameters.target, 94 | parameters.value, 95 | parameters.data, 96 | parameters.predecessor, 97 | parameters.salt, 98 | parameters.delay 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/amb-v2/Timelock.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "forge-std/Vm.sol"; 5 | import "forge-std/console.sol"; 6 | import "forge-std/Test.sol"; 7 | import {Timelock} from "src/libraries/Timelock.sol"; 8 | 9 | contract Simple { 10 | uint256 value; 11 | 12 | function setValue(uint256 _value) public { 13 | value = _value; 14 | } 15 | } 16 | 17 | struct Parameters { 18 | address target; 19 | uint256 value; 20 | bytes data; 21 | bytes32 predecessor; 22 | bytes32 salt; 23 | uint256 delay; 24 | } 25 | 26 | contract TimelockTest is Test { 27 | bytes32 SALT = 0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726; 28 | uint256 MIN_DELAY = 60 * 24 * 24; 29 | address bob = payable(address(uint160(uint256(keccak256(abi.encodePacked("bob")))))); 30 | Timelock timelock; 31 | 32 | event CallScheduled( 33 | bytes32 indexed id, 34 | uint256 indexed index, 35 | address target, 36 | uint256 value, 37 | bytes data, 38 | bytes32 predecessor, 39 | uint256 delay 40 | ); 41 | 42 | function setUp() public { 43 | address[] memory proposers = new address[](1); 44 | address[] memory executors = new address[](1); 45 | proposers[0] = bob; 46 | executors[0] = bob; 47 | 48 | vm.deal(bob, 100); 49 | 50 | timelock = new Timelock( 51 | MIN_DELAY, 52 | proposers, 53 | executors, 54 | address(0) 55 | ); 56 | } 57 | 58 | function test_Propose() public { 59 | Simple sample = new Simple(); 60 | 61 | Parameters memory parameters = Parameters( 62 | address(sample), 63 | 0, 64 | abi.encodeWithSelector(Simple.setValue.selector, 1), 65 | bytes32(0), 66 | SALT, 67 | MIN_DELAY 68 | ); 69 | 70 | bytes32 id = keccak256( 71 | abi.encode( 72 | parameters.target, 73 | parameters.value, 74 | parameters.data, 75 | parameters.predecessor, 76 | parameters.salt 77 | ) 78 | ); 79 | 80 | vm.expectEmit(true, true, false, true, address(timelock)); 81 | emit CallScheduled( 82 | id, 83 | 0, 84 | parameters.target, 85 | parameters.value, 86 | parameters.data, 87 | parameters.predecessor, 88 | parameters.delay 89 | ); 90 | 91 | vm.prank(bob); 92 | timelock.schedule( 93 | parameters.target, 94 | parameters.value, 95 | parameters.data, 96 | parameters.predecessor, 97 | parameters.salt, 98 | parameters.delay 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof22.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "08", 4 | "logIndex": 0, 5 | "messageRoot": "0x6e0909b5cbe16ecdf8e1facca676377d135de55ff589117840ec2d4f5b03dba0", 6 | "proof": [ 7 | "f8b1a0be2cf796ff3a2b06c586fbf1a2056b1aba30d2d9057a31ba0433e7c47cdb1c2aa037a25908599493b9d98b1cb0e61bccf0bd2be4c20d78bd4fdf0fed847f42b783a0c232ea25d2f78828ab76bddc7dd9a101651a7c8842fca1541b506611a2807c14a0bb5a15ca0ff5e342e8724e1e14e10247b1cf09db09fb7f0f86ded42bfca1ffa480808080a0137de2431838a53d5a47fe0a0a0f54748f705496181ca66157e3ac349bd640268080808080808080", 8 | "f901f180a0c905908ba1eace0300d38ba819bc37037c2c59d7bdc9fc592501b37731a8af30a0ac4302f3f60b0f37837c6dfc51b95c75a6d025cd192d8c40aec7ea65fe8348bfa058bba9a1bdd1f3bf19871ab3a4cb56b5a774615cfb4b3aade372e1a57e4c0d69a0f2d6cf8f7a4361fc5fa1560bb00dfff5fdc72279cfde6b0edc6157944b1252b4a01f7ce16ef699d8194efb22be73b064861f39097a88f196dfe78198f59859eca1a0459c5ccf987f0e7f24df9ec39ee8bc10dfb82b227d5634c245a227f32feb7d50a0b62a5c5c62fdcfe2f4033ae37df2d511254a1ba31a5437f804ad776f35a41603a0052bd4fb22704d498caf9383595e817b4843f8327bc8fa55f4e889e10bbae532a0978f6ed371e0388759998338cf4c159fdb6e016b41857bffc9089da12c1d9fefa09ca0ad29ba4bc2d367d43cb294b325c5ac8370ae71e6effc94e42a0d0dbf8f7ea0f9c97ec52bfe7c112dd251b3bdc9041f6a26909556af55897624e3623be18c55a0280a947ae453d38311140e8498173557adc0e8a8cad04226828dcbdbd1099426a0e43b7eca5bc8a81bda808edd86917b2142306f58ea7b0e4bfa773820493f8631a003db84911ddf63696337401068d1484e924c990da229bb41b5c79815e38869b1a074c4443d5b234a36969237cc062224544cf0bd0b653ed1ed3d350a463213759480", 9 | "f9027220b9026e02f9026a01830c39dcb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000022000000000000800000000000000000000000000000000002000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000008000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000016a06e0909b5cbe16ecdf8e1facca676377d135de55ff589117840ec2d4f5b03dba0b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001600000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x07027e85131341f6c0b5071fb5e53c5e30872c370204842587ea3a9e08085140" 12 | } -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof-type0.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "1f", 4 | "logIndex": 0, 5 | "messageRoot": "0xc06a3542fde0ffa1637d62dfb7b7996d5e65af9ecf6555ce502e4e8967ca010e", 6 | "proof": [ 7 | "f891a06f7076d10f6ace04af9f0a067e42dd6ea17ada9bc77d7b4e15c1372cf52880eca069d499e254c8beb3eb4be64178187cb5db9b2ecd82fe5fbcf158e7380da553ada08f57db042fc46d19e373f856eb1fadef8806b75321b6f2e50de3e5a7e13384b98080808080a0e58215be848c1293dd381210359d84485553000a82b67410406d183b42adbbdd8080808080808080", 8 | "f90211a065504787953e29af29f90669d4ed96a82d8f695cb2d2867d1aa2149bdca2cf10a0b3e951ff103ce66625087baad736b7752166b2169d160dcbaea2b4a45e9391dfa06819740347d0f27bebe3b6c0c0adc921de14f266dfff8a9e2542ac2c5d8613c9a018987f058cc09400c58f27729fa663505b2a6fbd198195a77c4b7771dbc157dea0c0455b3e34d99d1d17e885987c8ece225ca0c376575482238716f0b9351191afa00da34fa18a5e07915aa885389d52ec5eeed38f3f4e9c8bcba7facd3d41c7f3b9a0429c0aaa1b5cb508ecfac42d32898669b0fbaa4030d27fab2eed2f51752d3672a0ff6e772810e056babcc77b631dd78dfdd0c3533d76b7986f35a3bc20edc11569a0caf4c0c1f79b65a948b264b606256b9e5827b9b67ac8298d9fe31be1a303f2baa0eb7d23db4cac9fbbf57b243bd0ad36240e91c85c572464758895c88c40d91f3da0acd5db7ebe870550de9b225b8fdf9f2e10ec782f05e4de53cd90cf4baf163a1fa0506bbf49b1b0d50837be9bb0e1a4a4f2fd459c1dc7548bc217a792ed1ab5f268a02082ead9eaacf3b51ed78c5d118e3f240bb41c78b90a76aa0e1e5ba22660a923a05b4b17e9c66f6613c23c9b87408cf36d4a2f69009c90348471c3186dcbc5503fa013355753e432d9de57c6871589622c65a05b6f2811de9f5762f25e5681cc269ba0084b284eb1385857da0905be0faf0b2e8483f57c979a963edaad931b977b7f6580", 9 | "f9027120b9026df9026a0183300a84b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000040080000020000000000000000000000000000000000000000000000002000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000010000000020000000001000000000000000000000000000000000000000000008000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000064a0c06a3542fde0ffa1637d62dfb7b7996d5e65af9ecf6555ce502e4e8967ca010eb8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000006400000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0xb7edd4e2c1f0689e13a33e61e33fdeee3eb92fdc121ce3805c29bc7d5509f5c2" 12 | } 13 | -------------------------------------------------------------------------------- /test/lightclient/fixtures/slot6000991.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1606824023", 4 | "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 732, 8 | "syncCommitteePoseidon": "4813308915827648623878451314018279762646764441782177839668061462095865237660" 9 | }, 10 | "step": { 11 | "attestedSlot": 6000991, 12 | "finalizedSlot": 6000896, 13 | "participation": "509", 14 | "finalizedHeaderRoot": "0xae812152ba91922b476d3afa0b26b918be01690e84696fbcf3023b4bed3396f0", 15 | "executionStateRoot": "0x6f6260a793fd323a890e5cbe5fff9d664f5732968eb05c562fd5cf7c69a7b038", 16 | "a": [ 17 | "3676139238122208934671756152026087393011655100877803027533583278695190541966", 18 | "11836508189069845910598347201582859578444600058447949755897847797146987690736" 19 | ], 20 | "b": [ 21 | [ 22 | "7943892160160627360854359757850937321286888197399736629189202983135108799342", 23 | "17958348333706329801211415473006536558326045075949802622097123050459446907791" 24 | ], 25 | [ 26 | "3017179222538112659560076837335966232966937592302119468383996429058193371338", 27 | "10851545949716579406228208562953309386126454997951555170033418150347565366018" 28 | ] 29 | ], 30 | "c": [ 31 | "3826415547934241255592040301787719243782237042565706406629842625547832795852", 32 | "9662121948388664455804908316240980693020746950311336925446354575356265754019" 33 | ], 34 | "inputs": [ 35 | "10517422026590256917855005281854198065699053101035254455462099250146078836236" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "14825565096227403931419065561746726231701803856816263031095204302560742856234", 41 | "742119048057379490874842089131411287694989036403830695206432124064802743649" 42 | ], 43 | "b": [ 44 | [ 45 | "789981713043537691350945744385327448732729782355589712034570844818671099558", 46 | "4182889003716876298862682424645226702620978547320291953332591742435441210003" 47 | ], 48 | [ 49 | "19859879154754607859406095164824887234483033076265539930748627617970234775567", 50 | "14896233670588873796655363281700626999450565590120038991614682054177622688550" 51 | ] 52 | ], 53 | "c": [ 54 | "12663891381410475532087292508795022863376581502329093397283883053173705959921", 55 | "19101979499311804437238410857262283698968055481532886661430317174032027162947" 56 | ], 57 | "syncCommitteeSSZ": "0x403da661a6ac02fdd553379b48f8c4e0811e9d3761dbbbced7abffea1d6abc0b", 58 | "syncCommitteePoseidon": "6354759057798774408477943797544841426475282145456654929143417471023041374233" 59 | } 60 | } -------------------------------------------------------------------------------- /test/lightclient/fixtures/slot6001055.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1606824023", 4 | "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 732, 8 | "syncCommitteePoseidon": "4813308915827648623878451314018279762646764441782177839668061462095865237660" 9 | }, 10 | "step": { 11 | "attestedSlot": 6001055, 12 | "finalizedSlot": 6000960, 13 | "participation": "507", 14 | "finalizedHeaderRoot": "0x023469c31f62a6299ea211edcdda7d97959478f534c4675f94445f97835454c0", 15 | "executionStateRoot": "0x9f040f1c59aba7a57c4607cd4b94789d9b6bb264fb8e7c32d3677b17a2379edd", 16 | "a": [ 17 | "3297102136089603800455794964452311605380759306660149816368919518846038899774", 18 | "16103938392715800944871206401262753440896773134296655636350456955608937928179" 19 | ], 20 | "b": [ 21 | [ 22 | "10900832678735521796910061268867336829159009665397271226627212892926942226415", 23 | "11017281057778169532559909362405443377216095885613197013775881951721652130641" 24 | ], 25 | [ 26 | "11793479256711224473966162448002533064967575949795214166698132507173235579059", 27 | "570112375568534860417775680608080324807340969650880254505982770565359418308" 28 | ] 29 | ], 30 | "c": [ 31 | "19425579063761650535185270558891700841955386323492641605695701851166936283215", 32 | "5392019164151297055837568316913218406630156226510175556265422776852826346141" 33 | ], 34 | "inputs": [ 35 | "11587387096607643938697032436714368829377861660456912848673302962432697747244" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "12120158738583097667283936889968121648034010479732313683007868438305172469954", 41 | "1096505113290085113188423424094255492703914145204600985669405265796432514524" 42 | ], 43 | "b": [ 44 | [ 45 | "2098145636649445268919957257327492862023377961249615338077916510507853484984", 46 | "4203962684387247876849247326815164470718251840303133050714931144295364379328" 47 | ], 48 | [ 49 | "16430106686501670825112996232053466557688220674100532612959221085064893097077", 50 | "7099709378880119855493092808912071733574064395387128324227469950881503269428" 51 | ] 52 | ], 53 | "c": [ 54 | "13207434676807772757961482219757907191517691213435840102746022205677254858057", 55 | "14229020319825771133998139885069174855087561139057454878744158794897394008651" 56 | ], 57 | "syncCommitteeSSZ": "0x403da661a6ac02fdd553379b48f8c4e0811e9d3761dbbbced7abffea1d6abc0b", 58 | "syncCommitteePoseidon": "6354759057798774408477943797544841426475282145456654929143417471023041374233" 59 | } 60 | } -------------------------------------------------------------------------------- /test/lightclient/fixtures/slot6001087.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1606824023", 4 | "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 732, 8 | "syncCommitteePoseidon": "4813308915827648623878451314018279762646764441782177839668061462095865237660" 9 | }, 10 | "step": { 11 | "attestedSlot": 6001087, 12 | "finalizedSlot": 6000992, 13 | "participation": "508", 14 | "finalizedHeaderRoot": "0xdcd89299f2b5b3499f792c9b440118817b8cb711194340dff0db76e3c60109c6", 15 | "executionStateRoot": "0x0434dfb3ba11da3a043e768857e638fc9078d3c896bee975f069b5a05fbd72b5", 16 | "a": [ 17 | "21733221182584036665213975831501047278904699693662743399955423510324014099339", 18 | "11782636463909931279439090933887605837349495350325312572140935436993667369583" 19 | ], 20 | "b": [ 21 | [ 22 | "20530998580835508327309786743964141589193656698498774687418944955276180478435", 23 | "8070551007288813929962160662296364498415847068367006531979535834213043437245" 24 | ], 25 | [ 26 | "14149143413920679319514119885675311629577411588620179281391200739576178365073", 27 | "5605483398361368790683123655812382466353430933071596537005717750173361474578" 28 | ] 29 | ], 30 | "c": [ 31 | "592982412917449664561349571084558017674572068915763196005053710787436334097", 32 | "21151398297131925506337269169489336204685552415440567154265663975515841599441" 33 | ], 34 | "inputs": [ 35 | "9141550529025973292319140032625768225290930186037007367190775073631248606070" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "14774734938009796155476886825594708457045379599137110927821936560350295772743", 41 | "5388146987124104862420482406128294686406349717313157999564274335183623565234" 42 | ], 43 | "b": [ 44 | [ 45 | "9976400920836509141124970337820717777989892134562967677988691755953579294702", 46 | "12914679374609082903197159650906068315689501857239935841699781441019986188746" 47 | ], 48 | [ 49 | "7763313535306439296946563613492241120766780455528328568140511519312608193006", 50 | "21050222363470917146752062142454022594747816010629407899152387743324795097298" 51 | ] 52 | ], 53 | "c": [ 54 | "15569021443997961551547545205853992139177732820659664146141137080777149108418", 55 | "21507788302196301228583087460881608910419323852198670550676493182757185866454" 56 | ], 57 | "syncCommitteeSSZ": "0x403da661a6ac02fdd553379b48f8c4e0811e9d3761dbbbced7abffea1d6abc0b", 58 | "syncCommitteePoseidon": "6354759057798774408477943797544841426475282145456654929143417471023041374233" 59 | } 60 | } -------------------------------------------------------------------------------- /test/lightclient/fixtures/periodBoundaryEarlySlot.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1616508000", 4 | "genesisValidatorsRoot": "0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 615, 8 | "syncCommitteePoseidon": "2170105493911194132166049820797109901979535733649423633796001548504069659524" 9 | }, 10 | "step": { 11 | "attestedSlot": 5038081, 12 | "finalizedSlot": 5038016, 13 | "participation": "386", 14 | "finalizedHeaderRoot": "0xb74b2001ae866af7e579608749c2e13208d08f3bad1b222c87763692f2803419", 15 | "executionStateRoot": "0x49be509f85d3793ce2cff2fbff5a1ed6b7cee0af0d72f7ea3fe72fcd92815d30", 16 | "a": [ 17 | "5700452719463320939379469169081302662604647201116368676984088284623996229609", 18 | "9002965093483241603599474811911119303141456809952042117262075829150901046549" 19 | ], 20 | "b": [ 21 | [ 22 | "12297374036593722548706248130583817967108059218336084923843922542558428206443", 23 | "2683627131793679872515909219956680337887375328207427088053912229117403824325" 24 | ], 25 | [ 26 | "13027965577597010777029354784157487228335195394026742102346762747221039712505", 27 | "19020980273425463682263699768217148922025145491888316279822386852165281594820" 28 | ] 29 | ], 30 | "c": [ 31 | "4337103017821066723055233421684961886928338263575964669547775534781467892480", 32 | "6897833398389830746516450566689248516399637782247391469161883612985061528802" 33 | ], 34 | "inputs": [ 35 | "1174491062440148485403224851680402213435776169997219625383106834783899121541" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "9363407468164545935933536321552001053835375070949951823370992998801262818924", 41 | "17581202581868850988565833587164525972133064037299886310474505763095381926827" 42 | ], 43 | "b": [ 44 | [ 45 | "2755805688039420134966659216879673776312531949383948144910744510474813505199", 46 | "6627145305418769072287163116299890763528036871442702459594298225220724258288" 47 | ], 48 | [ 49 | "9150810850023643952439388782353082256422598202076200493287043255595983972757", 50 | "17278269179210323638497450215896949518569854454266097357205528742851812398268" 51 | ] 52 | ], 53 | "c": [ 54 | "9237630358029717356303527105507924204968473826679866258539299942952491976807", 55 | "21568484419664196505783142693969842653127730408649611129312692083965544141634" 56 | ], 57 | "syncCommitteeSSZ": "0x5aa745866b3790aa8c6720db777e41dfdb436e854f30bf43e5d43c7d01ae25f7", 58 | "syncCommitteePoseidon": "2170105493911194132166049820797109901979535733649423633796001548504069659524" 59 | } 60 | } -------------------------------------------------------------------------------- /test/lightclient/fixtures/periodBoundaryLateSlot.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1616508000", 4 | "genesisValidatorsRoot": "0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 614, 8 | "syncCommitteePoseidon": "2170105493911194132166049820797109901979535733649423633796001548504069659524" 9 | }, 10 | "step": { 11 | "attestedSlot": 5038079, 12 | "finalizedSlot": 5037983, 13 | "participation": "370", 14 | "finalizedHeaderRoot": "0x6ad64add668f58ace0c70a24cb5e21a3c57993ad4aa07c8e6fde6e19b1107302", 15 | "executionStateRoot": "0x0c63d0782eb373ed777e8367f505e62805dc4612fd40d6c0962a529cc040dd7a", 16 | "a": [ 17 | "9379304532892822578778734625568466809761721492442764542201586509285507838290", 18 | "13037377676536099196488833154443810808699509991336459091518536231594155485545" 19 | ], 20 | "b": [ 21 | [ 22 | "15166391610161459995154722889018215874938794399727775503572554360375838863027", 23 | "10245656146195985665518873164708433588770537350288590995072247078762842388537" 24 | ], 25 | [ 26 | "21698333472255328928274819661974925867990694803965297197570349752093380194993", 27 | "12690766876116466741153464870617082659079209591365541535655114912694265642349" 28 | ] 29 | ], 30 | "c": [ 31 | "873131421668777533879956696430739144975621344463151634280665872199446954030", 32 | "10624653158884432175000018609189192145919559328039195620489215520019620956258" 33 | ], 34 | "inputs": [ 35 | "2441067844648397328304923956032172495105413006278921216616076703755146890127" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "17825251970261048181589332189077940825971922608033067022683155832352208867585", 41 | "15929708558829220730474123127297284714201458267163995845060596235330456693668" 42 | ], 43 | "b": [ 44 | [ 45 | "3988264827152122970153947645928667677816272275194716862482572999311607359545", 46 | "20806894101846450209088488047044403118716557762063763838469142249550814699856" 47 | ], 48 | [ 49 | "14340593788911072245102691585191859440957386679592100341272505353249261455463", 50 | "7805345110864658204699828830688715440716563825197402925612307793453386921236" 51 | ] 52 | ], 53 | "c": [ 54 | "14755302803537520492658615347399857084137958742627075335653774817282513583687", 55 | "343532102643037463392409627179859493303598373810847194326916083803480399991" 56 | ], 57 | "syncCommitteeSSZ": "0x5aa745866b3790aa8c6720db777e41dfdb436e854f30bf43e5d43c7d01ae25f7", 58 | "syncCommitteePoseidon": "2170105493911194132166049820797109901979535733649423633796001548504069659524" 59 | } 60 | } -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof-type1.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "38", 4 | "logIndex": 0, 5 | "messageRoot": "0x9e942d87176fe6a6a32b055107934ed54c567c769960ddb7b3070782db79fda3", 6 | "proof": [ 7 | "f8d1a051a784e3543ffecac27e4861512b5c60200abd1e108deff8119b2f08322e5bfda0464c67f75cb746594288403df50de4e70fe48bf8fba3f43e9f68bc8781f326f5a044ad05b30e3f0b561764dd6b7b056d31368c505e6f878b0e2489bb559df3fe41a0070ff21f35b4349eca02254509d5a13f88af44114193ff434009a8a1e9eecc06a0910ec4340e13af1d7357457e3c30fbe458e1911b2e2cb1b465c4e55c1a632eed808080a0e58215be848c1293dd381210359d84485553000a82b67410406d183b42adbbdd8080808080808080", 8 | "f90211a0eefd45402e89fbd5c52906c6972687bffea7438499e3cfce9b9d1d92aad7c9eaa0a70aff1abe0ecbeebaff54dac9b3a12534b9e5d2d03087b12baad9805b09aaaaa03a2d7e19eadee43817a5b864243e9f4e508434ee4c931e309d267635fd700d79a024581322ebcf6ef077c927cdabce86ca55bfad53003f0cfcb141420c248670bda004d52eff19e316abc3105c0c51e29500fb6ea184c1442b61eb361725d60b387fa0bac6b3d5cbdacc991ad9419b1678244a99033d41657145ac5ae9b2c153cf1843a07a09a2a68aa6820c5893027669f3eda5704a2b1243a8215b39d9911f37ea096ba0544655bc269c92a8614184eadaad0e53e3b63159d66c4679efa7c477af3e4bf5a09e63874cd2d62280cf24e79b2d38afb0949192f7e55b9f6bfb83ea7c0caf90c8a06f82c249ec3dc71ac73c47df49af02d51e38bd979a0cc83086e6b039a6bd2c40a03c5885119c22bc7cc866205b9ed7a7db1654db57db78acd2f0aaad5ee2093f85a0253a043ebcbd25d5b9c7d78ae8d982d1a432321f657cdfd12df694da497531e6a0f1cfa1d3825954383dbf20cd558e181cc24dc7a6e308edceef9b8cf514f87141a03b6ab10d84ff4c01739a20ddea376330e080dadc85a0c1d72d7f1139581bbc2da0f51430866c3f7325dc09562d4f831ba1e483a6718cc6238cfa732c01e0bf84a4a0c682df5d4a99b29cad66a1efc6379c9960b647babe52b5f8e9007ddc460bd01b80", 9 | "f9027320b9026f01f9026b018401264e27b9010000000000000000000000000000000000000000000000000020000000000000000400000000000000000000000000000000000000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000800000800000000000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000002000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000063a09e942d87176fe6a6a32b055107934ed54c567c769960ddb7b3070782db79fda3b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000006300000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x81f4fc3ef2cf6e49d2742bfb5321385c745335255683d1c60b6611124bfae085" 12 | } 13 | -------------------------------------------------------------------------------- /external/integrations/eigenlayer/EigenLayerBeaconOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 4 | import {SSZ} from "src/libraries/SimpleSerialize.sol"; 5 | import {BeaconOracleHelper} from "external/integrations/libraries/BeaconOracleHelper.sol"; 6 | import {EigenLayerBeaconOracleStorage} from 7 | "external/integrations/eigenlayer/EigenLayerBeaconOracleStorage.sol"; 8 | import {ReentrancyGuardUpgradeable} from 9 | "@openzeppelin-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol"; 10 | 11 | contract EigenLayerBeaconOracle is EigenLayerBeaconOracleStorage { 12 | event EigenLayerBeaconOracleUpdate(uint256 slot, uint256 timestamp, bytes32 blockRoot); 13 | 14 | error InvalidUpdater(address updater); 15 | error SlotNumberTooLow(); 16 | 17 | modifier onlyWhitelistedUpdater() { 18 | if (!whitelistedOracleUpdaters[msg.sender]) { 19 | revert InvalidUpdater(msg.sender); 20 | } 21 | _; 22 | } 23 | 24 | /// @notice Get beacon block root for the given timestamp 25 | function getBeaconBlockRoot(uint256 _timestamp) external view returns (bytes32) { 26 | return timestampToBlockRoot[_timestamp]; 27 | } 28 | 29 | /// @notice Fulfill request for a given timestamp (associated with a block) 30 | function fulfillRequest( 31 | BeaconOracleHelper.BeaconStateRootProofInfo calldata _sourceBeaconStateRootProofInfo, 32 | BeaconOracleHelper.TargetBeaconBlockRootProofInfo calldata _targetBeaconBlockRootProofInfo, 33 | bytes32[] calldata _targetTimestampProof, 34 | uint256 _targetTimestamp 35 | ) external onlyWhitelistedUpdater { 36 | // Get the source beacon block header root. 37 | bytes32 sourceHeaderRoot = ILightClient(lightclient).headers( 38 | _sourceBeaconStateRootProofInfo.slot 39 | ); 40 | 41 | // Extract the source beacon state root. 42 | BeaconOracleHelper.verifyBeaconStateRoot( 43 | _sourceBeaconStateRootProofInfo, 44 | sourceHeaderRoot 45 | ); 46 | 47 | // Verify the target beacon block root. 48 | bytes32 sourceBeaconStateRoot = _sourceBeaconStateRootProofInfo.beaconStateRoot; 49 | BeaconOracleHelper.verifyTargetBeaconBlockRoot( 50 | _targetBeaconBlockRootProofInfo, 51 | sourceBeaconStateRoot 52 | ); 53 | 54 | // Verify timestamp against target block header root. 55 | bytes32 targetBeaconBlockRoot = _targetBeaconBlockRootProofInfo.targetBeaconBlockRoot; 56 | BeaconOracleHelper.verifyTimestamp( 57 | _targetTimestamp, 58 | _targetTimestampProof, 59 | targetBeaconBlockRoot 60 | ); 61 | 62 | // Store the target block header root 63 | timestampToBlockRoot[_targetTimestamp] = targetBeaconBlockRoot; 64 | 65 | // Emit the event. 66 | emit EigenLayerBeaconOracleUpdate( 67 | _targetBeaconBlockRootProofInfo.targetSlot, 68 | _targetTimestamp, 69 | targetBeaconBlockRoot 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof19.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "11", 4 | "logIndex": 0, 5 | "messageRoot": "0xee82e8f1d871807b7187403b1aec0a96fbc11bb19de9b2c68a74670fcc48a843", 6 | "proof": [ 7 | "f8f1a05542706ecb22f74e7b176a62b19499b170a4b6e59a703043f41a65888aad015fa0920b49cfbeff3a78ad86deaeb37488d1c71e445307ad1009029f4330783e48d6a00355c5a07c2874cde519e4d858271e0bd187cdbe0b3003506c3f314357e0a22ca05cba8d75ef8ade066e86cce0319da97c4e9fd5096b5cf0cfedd39708723e09e4a00217c6589a94011f127b24c7a9dd4d17f4ef0f48efd6c1b29458100c4338ea76a065e9c130905deef91e1b7cb91384f5deb9574f8efed83b4eeb2c973bd0d6c7638080a0b24d547750e1ec0003bc8d5bb8c999a78dc79c60782b8bb86cf2e182a406ca4c8080808080808080", 8 | "f90211a0a30f7121156cdb065f500beb79405996878c2f87d024decb3c06061b9db10a7ba08c5a21866057139312efd10f4a808e8e9007ef7f6167883c546b360f479015b4a0d0a1b895f31438c0bdd9098a96ecb8c63ff2197e7cc5568bd55668066c69004ea003a794ad1afe67c1db71819f4df83efdc8a986014f0aba0f84e380487e4eaca9a027b25dd0f4b8894f3af584d948af68e91dd79cc9f7a2c8ae7a3470e8760e22dfa0c3ad65039e3dec04d4a05fbcbcdf067a667c6d02a1ff2e76f417770a09f48ecfa0d9e8e630e3c1a7e91bafc3865b1df9fe9354e1dd6b3db25a25ea399a34180d02a0986ba755b1f2ef9f241d7a5bfe3511d4436e310f7e02cab4eec72ceadbd21e2ca0920e5eed9987e395f956de6bf08313a3fcb7486e3c7be7bf603d08a52162d733a00a6965f6af5b3ff3a77367dd8c608c30425848bc72f1c90b8d82b06d8b45375ba0cb7488334cf1af9cf4b531b623c25cf1623ccc37fe1c41f9e8543296eed0cfdfa09e1fc056c1ed03c3c86d8fce7c29e1bcfbee247a96b996eb47a375fd882be9dda0b1f09a557374abdea991687ebc7b1faefabef610057844a83f928a435be1d4d3a09192c22d5f864e86d528cd3961388cfeb19cab42c71992ce15a478053521991ea06b0da550f45a3e2823156e7c2d21cd34f0c74aa6c00eea07273c0a00a7736a77a0a22e6b737b42e2605b0f6857c40ad1a1f782c698ed29be1d9e4b34354ab5111880", 9 | "f9027220b9026e02f9026a01834f5a56b9010020000000000000000000000000800000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000020000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000013a0ee82e8f1d871807b7187403b1aec0a96fbc11bb19de9b2c68a74670fcc48a843b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001300000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0xdc251fa3de7f9bc1a7d929a087e94cc4ac56fabe30986649fcb7bffafb01df15" 12 | } -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof21.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "29", 4 | "logIndex": 0, 5 | "messageRoot": "0xa19f6d97bae7a59d4262969643b83f9c769a932f8ac2472cb6b047eadd6bffd3", 6 | "proof": [ 7 | "f8f1a05a12c91fa8e0b6ffaa7e358060a434a804c460a558c64c8cac739b5574c0fb3ca0ac43f6deba2e4e7de3bdb810b1e9657de6596eec48ba7434a344298a3eaea0ffa0ff4a914f5d7c37e41ca9af22a1b57667d1b93a2e7b3c9d571f6f4296243f975aa090e88932541b8bde1a4fc9026ef3fa2ca4c3653a692cae7697b8330359030bf5a08c5e253357b5d6327fb641552c66268879369c8d0b4c7ce4c913aae7e52ae1d8a01ba37702288266afac817efcc0092bccf1cd4ed32d12347d8d4b21922850e49c8080a00d39e4b4f3bd37be9d39240acfa574ee32f111254f26f5cd5fe61e6e416bb2d68080808080808080", 8 | "f90211a0b1df2af5342316e4523e2f0503e1143fdc444ca1a810b7cacd407ddaa1616176a048146f5064fe4bfb6bdbdc942ca5019452153e7147ef560bac31b53b7b08368fa0809e2ae8cd8ea2f4b04e4edec263f0572ae4b5961d71919da82d020319ad4574a0d675f6f762a2a7705880ca5340e320933e7b34081967ae9fde52b5e9f9711f55a07789e2f1d62337894b10ec9b9a6894a2c211acf8bac75838bd36cf8f23f041dda0c6ee1c83b0f940075566f012d19dc67a4ceb2056c9b1f2195bad1e46ae6a87eca03a4276e411a22bede0055d9b29b8d51bdefce5daa56eb2672044b295da32bb71a09533d16f0fa4e8e0f2abf04401ed1ca87b0a4da9edf81b2f6b7f603f7f00e354a0c79fabe94e09f48c86aaeda6b04450a62a490fc35c3a4c24cdb3dae0984c327da0199e23e997c1af157898214b631b3f18d5bea57d6e8f3532ced9e48b01eb484ba02b4fac48850c64345d10dcac72bf1c9ad3416b37fb3abe0e4063a8fe74333ec7a065f92f8014e4f31a2b36adb752e373014277b496123f5040e53ba515ea918f5ea039aca71d4b294863e66db30ffd7e441afc54d3523ffbe28235b181073cb32af8a06323d8eec8e3c21d82447ce5763bdbc8fa8f8eeb5cb082b623184ef16625a50fa026b6866e5ad31a388fe41cf3ea0b7a0d642fa18c65a5a3a3ae5dbf40df58b046a0fef8f5a6a45896ef72a1cd6af452805b24eb515dd61282a80a80b2dcebebb08c80", 9 | "f9027220b9026e02f9026a01834b8fb9b9010000000000000000400000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000040000000021000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000002000000000000000000000000000000000000000000000010000000020000002000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000015a0a19f6d97bae7a59d4262969643b83f9c769a932f8ac2472cb6b047eadd6bffd3b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001500000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x86bfb046f11a167c134efdd11ff8d3af90b5bd1c16f94a81961d4c7cdd23e7c0" 12 | } -------------------------------------------------------------------------------- /test/integrations/eigenlayer/fixtures/eigenlayer1.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceSlot": 6965728, 3 | "targetSlot": 6961632, 4 | "sourceHeaderRoot": "0x735779ac4552d6f48e4b47c58e1a8717399eb0448968913c3a61a4dd9c16175b", 5 | "sourceStateRootProof": [ 6 | "0xb92df0daf8b7fb233e4a0ec4cc396a811ad8eb11f3f85cda23cbc8f909f913ad", 7 | "0xe2938f19a26c1bfa820e83e8d1f7fb0253372cba44f6302d16e6939f55d1e23d", 8 | "0x05a7182b0b3a68fcd8c7f78b0277d28f4199e2e076859a57b2ff62248b1a61c2" 9 | ], 10 | "sourceStateRoot": "0xd512d6470b2a3717c2f07dcd5f28fbad729fc47e6986d11b47d5f259777a6b1b", 11 | "targetHeaderRootProof": [ 12 | "0x8bea9b2c16d3a116f5f8ec5a5876760c8832fabd08cd72c13defe1e8f8535f23", 13 | "0x86e3d568f98f133e617285285328068c252b1e25760b2fe42e7949058f1e333b", 14 | "0x47406b27d6150838c073c5d0649c0a53f2b2879a2dc8be4af91fa9855d44f9de", 15 | "0x5454efe929146e93f8d1807146776bd3ec115a5e01001cb8ecdb4aa2b28078cf", 16 | "0xe2abded5e81c13d64b8455991afe42a16df7a04dca7111c0ed3c1bfa34e44140", 17 | "0xfdb1ffc0cd8f46ecfe738a26c571d6e44118a7ed5eba40172e254b014cdc6813", 18 | "0x1162621f93fbc0ad639bc796bcbdbb6e4f673830dd1bf845275997ea75c04b4f", 19 | "0xbe733b4d8fdb00f5b9f8e9e9660d9a02b47c4155725924404c61b5c36e574ea2", 20 | "0x214fd916d3eefc846b55be28b669c9497484e855584145b333c3d69d30822b41", 21 | "0xc3c9dbf920b4605bf5dbf9eba2f21cdf36e246b5757bac6a20c5f0617f5fc9df", 22 | "0x9c0d7c95ec8e01b69e7be560f489a648d598e8999a3574aecbb50ec0e24e168e", 23 | "0x249d3e5b7dbb22bd5f18f6164c4c08e08182679c3bbd77b55070b914bcdf9b45", 24 | "0x7d319687a11024df52475740eb457b96a070017792a1a85b3e1ae11354857b19", 25 | "0xa630f0cb39632cc8f213db486bc54874f524ec34c41304c805d36eb171161d8a", 26 | "0xea22041498b74c2f1b14a16bd7df61b749db600604d39ee85aa702c5db4eaf82", 27 | "0x378b94d19720425858164f1211b13579d82855fcd21d1bc2cd93e9309f010bad", 28 | "0xa34b734d36dd53db4897cf4fcb1e2915fba9fb35190db5d87ed47e39cedb8105", 29 | "0xe9630763f9f600abc1d020f45d2fd717907d0126e0f3b8c8d8e3b5760c3482c3" 30 | ], 31 | "targetHeaderRoot": "0xb8921036e69eb9423fa8b60eda8d1b166f5d72fb25cdf206b56f71d06d16f4c5", 32 | "targetTimestampProof": [ 33 | "0x8485dd0000000000000000000000000000000000000000000000000000000000", 34 | "0xeef8845e73b8cd8d4761d7e9545e88aa228e2282e5d6831450be1afbc4508ae0", 35 | "0xa593c1ab162e7e2c9b9e5b382fe870e096814e5b816297dfad54eabebe630846", 36 | "0x10eea123973ea94bb04060ed8a76f6151a41d954cf7af42ed54c789891757f97", 37 | "0x2915bb39d33bacd50568c539ca3b7a1fd5c00371896dc9ea6b54c076d19ca38b", 38 | "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", 39 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 40 | "0xda197d976cd0d1526c60d4bbbda82594c96078d6e1caf2a46dd114312f0fed66", 41 | "0x0000000000000000000000000000000000000000000000000000000000000000", 42 | "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", 43 | "0xe24f696e3e7c887cf71393ed6a8e0c70fbc329d0d803830a2f608af3d9f95b57" 44 | ], 45 | "targetTimestamp": 1690363607 46 | } -------------------------------------------------------------------------------- /test/integrations/eigenlayer/fixtures/eigenlayer2.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceSlot": 6965856, 3 | "targetSlot": 6961760, 4 | "sourceHeaderRoot": "0x24ffd6df02ccc23ed5534b52907de238aa5ffe2e9cd9c147e43e9426d892a2a6", 5 | "sourceStateRootProof": [ 6 | "0x1a40c0e7c589909004f344b4449424176abe2da1c5aafcfe45cb8e10bf874cbd", 7 | "0xbb4ec80ead8ddba3164a44da1f0c3278db9e0fea187cbff51e8dae368618431c", 8 | "0xe12d57f8f6069dfbbdea9569806f6485033974efd2107b0cad089e93adaaee54" 9 | ], 10 | "sourceStateRoot": "0xbd561955a6c098eade20ffbdc9123031bbf84d1fd92d3577185bf1ad4f2d6d1d", 11 | "targetHeaderRootProof": [ 12 | "0x43c6d70aa60cced4b75f2c6bd76844e39fd0f651f457ac702b1b1fc0b0ed32ac", 13 | "0x662e4d8e17a6f4e5542cab5040b23b803bbc8f7ec0b386972cf73b27237a82fc", 14 | "0xfabb1757063704811bcd0d08a424f34ed7ea5fa36a88e09d92343a16d5222758", 15 | "0xa13118b6fd96e370a53479b1563b070de4f5ea7cb12adce1ef3a1522a3d88a01", 16 | "0x390ecea544d1a32bcd9856d9fdf6a75aa547111436bd0073fa1227ac65841475", 17 | "0x1c7c6ddbd9123d3cdfe4aae563de77d74c223ac85d9577a7e76f8bdedac38818", 18 | "0xd7562d4a0cc0310c0bc214195aa4080968de45450975f202b99c92707b1fe9b8", 19 | "0x26c6a2bc253080efd78a4a5f7dc8d617eeeb370e82c7f5cdbaa146c2f94eae4d", 20 | "0xbec8db9253efd440ec7626fe757d9e772a4b9a3e111ed19a0114c435e46a91f6", 21 | "0xc285062cb5c0c8b3501c7a0582f4a50703dfd70bcb7ae97292f1cd9eb0ec6289", 22 | "0x9c0d7c95ec8e01b69e7be560f489a648d598e8999a3574aecbb50ec0e24e168e", 23 | "0x249d3e5b7dbb22bd5f18f6164c4c08e08182679c3bbd77b55070b914bcdf9b45", 24 | "0x0bb19669f096d7ef298083ecd7e5843bd412a78c97423d1ece25cb1c1c37b279", 25 | "0x93f91f6cc9f2d427ea7068c2e0828eea4317c45fa577311a28e700d9d1eb4f96", 26 | "0xa65fba97653b668b628c5beebc2b54f22c90fb0ba58beda77ff70ce0ae014c77", 27 | "0xff7089fdebcc734ab4c15733f1f677468ccaf596c8195555560139dcf1c3dd60", 28 | "0x41b8795daef18638ec3c3bdb946a40c1a5388bfc79323da823630af32276ae3e", 29 | "0xa06b1a8b19b6d44bc8d247d54bb621b22868757c045e44ea6526c0970ae3c146" 30 | ], 31 | "targetHeaderRoot": "0xdfb5868489836b0ff4a3c139b1c81d5e15457e34fc65f83ec5846b0b8b6f5ca1", 32 | "targetTimestampProof": [ 33 | "0x7ee09d0000000000000000000000000000000000000000000000000000000000", 34 | "0x420ac164045162d6c96d0b0b9de1d835388b5a659383b8ed6a9b53fe342d298a", 35 | "0x9d20a2029c1a052be99489987833c2565823607c92bcdbd9ff54e5ff1f9560c1", 36 | "0x467f6000152419fef724732fd4c1215681f6d674bfbaaa59fb07247d8e115d4d", 37 | "0x7613a880d2f5a7e0446108a413ec071983923f3fc5f9b22da2e512dd53fdacff", 38 | "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", 39 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 40 | "0x43e6e897e5580e36c15466343d136b548d5f459edc134075ac3dfafba682c736", 41 | "0x0000000000000000000000000000000000000000000000000000000000000000", 42 | "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", 43 | "0x87fbedb8cbcee1fd7fcbdb65607c17d4d08fb9c01217a0ee384c77c0cad1ab32" 44 | ], 45 | "targetTimestamp": 1690365143 46 | } -------------------------------------------------------------------------------- /external/integrations/eigenlayer/EigenLayerBeaconOracleProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {UUPSUpgradeable} from "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; 4 | import {AccessControlUpgradeable} from 5 | "@openzeppelin-upgradeable/contracts/access/AccessControlUpgradeable.sol"; 6 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 7 | import {EigenLayerBeaconOracle} from "external/integrations/eigenlayer/EigenLayerBeaconOracle.sol"; 8 | 9 | /// @title EigenLayer Beacon Oracle Proxy 10 | /// @author Succinct Labs 11 | /// @notice Store a mapping of block numbers to beacon state roots. 12 | contract EigenLayerBeaconOracleProxy is 13 | EigenLayerBeaconOracle, 14 | UUPSUpgradeable, 15 | AccessControlUpgradeable 16 | { 17 | /// @notice A random constant used to identify addresses with the permission of a 'timelock'. 18 | bytes32 public constant TIMELOCK_ROLE = keccak256("TIMELOCK_ROLE"); 19 | 20 | /// @notice A random constant used to identify addresses with the permission of a 'guardian'. 21 | bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE"); 22 | 23 | /// @notice Invalid light client address error. 24 | error InvalidLightClientAddress(); 25 | 26 | modifier onlyTimelock() { 27 | require(hasRole(TIMELOCK_ROLE, msg.sender), "EigenLayerBeaconOracleProxy: not timelock"); 28 | _; 29 | } 30 | 31 | modifier onlyGuardian() { 32 | require(hasRole(GUARDIAN_ROLE, msg.sender), "EigenLayerBeaconOracleProxy: not guardian"); 33 | _; 34 | } 35 | 36 | /// @notice Prevents the implementation contract from being initialized outside of the 37 | /// upgradeable proxy. 38 | constructor() { 39 | _disableInitializers(); 40 | } 41 | 42 | /// @notice Initializes the contract and the parent contracts once. 43 | function initialize(address _lightClient, address _timelock, address _guardian, address _operator) 44 | external 45 | initializer 46 | { 47 | __AccessControl_init(); 48 | _grantRole(TIMELOCK_ROLE, _timelock); 49 | _grantRole(DEFAULT_ADMIN_ROLE, _timelock); 50 | _grantRole(GUARDIAN_ROLE, _guardian); 51 | __UUPSUpgradeable_init(); 52 | 53 | if (_lightClient == address(0)) { 54 | revert InvalidLightClientAddress(); 55 | } 56 | 57 | whitelistedOracleUpdaters[_operator] = true; 58 | lightclient = ILightClient(_lightClient); 59 | } 60 | 61 | /// @notice Updates the whitelist of addresses that can update the beacon state root. 62 | function updateWhitelist(address _oracleUpdater, bool _isWhitelisted) external onlyGuardian { 63 | whitelistedOracleUpdaters[_oracleUpdater] = _isWhitelisted; 64 | } 65 | 66 | /// @notice Update the light client contract. 67 | function updateLightClient(address _lightClient) external onlyTimelock { 68 | lightclient = ILightClient(_lightClient); 69 | } 70 | 71 | /// @notice Authorizes an upgrade for the implementation contract. 72 | function _authorizeUpgrade(address newImplementation) internal override onlyTimelock {} 73 | } 74 | -------------------------------------------------------------------------------- /test/integrations/diva/fixtures/diva_withdrawal_6358918.json: -------------------------------------------------------------------------------- 1 | { 2 | "amount": 11853210, 3 | "amountProof": [ 4 | "0x42be225e743e8f621b07b57f85e8f02a70763c4f000000000000000000000000", 5 | "0xbb1dfb519db92f316a34d11dc0d53e087f2f6791f56c052cb3843366299434b6", 6 | "0x114ce610d300f94bfd658c05647f8e3c4840b446777ede4649373708fb094467", 7 | "0x8f565a16e9da39ca7ce4c493560afd2af4b44e0b747bd112b153ebb956d4a430", 8 | "0x72e232556089e0f72fc4aad96881a77cb605c932a89cbcf787ad961a03000791", 9 | "0x3736d553fac02286264ff19011954a9f1131fbac75e050f0228bfa72c74164b9", 10 | "0x1000000000000000000000000000000000000000000000000000000000000000", 11 | "0x0000000000000000000000000000000000000000000000000000000000000000", 12 | "0xe65a9836fd50393247ac445d4da7aa8d05519af7c5f1f1298a65a2cef50891e7", 13 | "0x0a6d78072f6310d9cf46d477c7894b61c86a05bc9041cd61ed7954cdfc32e836", 14 | "0xe531792bafba81dde49fb24c81704c1da401dfb1975cee814cc9f0a4aff265f5", 15 | "0x4fdab46427aaedaf36c74c92809f7f860a5c15b5f07e7f0f6c04e0521c65a5b1", 16 | "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", 17 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 18 | "0x003dce957febb6fa28bdc5afb92bdb75b3f1d497f3eb70177abba492f15498d8", 19 | "0x0000000000000000000000000000000000000000000000000000000000000000", 20 | "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", 21 | "0x341f1429263e9581af9c5f35b74afc4f68453cf3d6bf31109a05c53b339753c5" 22 | ], 23 | "headerRoot": "0x45c6bf26d710f2915dbdbfeb684d8676b63cfa3cf94cb744a0d86ca1e0518723", 24 | "slot": 6358918, 25 | "validatorIndex": 136969, 26 | "validatorIndexProof": [ 27 | "0xb2c2230000000000000000000000000000000000000000000000000000000000", 28 | "0x2f276f08629a787e9a7d6df1e88cbfcac80042b74b409667e1fa90de80d17692", 29 | "0x114ce610d300f94bfd658c05647f8e3c4840b446777ede4649373708fb094467", 30 | "0x8f565a16e9da39ca7ce4c493560afd2af4b44e0b747bd112b153ebb956d4a430", 31 | "0x72e232556089e0f72fc4aad96881a77cb605c932a89cbcf787ad961a03000791", 32 | "0x3736d553fac02286264ff19011954a9f1131fbac75e050f0228bfa72c74164b9", 33 | "0x1000000000000000000000000000000000000000000000000000000000000000", 34 | "0x0000000000000000000000000000000000000000000000000000000000000000", 35 | "0xe65a9836fd50393247ac445d4da7aa8d05519af7c5f1f1298a65a2cef50891e7", 36 | "0x0a6d78072f6310d9cf46d477c7894b61c86a05bc9041cd61ed7954cdfc32e836", 37 | "0xe531792bafba81dde49fb24c81704c1da401dfb1975cee814cc9f0a4aff265f5", 38 | "0x4fdab46427aaedaf36c74c92809f7f860a5c15b5f07e7f0f6c04e0521c65a5b1", 39 | "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", 40 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 41 | "0x003dce957febb6fa28bdc5afb92bdb75b3f1d497f3eb70177abba492f15498d8", 42 | "0x0000000000000000000000000000000000000000000000000000000000000000", 43 | "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", 44 | "0x341f1429263e9581af9c5f35b74afc4f68453cf3d6bf31109a05c53b339753c5" 45 | ], 46 | "withdrawalIndex": 0 47 | } -------------------------------------------------------------------------------- /abi/LightClientMock.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "uint256", 8 | "name": "slot", 9 | "type": "uint256" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "bytes32", 14 | "name": "root", 15 | "type": "bytes32" 16 | } 17 | ], 18 | "name": "HeadUpdate", 19 | "type": "event" 20 | }, 21 | { 22 | "inputs": [], 23 | "name": "consistent", 24 | "outputs": [ 25 | { 26 | "internalType": "bool", 27 | "name": "", 28 | "type": "bool" 29 | } 30 | ], 31 | "stateMutability": "view", 32 | "type": "function" 33 | }, 34 | { 35 | "inputs": [ 36 | { 37 | "internalType": "uint256", 38 | "name": "", 39 | "type": "uint256" 40 | } 41 | ], 42 | "name": "executionStateRoots", 43 | "outputs": [ 44 | { 45 | "internalType": "bytes32", 46 | "name": "", 47 | "type": "bytes32" 48 | } 49 | ], 50 | "stateMutability": "view", 51 | "type": "function" 52 | }, 53 | { 54 | "inputs": [], 55 | "name": "head", 56 | "outputs": [ 57 | { 58 | "internalType": "uint256", 59 | "name": "", 60 | "type": "uint256" 61 | } 62 | ], 63 | "stateMutability": "view", 64 | "type": "function" 65 | }, 66 | { 67 | "inputs": [ 68 | { 69 | "internalType": "uint256", 70 | "name": "", 71 | "type": "uint256" 72 | } 73 | ], 74 | "name": "headers", 75 | "outputs": [ 76 | { 77 | "internalType": "bytes32", 78 | "name": "", 79 | "type": "bytes32" 80 | } 81 | ], 82 | "stateMutability": "view", 83 | "type": "function" 84 | }, 85 | { 86 | "inputs": [ 87 | { 88 | "internalType": "uint256", 89 | "name": "slot", 90 | "type": "uint256" 91 | }, 92 | { 93 | "internalType": "bytes32", 94 | "name": "executionRoot", 95 | "type": "bytes32" 96 | } 97 | ], 98 | "name": "setExecutionRoot", 99 | "outputs": [], 100 | "stateMutability": "nonpayable", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [ 105 | { 106 | "internalType": "uint256", 107 | "name": "slot", 108 | "type": "uint256" 109 | }, 110 | { 111 | "internalType": "bytes32", 112 | "name": "headerRoot", 113 | "type": "bytes32" 114 | } 115 | ], 116 | "name": "setHeader", 117 | "outputs": [], 118 | "stateMutability": "nonpayable", 119 | "type": "function" 120 | }, 121 | { 122 | "inputs": [ 123 | { 124 | "internalType": "uint256", 125 | "name": "", 126 | "type": "uint256" 127 | } 128 | ], 129 | "name": "timestamps", 130 | "outputs": [ 131 | { 132 | "internalType": "uint256", 133 | "name": "", 134 | "type": "uint256" 135 | } 136 | ], 137 | "stateMutability": "view", 138 | "type": "function" 139 | } 140 | ] -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof18.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "29", 4 | "logIndex": 0, 5 | "messageRoot": "0x1938484c1d9b3f4b88744b0ef47c85224ac468440b2156597add37d98d0e518e", 6 | "proof": [ 7 | "f90131a000aa8f1077f15c506ad22062239b0d98a45d50c3bc60b0587de97a74fffc48bba00da597b7e0a851be8ab12c3afd5239a8170b797ca9119985a7c596713b28a28fa01b0efdbfec50605eeef2cf2a5b1a362067a16a0e87a828951b5dbcee9d3d8274a071091e0c1eff3e12b8ae7357580f6deb7bba18a5f42beb0a313230698bf13427a0b9c2518ae9f97b34384e3ac71a7f26988741454b2d4552b95801048e8e5cb15da046f56697dac603ec374ed429e865f6b53917b3abeaf63f69c30366498df165f0a0a99628742b1ad5db3a14f5e6517112eeb8fc53bff5bd36001aa92ad4441faa9fa0c022f40fa803ff27f195eed4b64cec1ddeb4a9b61e051bcd59f0c693d6a3a6e2a0680446e9b8e54c7034c7c32dd32369353f73e4ff05ae2c4e8b0710ef020e54768080808080808080", 8 | "f90211a0433f73c2b5b319582d37706a84518795817198080a54d6edcd0628849f77c9b3a0b119dd4d1693f74474393dd3fd6e242c78e62e2f6c6fb1377f2581e2fb6d8b31a063fea0a859dbd22a5e51ffbeb2ab69004df0d127d39e70b1e265b505fc18ac57a06fd11fc7c449f60ee5e6ceb57c473f098fe524cc2740e5f97be5b617c5ac65c3a0294d52431c5616a2b9398ccb910eea9d11650a7620b39144f29aa9e8b5b9496ca0a38bbe39e8d4510fbc37cb4f6017adc421974e9dc8012ffa4aa4dda52dc3ddffa05a3e23d1d33f5f3ee76bae4acd3f056fe5fce82ebbac52f2aba571141e4914eea074c7f475117855698866656b485357a0cff74cf40666c6fe0efce8fbd6ef4433a089ed7ceabe09a0ca32a199bae31fc5f5b68ac043e5c9156cb5983cf9355af35ca050504d811bbcfaa4b30e149d2fe1af160df9cdeeaa2c3bf7c817eccf323807fba08a954618d63fd2dc537dbc94695af3a2b78636496d08719f6c447cb9ab77c996a0302e1582af58292cdda9d3a9aadb037d291bb53bc1faf7b7a036533cbfc39338a0e38ab0c780f487efd26a453a3989da62cc084fc2c46654da8df49ebaadb3df89a0c8f455f0f151c109b3fcad2a3d882586308f2641380b18cf1e4a2e6a2421edbda0bb6e313479f4196b20309ff2c0e4e551bdcd8406a8ff4ad111131d09fb702b13a03fa5d680b38ec07cf6e3174dfaff5abc5dabd48f61443ea0b60210bf798f2aa180", 9 | "f9027220b9026e02f9026a0183744ab3b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000400000000000000000000000000000010000000020000000000000000000000000000000000000000000000020000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000012a01938484c1d9b3f4b88744b0ef47c85224ac468440b2156597add37d98d0e518eb8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001200000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0xc90b465910867add32be4339f46cffb34bbb5380cb7aa26e9afbf18dd3ab6891" 12 | } -------------------------------------------------------------------------------- /test/libraries/fixtures/eventProof20.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "40", 4 | "logIndex": 0, 5 | "messageRoot": "0xcb8eb4e63f84a1c66a2221666c232ece01fd1b85f3076258d6a590bf55219e68", 6 | "proof": [ 7 | "f90131a029143648e4134ac4a859afe02fdc8a43ba401beb08d7105ad27088237891b069a0310357a2b6b1cc256b69dbb81c379705b8b2d044e77a60065bb39e4760b5e362a0dd31866ea0bf595cfc898c854cca326e6cda637af5c2a805f1723f4fd0c53a75a06bb9550e9ed22277676eb654be5ea79ba04c93440cfcf35ac1bb511eb8f56dffa0b5854ef88b5134f8d4b65c2d25e57eced7cb1f0ac3a1fd33e494b986231bb6d3a094738933e753b0519aae81ffa4e1bcadf2fed2732627aee8ddc7efbdf2ce2e12a0a281ebaf70bcf96e07e24a3d94757d3aa4a68513d5ccc7da1e7bc80a510e720fa018636d325c0e84287c51d96a8905e7eaa68d5084a60911850e0baa4b41240787a0baba8142ae3004868a45c0c064d92dbc88461b253c9a909e43ae11681bb41a978080808080808080", 8 | "f90211a064786284a2bcf89e10046e47cd839f8f530eede0166b03d661937ca6e17aabffa07495e2bfc5275a4af9e149aa14e4841f154ba8eefedafd351a90c68bf1c06bcaa045cf6a3864ebc581f8bfb15eb5462202ed4e8f76b351433f437012f7311e3590a03d9cbcf83748195b7d270fa126e422a8d6803b6db9a7d02596814658b1f4c25fa09ad8e55c2f220e54d77bad52268e89933b2d386c2eeeacf4c22c02f4260d7b0ca0922b996ff393e1efed2f3e852b6f004889a0acbb899704056614b45aec2b8a05a08b9ddd951795c2a1e2e77b8091d3af67c42d9f60d2bade81aa5324878460dd50a081a3e0462d03f30056e752c12fcd8312e28c4d1e5442af99ae770b838ba43752a056bdd12e6ffa8ceb6ffcdefce0970de6bfa312f63de7ecb5cdfb984e7b66d09ba03ab55d34c586b81831a98bbb7cd19bc547f28d8171c5ee45832b0747326f7e12a032424607e86cff9a1d6950682ca15bbb1db53e794b0699ce388ef0ce754db644a01d8a2307fc0234bad9f39cf566a849001e8d1e143a823f149d7db265f58979e7a062ab01fbd843642274c83796c02f9bdee0fa608cdc3252d0e838700669754ff7a0bf147696e942718362f87e1c680805f0c83c8a9a5d45fd0f1fc9e654592d1d38a0a8d544946dd7baf35d4085770fe8038881b826dda9c5c45469bf4ae2459ab583a0c3f44f311fb6d86ccaf992ea7abfdaae7fef0ed8f33ab18d69b4937d077293da80", 9 | "f9027220b9026e02f9026a0183b7733ab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000000100000000000000000000000000000000010000000000000000000000000000000000002000000000000400000000000020000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000014a0cb8eb4e63f84a1c66a2221666c232ece01fd1b85f3076258d6a590bf55219e68b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001400000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x6181399ab4ad28aa38be04014cadab703bed75efd2251e127b43af0ba832501c" 12 | } -------------------------------------------------------------------------------- /external/examples/bridge/Bridge.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.16; 3 | 4 | import "forge-std/console.sol"; 5 | import "ds-test/test.sol"; 6 | import "forge-std/Vm.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; 10 | 11 | import "src/amb/mocks/MockTelepathy.sol"; 12 | 13 | import "./Bridge.sol"; 14 | import "./Tokens.sol"; 15 | 16 | contract BridgeTest is Test { 17 | uint32 constant SOURCE_CHAIN = 1; 18 | uint32 constant DEST_CHAIN = 2; 19 | 20 | MockTelepathy broadcaster; 21 | MockTelepathy receiver; 22 | ERC20 depositToken; 23 | Deposit deposit; 24 | Withdraw withdraw; 25 | 26 | function setUp() public { 27 | broadcaster = new MockTelepathy(SOURCE_CHAIN); 28 | receiver = new MockTelepathy(DEST_CHAIN); 29 | broadcaster.addTelepathyReceiver(DEST_CHAIN, receiver); 30 | // Make a mock ERC-20 for testing 31 | depositToken = new ERC20PresetFixedSupply("GigaBrainToken", "GBT", 1000000, address(this)); 32 | deposit = new Deposit(address(broadcaster), address(depositToken)); 33 | withdraw = new Withdraw(address(deposit), address(receiver), SOURCE_CHAIN); 34 | deposit.setWithdraw(address(withdraw)); 35 | } 36 | 37 | function _deposit(uint256 _amount) internal { 38 | depositToken.approve(address(deposit), _amount); 39 | address recipient = makeAddr("recipient"); 40 | deposit.deposit{value: deposit.FEE()}(recipient, _amount, address(depositToken), DEST_CHAIN); 41 | } 42 | 43 | function test_Deposit() public { 44 | _deposit(5); 45 | require(depositToken.balanceOf(address(deposit)) == 5); 46 | } 47 | 48 | function test_DepositAndWithdraw() public { 49 | _deposit(5); 50 | require(depositToken.balanceOf(address(deposit)) == 5); 51 | require(withdraw.token().balanceOf(makeAddr("recipient")) == 0); 52 | broadcaster.executeNextMessage(); 53 | require(withdraw.token().balanceOf(makeAddr("recipient")) == 5); 54 | } 55 | 56 | function test_DepositRequiresFee() public { 57 | // expect a revert here 58 | depositToken.approve(address(deposit), 5); 59 | address recipient = makeAddr("recipient"); 60 | vm.expectRevert("Not enough fee"); 61 | deposit.deposit(recipient, 5, address(depositToken), 2); 62 | } 63 | 64 | function test_ClaimFees() public { 65 | _deposit(5); 66 | // We must transfer the deposit contract to an EOA to claim fees since 67 | // by default it owned by the BridgeTest contract, which is not payable 68 | address eoa = makeAddr("eoa"); 69 | deposit.transferOwnership(eoa); 70 | vm.prank(eoa); 71 | // deposit.claimFees(); 72 | assertEq(eoa.balance, deposit.FEE()); 73 | } 74 | 75 | function test_ClaimFeesToAddress() public { 76 | _deposit(5); 77 | // deposit.claimFees(makeAddr("anotherEOA")); 78 | assertEq(makeAddr("anotherEOA").balance, deposit.FEE()); 79 | } 80 | 81 | function test_OnlyOwnerClaimFees() public { 82 | vm.prank(makeAddr("anotherEOA")); 83 | vm.expectRevert("Ownable: caller is not the owner"); 84 | // deposit.claimFees(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/oracle/TelepathyOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {TelepathyHandler} from "src/amb/interfaces/TelepathyHandler.sol"; 4 | import {IOracleCallbackReceiver} from "src/oracle/interfaces/IOracleCallbackReceiver.sol"; 5 | 6 | enum RequestStatus { 7 | UNSENT, 8 | PENDING, 9 | SUCCESS, 10 | FAILED 11 | } 12 | 13 | struct RequestData { 14 | uint256 nonce; 15 | address targetContract; 16 | bytes targetCalldata; 17 | address callbackContract; 18 | } 19 | 20 | contract TelepathyOracle is TelepathyHandler { 21 | event CrossChainRequestSent( 22 | uint256 indexed nonce, 23 | address targetContract, 24 | bytes targetCalldata, 25 | address callbackContract 26 | ); 27 | 28 | error InvalidChainId(uint256 sourceChain); 29 | error NotFulfiller(address srcAddress); 30 | error RequestNotPending(bytes32 requestHash); 31 | 32 | /// @notice Maps request hashes to their status 33 | /// @dev The hash of a request is keccak256(abi.encode(RequestData)) 34 | mapping(bytes32 => RequestStatus) public requests; 35 | /// @notice The next nonce to use when sending a cross-chain request 36 | uint256 public nextNonce = 1; 37 | /// @notice The address of the fulfiller contract on the other chain 38 | address public fulfiller; 39 | /// @notice The chain ID of the fulfiller contract 40 | uint32 public fulfillerChainId; 41 | 42 | constructor(uint32 _fulfillerChainId, address _telepathyRouter, address _fulfiller) 43 | TelepathyHandler(_telepathyRouter) 44 | { 45 | fulfillerChainId = _fulfillerChainId; 46 | fulfiller = _fulfiller; 47 | } 48 | 49 | function requestCrossChain( 50 | address _targetContract, 51 | bytes calldata _targetCalldata, 52 | address _callbackContract 53 | ) external returns (uint256 nonce) { 54 | unchecked { 55 | nonce = nextNonce++; 56 | } 57 | RequestData memory requestData = 58 | RequestData(nonce, _targetContract, _targetCalldata, _callbackContract); 59 | bytes32 requestHash = keccak256(abi.encode(requestData)); 60 | requests[requestHash] = RequestStatus.PENDING; 61 | 62 | emit CrossChainRequestSent(nonce, _targetContract, _targetCalldata, _callbackContract); 63 | return nonce; 64 | } 65 | 66 | function handleTelepathyImpl(uint32 _sourceChain, address _senderAddress, bytes memory _data) 67 | internal 68 | override 69 | { 70 | if (_sourceChain != fulfillerChainId) { 71 | revert InvalidChainId(_sourceChain); 72 | } 73 | if (_senderAddress != fulfiller) { 74 | revert NotFulfiller(_senderAddress); 75 | } 76 | 77 | ( 78 | uint256 nonce, 79 | bytes32 requestHash, 80 | address callbackContract, 81 | bytes memory responseData, 82 | bool responseSuccess 83 | ) = abi.decode(_data, (uint256, bytes32, address, bytes, bool)); 84 | 85 | if (requests[requestHash] != RequestStatus.PENDING) { 86 | revert RequestNotPending(requestHash); 87 | } 88 | 89 | requests[requestHash] = responseSuccess ? RequestStatus.SUCCESS : RequestStatus.FAILED; 90 | 91 | callbackContract.call( 92 | abi.encodeWithSelector( 93 | IOracleCallbackReceiver.handleOracleResponse.selector, 94 | nonce, 95 | responseData, 96 | responseSuccess 97 | ) 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/amb-v2/TelepathyStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 5 | import {MessageStatus} from "src/amb-v2/interfaces/ITelepathy.sol"; 6 | import {VerifierType} from "src/amb-v2/verifier/interfaces/IMessageVerifier.sol"; 7 | 8 | contract TelepathyStorageV2 { 9 | /*////////////////////////////////////////////////////////////// 10 | BROADCASTER STORAGE 11 | //////////////////////////////////////////////////////////////*/ 12 | 13 | /// @notice Whether sending is enabled or not. 14 | bool public sendingEnabled; 15 | 16 | /// @notice Mapping between a nonce and a message root. 17 | mapping(uint64 => bytes32) public messages; 18 | 19 | /// @notice Keeps track of the next nonce to be used. 20 | uint64 public nonce; 21 | 22 | /*////////////////////////////////////////////////////////////// 23 | RECEIVER STORAGE 24 | //////////////////////////////////////////////////////////////*/ 25 | 26 | /// @notice All sourceChainIds. 27 | /// @dev DEPRECATED: This is no longer in use since the move over to external IMessageVerifiers. 28 | uint32[] public sourceChainIds; 29 | 30 | /// @notice Mapping between source chainId and the corresponding light client. 31 | /// @dev DEPRECATED: This is no longer in use since the move over to external IMessageVerifiers. 32 | mapping(uint32 => ILightClient) public lightClients; 33 | 34 | /// @notice Mapping between source chainId and the address of the TelepathyRouterV2 on that chain. 35 | /// @dev DEPRECATED: This is no longer in use since the move over to external IMessageVerifiers. 36 | mapping(uint32 => address) public broadcasters; 37 | 38 | /// @notice Mapping between a source chainId and whether it's frozen. 39 | /// @dev DEPRECATED: This is no longer in use, a global bool 'executingEnabled' is now used. 40 | mapping(uint32 => bool) public frozen; 41 | 42 | /// @notice Mapping between a message root and its status. 43 | mapping(bytes32 => MessageStatus) public messageStatus; 44 | 45 | /*////////////////////////////////////////////////////////////// 46 | SHARED STORAGE 47 | //////////////////////////////////////////////////////////////*/ 48 | 49 | /// @notice Returns current contract version. 50 | uint8 public version; 51 | 52 | /*////////////////////////////////////////////////////////////// 53 | RECEIVER STORAGE V2 54 | //////////////////////////////////////////////////////////////*/ 55 | 56 | /// @notice Storage root cache. 57 | /// @dev DEPRECATED: This is no longer in use since the move over to external IMessageVerifiers. 58 | mapping(bytes32 => bytes32) public storageRootCache; 59 | 60 | /// @notice Default verifier contracts for each type. 61 | mapping(VerifierType => address) public defaultVerifiers; 62 | 63 | /// @notice Whether executing messages is enabled or not. 64 | bool public executingEnabled; 65 | 66 | /// @notice Whitelisted relayers that can execute messages with the ZK verifiers. 67 | mapping(address => bool) public zkRelayers; 68 | 69 | /// @notice A reference to the contract where fees are sent. 70 | /// @dev During the send functions, this is used to add msg.value to the sender's balance. 71 | address public feeVault; 72 | 73 | /// @dev This empty reserved space is put in place to allow future versions to add new variables 74 | /// without shifting down storage in the inheritance chain. 75 | /// See: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps 76 | uint256[36] private __gap; 77 | } 78 | -------------------------------------------------------------------------------- /test/pubsub/fixtures/eventProof22.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "08", 4 | "logIndex": 0, 5 | "messageRoot": "0x6e0909b5cbe16ecdf8e1facca676377d135de55ff589117840ec2d4f5b03dba0", 6 | "proof": [ 7 | "f8b1a0be2cf796ff3a2b06c586fbf1a2056b1aba30d2d9057a31ba0433e7c47cdb1c2aa037a25908599493b9d98b1cb0e61bccf0bd2be4c20d78bd4fdf0fed847f42b783a0c232ea25d2f78828ab76bddc7dd9a101651a7c8842fca1541b506611a2807c14a0bb5a15ca0ff5e342e8724e1e14e10247b1cf09db09fb7f0f86ded42bfca1ffa480808080a0137de2431838a53d5a47fe0a0a0f54748f705496181ca66157e3ac349bd640268080808080808080", 8 | "f901f180a0c905908ba1eace0300d38ba819bc37037c2c59d7bdc9fc592501b37731a8af30a0ac4302f3f60b0f37837c6dfc51b95c75a6d025cd192d8c40aec7ea65fe8348bfa058bba9a1bdd1f3bf19871ab3a4cb56b5a774615cfb4b3aade372e1a57e4c0d69a0f2d6cf8f7a4361fc5fa1560bb00dfff5fdc72279cfde6b0edc6157944b1252b4a01f7ce16ef699d8194efb22be73b064861f39097a88f196dfe78198f59859eca1a0459c5ccf987f0e7f24df9ec39ee8bc10dfb82b227d5634c245a227f32feb7d50a0b62a5c5c62fdcfe2f4033ae37df2d511254a1ba31a5437f804ad776f35a41603a0052bd4fb22704d498caf9383595e817b4843f8327bc8fa55f4e889e10bbae532a0978f6ed371e0388759998338cf4c159fdb6e016b41857bffc9089da12c1d9fefa09ca0ad29ba4bc2d367d43cb294b325c5ac8370ae71e6effc94e42a0d0dbf8f7ea0f9c97ec52bfe7c112dd251b3bdc9041f6a26909556af55897624e3623be18c55a0280a947ae453d38311140e8498173557adc0e8a8cad04226828dcbdbd1099426a0e43b7eca5bc8a81bda808edd86917b2142306f58ea7b0e4bfa773820493f8631a003db84911ddf63696337401068d1484e924c990da229bb41b5c79815e38869b1a074c4443d5b234a36969237cc062224544cf0bd0b653ed1ed3d350a463213759480", 9 | "f9027220b9026e02f9026a01830c39dcb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000022000000000000800000000000000000000000000000000002000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000008000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000016a06e0909b5cbe16ecdf8e1facca676377d135de55ff589117840ec2d4f5b03dba0b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001600000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x07027e85131341f6c0b5071fb5e53c5e30872c370204842587ea3a9e08085140", 12 | "logSource": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 13 | "logTopics": [ 14 | "0xe5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffac", 15 | "0x0000000000000000000000000000000000000000000000000000000000000016", 16 | "0x6e0909b5cbe16ecdf8e1facca676377d135de55ff589117840ec2d4f5b03dba0" 17 | ], 18 | "logData": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001600000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 19 | } -------------------------------------------------------------------------------- /src/pubsub/EventProof.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import {RLPReader} from "optimism-bedrock-contracts/rlp/RLPReader.sol"; 4 | import {RLPWriter} from "optimism-bedrock-contracts/rlp/RLPWriter.sol"; 5 | import {MerkleTrie} from "optimism-bedrock-contracts/trie/MerkleTrie.sol"; 6 | 7 | library EventProof { 8 | using RLPReader for RLPReader.RLPItem; 9 | using RLPReader for bytes; 10 | 11 | /// @notice Verifies that the given log data is valid for the given event proof. 12 | function parseEvent( 13 | bytes[] memory _receiptProof, 14 | bytes32 _receiptRoot, 15 | bytes memory _txIndexRLPEncoded, 16 | uint256 _logIndex, 17 | address _eventSource, 18 | bytes32 _eventSig 19 | ) internal pure returns (bytes32[] memory, bytes memory) { 20 | bytes memory value = MerkleTrie.get(_txIndexRLPEncoded, _receiptProof, _receiptRoot); 21 | bytes1 txTypeOrFirstByte = value[0]; 22 | 23 | // Currently, there are three possible transaction types on Ethereum. Receipts either come 24 | // in the form "TransactionType | ReceiptPayload" or "ReceiptPayload". The currently 25 | // supported set of transaction types are 0x01 and 0x02. In this case, we must truncate 26 | // the first byte to access the payload. To detect the other case, we can use the fact 27 | // that the first byte of a RLP-encoded list will always be greater than 0xc0. 28 | // Reference 1: https://eips.ethereum.org/EIPS/eip-2718 29 | // Reference 2: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp 30 | uint256 offset; 31 | if (txTypeOrFirstByte == 0x01 || txTypeOrFirstByte == 0x02) { 32 | offset = 1; 33 | } else if (txTypeOrFirstByte >= 0xc0) { 34 | offset = 0; 35 | } else { 36 | revert("Unsupported transaction type"); 37 | } 38 | 39 | // Truncate the first byte if eneded and get the RLP decoding of the receipt. 40 | uint256 ptr; 41 | assembly { 42 | ptr := add(value, 32) 43 | } 44 | RLPReader.RLPItem memory valueAsItem = RLPReader.RLPItem({ 45 | length: value.length - offset, 46 | ptr: RLPReader.MemoryPointer.wrap(ptr + offset) 47 | }); 48 | 49 | // The length of the receipt must be at least four, as the fourth entry contains events 50 | RLPReader.RLPItem[] memory valueAsList = valueAsItem.readList(); 51 | require(valueAsList.length == 4, "Invalid receipt length"); 52 | 53 | // Read the logs from the receipts and check that it is not ill-formed 54 | RLPReader.RLPItem[] memory logs = valueAsList[3].readList(); 55 | require(_logIndex < logs.length, "Log index out of bounds"); 56 | RLPReader.RLPItem[] memory relevantLog = logs[_logIndex].readList(); 57 | 58 | // Validate that the correct contract emitted the event 59 | address sourceContract = relevantLog[0].readAddress(); 60 | require(sourceContract == _eventSource, "Event was not emitted by source contract"); 61 | 62 | // Validate that the event signature matches 63 | bytes32[] memory topics = parseTopics(relevantLog[1].readList()); 64 | require(bytes32(topics[0]) == _eventSig, "Event signature does not match"); 65 | 66 | bytes memory data = relevantLog[2].readBytes(); 67 | 68 | return (topics, data); 69 | } 70 | 71 | function parseTopics(RLPReader.RLPItem[] memory _topicsRLPEncoded) 72 | private 73 | pure 74 | returns (bytes32[] memory) 75 | { 76 | bytes32[] memory topics = new bytes32[](_topicsRLPEncoded.length); 77 | for (uint256 i = 0; i < _topicsRLPEncoded.length; i++) { 78 | topics[i] = bytes32(_topicsRLPEncoded[i].readUint256()); 79 | } 80 | return topics; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/amb-v2/TelepathyAccess.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {TelepathyStorageV2} from "src/amb-v2/TelepathyStorage.sol"; 5 | import {VerifierType} from "src/amb-v2/verifier/interfaces/IMessageVerifier.sol"; 6 | import {AccessControlUpgradeable} from 7 | "@openzeppelin-upgradeable/contracts/access/AccessControlUpgradeable.sol"; 8 | 9 | contract TelepathyAccessV2 is TelepathyStorageV2, AccessControlUpgradeable { 10 | /// @notice Emitted when the sendingEnabled flag is changed. 11 | event SendingEnabledChanged(bool enabled); 12 | 13 | /// @notice Emitted when the executingEnabled flag is changed. 14 | event ExecutingEnabledChanged(bool enabled); 15 | 16 | /// @notice Emitted when the zkRelayer whitelist is changed. 17 | event ZkRelayerChanged(address zkRelayer, bool enabled); 18 | 19 | /// @notice A random constant used to identify addresses with the permission of a 'guardian'. 20 | bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE"); 21 | 22 | /// @notice A random constant used to identify addresses with the permission of a 'timelock'. 23 | bytes32 public constant TIMELOCK_ROLE = keccak256("TIMELOCK_ROLE"); 24 | 25 | error OnlyAdmin(address sender); 26 | error OnlyTimelock(address sender); 27 | error OnlyGuardian(address sender); 28 | 29 | modifier onlyAdmin() { 30 | if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) { 31 | revert OnlyAdmin(msg.sender); 32 | } 33 | _; 34 | } 35 | 36 | modifier onlyTimelock() { 37 | if (!hasRole(TIMELOCK_ROLE, msg.sender)) { 38 | revert OnlyTimelock(msg.sender); 39 | } 40 | _; 41 | } 42 | 43 | modifier onlyGuardian() { 44 | if (!hasRole(GUARDIAN_ROLE, msg.sender)) { 45 | revert OnlyGuardian(msg.sender); 46 | } 47 | _; 48 | } 49 | 50 | /// @notice Allows the owner to control whether sending is enabled or not. 51 | /// @param _enabled Whether sending should be enabled or not. 52 | function setSendingEnabled(bool _enabled) external onlyGuardian { 53 | sendingEnabled = _enabled; 54 | emit SendingEnabledChanged(_enabled); 55 | } 56 | 57 | /// @notice Allows the owner to control whether executing is enabled or not. 58 | /// @param _enabled Whether executing should be enabled or not. 59 | function setExecutingEnabled(bool _enabled) external onlyGuardian { 60 | executingEnabled = _enabled; 61 | emit ExecutingEnabledChanged(_enabled); 62 | } 63 | 64 | /// @notice Updates the ZkRelayer whitelist with a given address. 65 | /// @param _zkRelayer The address to update. 66 | /// @param _enabled Whether the address should be enabled or not. 67 | function setZkRelayer(address _zkRelayer, bool _enabled) external onlyGuardian { 68 | zkRelayers[_zkRelayer] = _enabled; 69 | emit ZkRelayerChanged(_zkRelayer, _enabled); 70 | } 71 | 72 | /// @notice Sets the default IMessageVerifier contract for a given VerifierType. 73 | /// @param _verifierType The VerifierType to set the default verifier for. 74 | /// @param _verifier The address of the default verifier. 75 | function setDefaultVerifier(VerifierType _verifierType, address _verifier) 76 | external 77 | onlyTimelock 78 | { 79 | defaultVerifiers[_verifierType] = _verifier; 80 | } 81 | 82 | /// @notice Sets the address of the FeeVault. 83 | /// @param _feeVault The address of the FeeVault. 84 | function setFeeVault(address _feeVault) external onlyTimelock { 85 | feeVault = _feeVault; 86 | } 87 | 88 | /// @notice Sets the current version of the contract. 89 | /// @param _version The new version. 90 | function setVersion(uint8 _version) external onlyTimelock { 91 | version = _version; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/pubsub/interfaces/IPubSub.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | /// @notice The possible states a subscription can be in. 4 | enum SubscriptionStatus { 5 | UNSUBSCIBED, 6 | SUBSCRIBED 7 | } 8 | 9 | /// @notice Represents an active subscription, specific to the combination of all of the parameters. 10 | /// @param sourceChainId The chain ID of the source contract. 11 | /// @param sourceAddress The address of the source contract which emits the target event. 12 | /// @param callbackAddress The address of the contract which will receive the event data. MUST be implement 13 | /// the ISubscriptionCallbackReceiver interface. 14 | /// @param eventSig The signature of the event to listen for. 15 | /// @dev A subscription will still be active even if the endSlot has passed. To renew a subscription 16 | /// with different slot ranges, unsubscribe and re-subscribe. The reason slot ranges are not included 17 | /// in this struct is because they shouldn't influence the subscriptionId -- otherwise a subscriber could 18 | /// have overlapping subscriptions on the same event. 19 | struct Subscription { 20 | uint32 sourceChainId; 21 | address sourceAddress; 22 | address callbackAddress; 23 | bytes32 eventSig; 24 | } 25 | 26 | interface ISubscriber { 27 | /// @notice Emitted when a new subscription is created. 28 | /// @param subscriptionId The unique identifier for the subscription. 29 | /// @param startSlot The Beacon Chain slot to start listening for events, 0 for all slots up to endSlot. 30 | /// @param endSlot The Beacon Chain slot to stop listening for events, 0 for no limit. 31 | /// @param subscription The subscription details. 32 | /// @dev The startSlot and endSlot are inclusive. 33 | event Subscribe( 34 | bytes32 indexed subscriptionId, 35 | uint64 indexed startSlot, 36 | uint64 indexed endSlot, 37 | Subscription subscription 38 | ); 39 | 40 | /// @notice Emitted when a subscription is cancelled. 41 | /// @param subscriptionId The unique identifier for the subscription. 42 | /// @param subscription The subscription details. 43 | event Unsubscribe(bytes32 indexed subscriptionId, Subscription subscription); 44 | 45 | function subscribe( 46 | uint32 sourceChainId, 47 | address sourceAddress, 48 | address callbackAddress, 49 | bytes32 eventSig, 50 | uint64 startSlot, 51 | uint64 endSlot 52 | ) external returns (bytes32 subscriptionId); 53 | 54 | function unsubscribe(uint32 sourceChainId, address sourceAddress, bytes32 eventSig) 55 | external 56 | returns (bytes32 subscriptionId); 57 | } 58 | 59 | enum PublishStatus { 60 | NOT_EXECUTED, 61 | EXECUTION_FAILED, 62 | EXECUTION_SUCCEEDED 63 | } 64 | 65 | interface IPublisher { 66 | /// @notice Emitted when an event is published for a given subscription. 67 | /// @param subscriptionId The unique identifier for the subscription. 68 | /// @param sourceChainId The chain ID of the source contract. 69 | /// @param sourceAddress The address of the source contract which emitted the target event. 70 | /// @param callbackAddress The address of the contract which received the event data. 71 | /// @param success True if the callbackAddress successfully recieved the publish, false otherwise. 72 | event Publish( 73 | bytes32 indexed subscriptionId, 74 | uint32 indexed sourceChainId, 75 | address indexed sourceAddress, 76 | address callbackAddress, 77 | bool success 78 | ); 79 | 80 | function publishEvent( 81 | bytes calldata srcSlotTxSlotPack, 82 | bytes32[] calldata receiptsRootProof, 83 | bytes32 receiptsRoot, 84 | bytes[] calldata receiptProof, 85 | bytes memory txIndexRLPEncoded, 86 | uint256 logIndex, 87 | Subscription calldata subscription 88 | ) external; 89 | } 90 | -------------------------------------------------------------------------------- /test/pubsub/fixtures/eventProof19.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "11", 4 | "logIndex": 0, 5 | "messageRoot": "0xee82e8f1d871807b7187403b1aec0a96fbc11bb19de9b2c68a74670fcc48a843", 6 | "proof": [ 7 | "f8f1a05542706ecb22f74e7b176a62b19499b170a4b6e59a703043f41a65888aad015fa0920b49cfbeff3a78ad86deaeb37488d1c71e445307ad1009029f4330783e48d6a00355c5a07c2874cde519e4d858271e0bd187cdbe0b3003506c3f314357e0a22ca05cba8d75ef8ade066e86cce0319da97c4e9fd5096b5cf0cfedd39708723e09e4a00217c6589a94011f127b24c7a9dd4d17f4ef0f48efd6c1b29458100c4338ea76a065e9c130905deef91e1b7cb91384f5deb9574f8efed83b4eeb2c973bd0d6c7638080a0b24d547750e1ec0003bc8d5bb8c999a78dc79c60782b8bb86cf2e182a406ca4c8080808080808080", 8 | "f90211a0a30f7121156cdb065f500beb79405996878c2f87d024decb3c06061b9db10a7ba08c5a21866057139312efd10f4a808e8e9007ef7f6167883c546b360f479015b4a0d0a1b895f31438c0bdd9098a96ecb8c63ff2197e7cc5568bd55668066c69004ea003a794ad1afe67c1db71819f4df83efdc8a986014f0aba0f84e380487e4eaca9a027b25dd0f4b8894f3af584d948af68e91dd79cc9f7a2c8ae7a3470e8760e22dfa0c3ad65039e3dec04d4a05fbcbcdf067a667c6d02a1ff2e76f417770a09f48ecfa0d9e8e630e3c1a7e91bafc3865b1df9fe9354e1dd6b3db25a25ea399a34180d02a0986ba755b1f2ef9f241d7a5bfe3511d4436e310f7e02cab4eec72ceadbd21e2ca0920e5eed9987e395f956de6bf08313a3fcb7486e3c7be7bf603d08a52162d733a00a6965f6af5b3ff3a77367dd8c608c30425848bc72f1c90b8d82b06d8b45375ba0cb7488334cf1af9cf4b531b623c25cf1623ccc37fe1c41f9e8543296eed0cfdfa09e1fc056c1ed03c3c86d8fce7c29e1bcfbee247a96b996eb47a375fd882be9dda0b1f09a557374abdea991687ebc7b1faefabef610057844a83f928a435be1d4d3a09192c22d5f864e86d528cd3961388cfeb19cab42c71992ce15a478053521991ea06b0da550f45a3e2823156e7c2d21cd34f0c74aa6c00eea07273c0a00a7736a77a0a22e6b737b42e2605b0f6857c40ad1a1f782c698ed29be1d9e4b34354ab5111880", 9 | "f9027220b9026e02f9026a01834f5a56b9010020000000000000000000000000800000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000020000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000013a0ee82e8f1d871807b7187403b1aec0a96fbc11bb19de9b2c68a74670fcc48a843b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001300000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0xdc251fa3de7f9bc1a7d929a087e94cc4ac56fabe30986649fcb7bffafb01df15", 12 | "logSource": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 13 | "logTopics": [ 14 | "0xe5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffac", 15 | "0x0000000000000000000000000000000000000000000000000000000000000013", 16 | "0xee82e8f1d871807b7187403b1aec0a96fbc11bb19de9b2c68a74670fcc48a843" 17 | ], 18 | "logData": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001300000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 19 | } -------------------------------------------------------------------------------- /test/pubsub/fixtures/eventProof21.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "29", 4 | "logIndex": 0, 5 | "messageRoot": "0xa19f6d97bae7a59d4262969643b83f9c769a932f8ac2472cb6b047eadd6bffd3", 6 | "proof": [ 7 | "f8f1a05a12c91fa8e0b6ffaa7e358060a434a804c460a558c64c8cac739b5574c0fb3ca0ac43f6deba2e4e7de3bdb810b1e9657de6596eec48ba7434a344298a3eaea0ffa0ff4a914f5d7c37e41ca9af22a1b57667d1b93a2e7b3c9d571f6f4296243f975aa090e88932541b8bde1a4fc9026ef3fa2ca4c3653a692cae7697b8330359030bf5a08c5e253357b5d6327fb641552c66268879369c8d0b4c7ce4c913aae7e52ae1d8a01ba37702288266afac817efcc0092bccf1cd4ed32d12347d8d4b21922850e49c8080a00d39e4b4f3bd37be9d39240acfa574ee32f111254f26f5cd5fe61e6e416bb2d68080808080808080", 8 | "f90211a0b1df2af5342316e4523e2f0503e1143fdc444ca1a810b7cacd407ddaa1616176a048146f5064fe4bfb6bdbdc942ca5019452153e7147ef560bac31b53b7b08368fa0809e2ae8cd8ea2f4b04e4edec263f0572ae4b5961d71919da82d020319ad4574a0d675f6f762a2a7705880ca5340e320933e7b34081967ae9fde52b5e9f9711f55a07789e2f1d62337894b10ec9b9a6894a2c211acf8bac75838bd36cf8f23f041dda0c6ee1c83b0f940075566f012d19dc67a4ceb2056c9b1f2195bad1e46ae6a87eca03a4276e411a22bede0055d9b29b8d51bdefce5daa56eb2672044b295da32bb71a09533d16f0fa4e8e0f2abf04401ed1ca87b0a4da9edf81b2f6b7f603f7f00e354a0c79fabe94e09f48c86aaeda6b04450a62a490fc35c3a4c24cdb3dae0984c327da0199e23e997c1af157898214b631b3f18d5bea57d6e8f3532ced9e48b01eb484ba02b4fac48850c64345d10dcac72bf1c9ad3416b37fb3abe0e4063a8fe74333ec7a065f92f8014e4f31a2b36adb752e373014277b496123f5040e53ba515ea918f5ea039aca71d4b294863e66db30ffd7e441afc54d3523ffbe28235b181073cb32af8a06323d8eec8e3c21d82447ce5763bdbc8fa8f8eeb5cb082b623184ef16625a50fa026b6866e5ad31a388fe41cf3ea0b7a0d642fa18c65a5a3a3ae5dbf40df58b046a0fef8f5a6a45896ef72a1cd6af452805b24eb515dd61282a80a80b2dcebebb08c80", 9 | "f9027220b9026e02f9026a01834b8fb9b9010000000000000000400000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000040000000021000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000002000000000000000000000000000000000000000000000010000000020000002000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000015a0a19f6d97bae7a59d4262969643b83f9c769a932f8ac2472cb6b047eadd6bffd3b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001500000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x86bfb046f11a167c134efdd11ff8d3af90b5bd1c16f94a81961d4c7cdd23e7c0", 12 | "logSource": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 13 | "logTopics": [ 14 | "0xe5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffac", 15 | "0x0000000000000000000000000000000000000000000000000000000000000015", 16 | "0xa19f6d97bae7a59d4262969643b83f9c769a932f8ac2472cb6b047eadd6bffd3" 17 | ], 18 | "logData": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001500000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 19 | } -------------------------------------------------------------------------------- /test/pubsub/fixtures/eventProof18.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "29", 4 | "logIndex": 0, 5 | "messageRoot": "0x1938484c1d9b3f4b88744b0ef47c85224ac468440b2156597add37d98d0e518e", 6 | "proof": [ 7 | "f90131a000aa8f1077f15c506ad22062239b0d98a45d50c3bc60b0587de97a74fffc48bba00da597b7e0a851be8ab12c3afd5239a8170b797ca9119985a7c596713b28a28fa01b0efdbfec50605eeef2cf2a5b1a362067a16a0e87a828951b5dbcee9d3d8274a071091e0c1eff3e12b8ae7357580f6deb7bba18a5f42beb0a313230698bf13427a0b9c2518ae9f97b34384e3ac71a7f26988741454b2d4552b95801048e8e5cb15da046f56697dac603ec374ed429e865f6b53917b3abeaf63f69c30366498df165f0a0a99628742b1ad5db3a14f5e6517112eeb8fc53bff5bd36001aa92ad4441faa9fa0c022f40fa803ff27f195eed4b64cec1ddeb4a9b61e051bcd59f0c693d6a3a6e2a0680446e9b8e54c7034c7c32dd32369353f73e4ff05ae2c4e8b0710ef020e54768080808080808080", 8 | "f90211a0433f73c2b5b319582d37706a84518795817198080a54d6edcd0628849f77c9b3a0b119dd4d1693f74474393dd3fd6e242c78e62e2f6c6fb1377f2581e2fb6d8b31a063fea0a859dbd22a5e51ffbeb2ab69004df0d127d39e70b1e265b505fc18ac57a06fd11fc7c449f60ee5e6ceb57c473f098fe524cc2740e5f97be5b617c5ac65c3a0294d52431c5616a2b9398ccb910eea9d11650a7620b39144f29aa9e8b5b9496ca0a38bbe39e8d4510fbc37cb4f6017adc421974e9dc8012ffa4aa4dda52dc3ddffa05a3e23d1d33f5f3ee76bae4acd3f056fe5fce82ebbac52f2aba571141e4914eea074c7f475117855698866656b485357a0cff74cf40666c6fe0efce8fbd6ef4433a089ed7ceabe09a0ca32a199bae31fc5f5b68ac043e5c9156cb5983cf9355af35ca050504d811bbcfaa4b30e149d2fe1af160df9cdeeaa2c3bf7c817eccf323807fba08a954618d63fd2dc537dbc94695af3a2b78636496d08719f6c447cb9ab77c996a0302e1582af58292cdda9d3a9aadb037d291bb53bc1faf7b7a036533cbfc39338a0e38ab0c780f487efd26a453a3989da62cc084fc2c46654da8df49ebaadb3df89a0c8f455f0f151c109b3fcad2a3d882586308f2641380b18cf1e4a2e6a2421edbda0bb6e313479f4196b20309ff2c0e4e551bdcd8406a8ff4ad111131d09fb702b13a03fa5d680b38ec07cf6e3174dfaff5abc5dabd48f61443ea0b60210bf798f2aa180", 9 | "f9027220b9026e02f9026a0183744ab3b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000400000000000000000000000000000010000000020000000000000000000000000000000000000000000000020000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000012a01938484c1d9b3f4b88744b0ef47c85224ac468440b2156597add37d98d0e518eb8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001200000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0xc90b465910867add32be4339f46cffb34bbb5380cb7aa26e9afbf18dd3ab6891", 12 | "logSource": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 13 | "logTopics": [ 14 | "0xe5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffac", 15 | "0x0000000000000000000000000000000000000000000000000000000000000012", 16 | "0x1938484c1d9b3f4b88744b0ef47c85224ac468440b2156597add37d98d0e518e" 17 | ], 18 | "logData": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001200000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 19 | } -------------------------------------------------------------------------------- /test/pubsub/fixtures/eventProof20.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimedEmitter": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 3 | "key": "40", 4 | "logIndex": 0, 5 | "messageRoot": "0xcb8eb4e63f84a1c66a2221666c232ece01fd1b85f3076258d6a590bf55219e68", 6 | "proof": [ 7 | "f90131a029143648e4134ac4a859afe02fdc8a43ba401beb08d7105ad27088237891b069a0310357a2b6b1cc256b69dbb81c379705b8b2d044e77a60065bb39e4760b5e362a0dd31866ea0bf595cfc898c854cca326e6cda637af5c2a805f1723f4fd0c53a75a06bb9550e9ed22277676eb654be5ea79ba04c93440cfcf35ac1bb511eb8f56dffa0b5854ef88b5134f8d4b65c2d25e57eced7cb1f0ac3a1fd33e494b986231bb6d3a094738933e753b0519aae81ffa4e1bcadf2fed2732627aee8ddc7efbdf2ce2e12a0a281ebaf70bcf96e07e24a3d94757d3aa4a68513d5ccc7da1e7bc80a510e720fa018636d325c0e84287c51d96a8905e7eaa68d5084a60911850e0baa4b41240787a0baba8142ae3004868a45c0c064d92dbc88461b253c9a909e43ae11681bb41a978080808080808080", 8 | "f90211a064786284a2bcf89e10046e47cd839f8f530eede0166b03d661937ca6e17aabffa07495e2bfc5275a4af9e149aa14e4841f154ba8eefedafd351a90c68bf1c06bcaa045cf6a3864ebc581f8bfb15eb5462202ed4e8f76b351433f437012f7311e3590a03d9cbcf83748195b7d270fa126e422a8d6803b6db9a7d02596814658b1f4c25fa09ad8e55c2f220e54d77bad52268e89933b2d386c2eeeacf4c22c02f4260d7b0ca0922b996ff393e1efed2f3e852b6f004889a0acbb899704056614b45aec2b8a05a08b9ddd951795c2a1e2e77b8091d3af67c42d9f60d2bade81aa5324878460dd50a081a3e0462d03f30056e752c12fcd8312e28c4d1e5442af99ae770b838ba43752a056bdd12e6ffa8ceb6ffcdefce0970de6bfa312f63de7ecb5cdfb984e7b66d09ba03ab55d34c586b81831a98bbb7cd19bc547f28d8171c5ee45832b0747326f7e12a032424607e86cff9a1d6950682ca15bbb1db53e794b0699ce388ef0ce754db644a01d8a2307fc0234bad9f39cf566a849001e8d1e143a823f149d7db265f58979e7a062ab01fbd843642274c83796c02f9bdee0fa608cdc3252d0e838700669754ff7a0bf147696e942718362f87e1c680805f0c83c8a9a5d45fd0f1fc9e654592d1d38a0a8d544946dd7baf35d4085770fe8038881b826dda9c5c45469bf4ae2459ab583a0c3f44f311fb6d86ccaf992ea7abfdaae7fef0ed8f33ab18d69b4937d077293da80", 9 | "f9027220b9026e02f9026a0183b7733ab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000020000000000000000000000000000000000000000000000002000000000000000000000000000000000000100000000000000000000000000000000010000000000000000000000000000000000002000000000000400000000000020000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020000000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c9443f0222552e8114ad8f224dea89976d3bf41659df863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000014a0cb8eb4e63f84a1c66a2221666c232ece01fd1b85f3076258d6a590bf55219e68b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001400000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 10 | ], 11 | "receiptsRoot": "0x6181399ab4ad28aa38be04014cadab703bed75efd2251e127b43af0ba832501c", 12 | "logSource": "0x43f0222552e8114ad8F224DEA89976d3bf41659D", 13 | "logTopics": [ 14 | "0xe5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffac", 15 | "0x0000000000000000000000000000000000000000000000000000000000000014", 16 | "0xcb8eb4e63f84a1c66a2221666c232ece01fd1b85f3076258d6a590bf55219e68" 17 | ], 18 | "logData": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008501000000000000001400000005e2b19845fe2b7bb353f377d12dd51af012fbba2000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 19 | } -------------------------------------------------------------------------------- /external/examples/uniswap/UniswapExample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; 4 | import "@uniswap/v3-core/contracts/libraries/TickMath.sol"; 5 | import "@uniswap/v3-core/contracts/libraries/FixedPoint96.sol"; 6 | import "@uniswap/v3-core/contracts/libraries/FullMath.sol"; 7 | 8 | import {ITelepathyRouterV2} from "src/amb-v2/interfaces/ITelepathy.sol"; 9 | import {TelepathyHandlerV2} from "src/amb-v2/interfaces/TelepathyHandler.sol"; 10 | 11 | contract CrossChainTWAPRoute { 12 | ITelepathyRouterV2 router; 13 | mapping(uint32 => address) public deliveringContracts; 14 | 15 | event Route( 16 | uint32 indexed chainId, 17 | address poolAddress, 18 | uint32 twapInterval, 19 | uint256 timestamp, 20 | uint256 price 21 | ); 22 | 23 | constructor(address _router) { 24 | router = ITelepathyRouterV2(_router); 25 | } 26 | 27 | function setDeliveringContract(uint32 chainId, address _contract) external { 28 | deliveringContracts[chainId] = _contract; 29 | } 30 | 31 | // Code taken from https://chaoslabs.xyz/posts/chaos-labs-uniswap-v3-twap-deep-dive-pt-1 32 | function getPrice(address poolAddress, uint32 twapInterval) 33 | public 34 | view 35 | returns (uint256 priceX96) 36 | { 37 | require(twapInterval > 0); 38 | uint32[] memory secondsAgos = new uint32[](2); 39 | secondsAgos[0] = twapInterval; 40 | secondsAgos[1] = 0; 41 | (int56[] memory tickCumulatives,) = IUniswapV3Pool(poolAddress).observe(secondsAgos); 42 | // uint32 numerator; 43 | int24 tick; 44 | unchecked { 45 | tick = 46 | int24(int32(uint32(uint56(tickCumulatives[1] - tickCumulatives[0]) / twapInterval))); 47 | } 48 | uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick); 49 | return FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96); 50 | } 51 | 52 | function routePrice(uint32 chainId, address poolAddress, uint32 twapInterval) 53 | external 54 | payable 55 | returns (uint256 priceX96) 56 | { 57 | require(deliveringContracts[chainId] != address(0), "TWAP Route: otherSideContract not set"); 58 | priceX96 = getPrice(poolAddress, twapInterval); 59 | bytes memory data = abi.encode(poolAddress, twapInterval, block.timestamp, priceX96); 60 | router.send{value: msg.value}(chainId, deliveringContracts[chainId], data); 61 | emit Route(chainId, poolAddress, twapInterval, block.timestamp, priceX96); 62 | } 63 | } 64 | 65 | contract CrossChainTWAPReceiver is TelepathyHandlerV2 { 66 | uint32 srcChainId; 67 | address sourceAddress; 68 | 69 | mapping(bytes32 => uint256) public priceMap; 70 | mapping(bytes32 => uint256) public timestampMap; 71 | 72 | constructor(uint32 _srcChainId, address _sourceAddress, address _telepathyReceiver) 73 | TelepathyHandlerV2(_telepathyReceiver) 74 | { 75 | srcChainId = _srcChainId; 76 | sourceAddress = _sourceAddress; 77 | } 78 | 79 | function handleTelepathyImpl(uint32 _srcChainId, address sender, bytes memory data) 80 | internal 81 | override 82 | { 83 | require(_srcChainId == srcChainId); 84 | require(sender == sourceAddress); 85 | (address poolAddress, uint32 twapInterval, uint256 timestamp, uint256 priceX96) = 86 | abi.decode(data, (address, uint32, uint256, uint256)); 87 | // We only accept incrementing timestamps per (poolAddress, twapInterval) pair, for simplicity 88 | bytes32 key = keccak256(abi.encode(poolAddress, twapInterval)); 89 | require(timestamp > timestampMap[key]); 90 | priceMap[key] = priceX96; 91 | timestampMap[key] = timestamp; 92 | } 93 | 94 | function getLatestPrice(address poolAddress, uint32 twapInterval) 95 | public 96 | view 97 | returns (uint256 priceX96, uint256 timestamp) 98 | { 99 | bytes32 key = keccak256(abi.encode(poolAddress, twapInterval)); 100 | priceX96 = priceMap[key]; 101 | timestamp = timestampMap[key]; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /test/pubsub/EventProof.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | 3 | import "forge-std/Test.sol"; 4 | import "forge-std/console.sol"; 5 | import {MockTelepathy} from "src/amb/mocks/MockTelepathy.sol"; 6 | import { 7 | TelepathySubscriber, 8 | SubscriptionStatus, 9 | Subscription 10 | } from "src/pubsub/TelepathySubscriber.sol"; 11 | import {TelepathyHandler} from "src/amb/interfaces/TelepathyHandler.sol"; 12 | 13 | import {RLPReader} from "optimism-bedrock-contracts/rlp/RLPReader.sol"; 14 | import {RLPWriter} from "optimism-bedrock-contracts/rlp/RLPWriter.sol"; 15 | import {MerkleTrie} from "optimism-bedrock-contracts/trie/MerkleTrie.sol"; 16 | 17 | import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; 18 | 19 | import {EventProof} from "src/pubsub/EventProof.sol"; 20 | import {EventProofFixture} from "test/pubsub/EventProofFixture.sol"; 21 | 22 | contract EventProofTest is Test, EventProofFixture { 23 | using RLPReader for bytes; 24 | using RLPReader for RLPReader.RLPItem; 25 | 26 | uint256 constant FIXTURE_START = 18; 27 | uint256 constant FIXTURE_END = 22; 28 | 29 | Fixture[] fixtures; 30 | 31 | function setUp() public { 32 | // read all event proof fixtures 33 | for (uint256 i = FIXTURE_START; i <= FIXTURE_END; i++) { 34 | uint256 msgNonce = i; 35 | 36 | string memory filename = string.concat("eventProof", Strings.toString(msgNonce)); 37 | string memory path = 38 | string.concat(vm.projectRoot(), "/test/pubsub/fixtures/", filename, ".json"); 39 | try vm.readFile(path) returns (string memory file) { 40 | bytes memory parsed = vm.parseJson(file); 41 | fixtures.push(abi.decode(parsed, (Fixture))); 42 | } catch { 43 | continue; 44 | } 45 | } 46 | } 47 | 48 | function test_SetUp() public view { 49 | require(fixtures.length > 0, "no fixtures found"); 50 | } 51 | 52 | function test_VerifyEvent() public { 53 | for (uint256 i = 0; i < fixtures.length; i++) { 54 | Fixture memory fixture = fixtures[i]; 55 | 56 | bytes[] memory receiptProof = buildProof(fixture); 57 | 58 | (bytes32[] memory eventTopics, bytes memory eventData) = EventProof.parseEvent( 59 | receiptProof, 60 | fixture.receiptsRoot, 61 | vm.parseBytes(fixture.key), 62 | fixture.logIndex, 63 | fixture.logSource, 64 | fixture.logTopics[0] 65 | ); 66 | 67 | assertEq(eventTopics.length, fixture.logTopics.length); 68 | for (uint256 j = 0; j < eventTopics.length; j++) { 69 | assertEq(eventTopics[j], fixture.logTopics[j]); 70 | } 71 | assertEq(eventData, fixture.logData); 72 | } 73 | } 74 | 75 | function test_VerifyEventRevert_WhenEventSourceInvalid() public { 76 | for (uint256 i = 0; i < fixtures.length; i++) { 77 | Fixture memory fixture = fixtures[i]; 78 | 79 | bytes[] memory receiptProof = buildProof(fixture); 80 | 81 | vm.expectRevert("Event was not emitted by source contract"); 82 | EventProof.parseEvent( 83 | receiptProof, 84 | fixture.receiptsRoot, 85 | vm.parseBytes(fixture.key), 86 | fixture.logIndex, 87 | makeAddr("bad"), 88 | fixture.logTopics[0] 89 | ); 90 | } 91 | } 92 | 93 | function test_VerifyEventRevert_WhenEventSigInvalid() public { 94 | for (uint256 i = 0; i < fixtures.length; i++) { 95 | Fixture memory fixture = fixtures[i]; 96 | 97 | bytes[] memory receiptProof = buildProof(fixture); 98 | 99 | vm.expectRevert("Event signature does not match"); 100 | EventProof.parseEvent( 101 | receiptProof, 102 | fixture.receiptsRoot, 103 | vm.parseBytes(fixture.key), 104 | fixture.logIndex, 105 | fixture.logSource, 106 | keccak256("Incremented(uint256)") 107 | ); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/lightclient/fixtures/opt_slot6440799.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1606824023", 4 | "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 786, 8 | "syncCommitteePoseidon": "8990793856514711848343989034346087954858322624354434024535729644189516427275" 9 | }, 10 | "step": { 11 | "attestedSlot": 6440799, 12 | "finalizedSlot": 6440704, 13 | "participation": "508", 14 | "finalizedHeaderRoot": "0x83b07225d8881a370704969524d27f254d3cf95b5e0e990cdce0a62c957ddde2", 15 | "executionStateRoot": "0x726a021ba94ef73eedbc660dd8361ae62562889b3c57122e7deb31a48ce49250", 16 | "a": [ 17 | "20341816226037290162695097894738502827183722475087077746809376200889990486779", 18 | "568944459817075437156906411017177243839272276410780786991556439061209668163" 19 | ], 20 | "b": [ 21 | [ 22 | "8935781967721625090293207997876747593432636237238089048210670327982588832013", 23 | "13748780066162296131457946874723275099304692224718888735365503953453001983357" 24 | ], 25 | [ 26 | "15889672098076435610852652654377666004089655251962699814340540277621867845834", 27 | "13322169779056015981335166591774949988927886447836969115292624530742787496039" 28 | ] 29 | ], 30 | "c": [ 31 | "1715523897170803081382120731006800709179107149741930555170237611475564086149", 32 | "16979274642311964239459365757618505309158579302151552961728211120444336578879" 33 | ], 34 | "inputs": [ 35 | "10610510149051423230032334406071331366708680680180718028331928014597884898202" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "18005677098958292055315155192295690371073150476870130494562051599534870711391", 41 | "1826751239282886035492398409293992700873321214952499616699018006142050035289" 42 | ], 43 | "b": [ 44 | [ 45 | "18266709471483601577284657044611483021848099535392067381114490834696470256292", 46 | "9473246228907545021046833838732669589251959915292717694845600392994244286165" 47 | ], 48 | [ 49 | "16206365825567386604019298827298478207370030310560371299428926012580418397662", 50 | "15377594734101233465286867538065087294077574727948314435212821525963941475757" 51 | ] 52 | ], 53 | "c": [ 54 | "21051477170779585852282123810593796791011929993845201413658275269177193790977", 55 | "17488741047153057389541286677551687915946676828265490067224995426429164921068" 56 | ], 57 | "syncCommitteeSSZ": "0x98356e429c2a0bb6803bf55345b6e2ba6adf0cfbc7ea2b1128b67c628850c8bc", 58 | "syncCommitteePoseidon": "10964711001812479034952207793039478625146882342202782719041593415229899053922" 59 | }, 60 | "optimizedRotate": { 61 | "a": [ 62 | "19855495344375448376093991090945386494925741117270288304575226134816748762748", 63 | "16170823843035264458789312611931162929617715543543527307134660784082426426394" 64 | ], 65 | "b": [ 66 | [ 67 | "3806624807736353399327508659284138136262878430545867367152999332624724648835", 68 | "4665988725781221481434278696255123073225611593206380066770681549750301094900" 69 | ], 70 | [ 71 | "5775594486496634657534455888475956791195652745608677729593944747025625699932", 72 | "8249517035411414771942734770834977872420996587538569603873876545863813976170" 73 | ] 74 | ], 75 | "c": [ 76 | "21709584236388747201071407317546386753772938747734288602788608394056474457558", 77 | "18434266048868968688344100703517907642836346429174537368927181909699783836935" 78 | ], 79 | "syncCommitteeSSZ": "0x98356e429c2a0bb6803bf55345b6e2ba6adf0cfbc7ea2b1128b67c628850c8bc", 80 | "syncCommitteePoseidon": "10964711001812479034952207793039478625146882342202782719041593415229899053922", 81 | "publicInputsRoot": "6440509966686103059663821322418072893000431437753392825133237418136880763871" 82 | } 83 | } -------------------------------------------------------------------------------- /test/lightclient/fixtures/opt_slot6443999.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial": { 3 | "genesisTime": "1606824023", 4 | "genesisValidatorsRoot": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", 5 | "secondsPerSlot": 12, 6 | "slotsPerPeriod": 8192, 7 | "syncCommitteePeriod": 786, 8 | "syncCommitteePoseidon": "8990793856514711848343989034346087954858322624354434024535729644189516427275" 9 | }, 10 | "step": { 11 | "attestedSlot": 6443999, 12 | "finalizedSlot": 6443904, 13 | "participation": "501", 14 | "finalizedHeaderRoot": "0xd7936d2a904071ef1ab8295ae8be2770104e028a55c3debb895ee8174865e65d", 15 | "executionStateRoot": "0x1240f63dbd6ddb3d00bce11b088cbc7beab41261fb7ebcb6f6c8ff02177538f0", 16 | "a": [ 17 | "21153681123722234787152272149834651141106189246928508505951444563500362804792", 18 | "205875987076445865648998047801296403970680416009264482187983296014269765774" 19 | ], 20 | "b": [ 21 | [ 22 | "12971729021046699280677101368490117242331399429036090494350717046986353746259", 23 | "1437195201681253480037781573699018523995892575939474875157476383102884795613" 24 | ], 25 | [ 26 | "11641799076819879642558038848261564482314053680184364203857544396501280179184", 27 | "13778676177934584995709041492937296786613292382280399978262943904033943040594" 28 | ] 29 | ], 30 | "c": [ 31 | "13040679602778678179241263232588162640602174401959851897675543433476248858228", 32 | "14560832262805936971047272874559217121725481254551211223446580072812971832388" 33 | ], 34 | "inputs": [ 35 | "1806085386577832834067799607409954814919110296445814891317325147334693258457" 36 | ] 37 | }, 38 | "rotate": { 39 | "a": [ 40 | "503143496600375291383538410140600361702754739436212536491753670976461058835", 41 | "11169541001879667510330528675149564113250157628748723821668508680876682245641" 42 | ], 43 | "b": [ 44 | [ 45 | "15717792339624467599237177941393737936194196832102247537467533636029042917191", 46 | "16168857132555954631365408367271945659885936058329015834716872062757004906437" 47 | ], 48 | [ 49 | "19928663812519829792827842102051629656133323739318461492573108410691306357150", 50 | "13098041980633432622653326324459686763196056126956350527017196934311415848367" 51 | ] 52 | ], 53 | "c": [ 54 | "18828517046247302827009148730397612239899735965395663949432688726198444578777", 55 | "3937437295302744480700406313593277518962046895207919968401453422493970093509" 56 | ], 57 | "syncCommitteeSSZ": "0x98356e429c2a0bb6803bf55345b6e2ba6adf0cfbc7ea2b1128b67c628850c8bc", 58 | "syncCommitteePoseidon": "10964711001812479034952207793039478625146882342202782719041593415229899053922" 59 | }, 60 | "optimizedRotate": { 61 | "a": [ 62 | "5717659234390607800421851041769775558467226226922268205757238639857075225766", 63 | "19743269065044222654115154141311638991577847495622605728892025991700738326324" 64 | ], 65 | "b": [ 66 | [ 67 | "12933830376321867130056003173538920597786758891526111789766352367579077487830", 68 | "10069974556429574979768155894016365506984604468874276253425690580477028572735" 69 | ], 70 | [ 71 | "4858633308380775176929971714250165701751490718570885123051633496291905999168", 72 | "19905051158440603403594614803467445147164844313036141440567421482395610979943" 73 | ] 74 | ], 75 | "c": [ 76 | "12781791956060505925386505847451370714551164256628629505466212376854438155277", 77 | "14063036259523637785927597295635711596254492798622093060777911873573751373209" 78 | ], 79 | "syncCommitteeSSZ": "0x98356e429c2a0bb6803bf55345b6e2ba6adf0cfbc7ea2b1128b67c628850c8bc", 80 | "syncCommitteePoseidon": "10964711001812479034952207793039478625146882342202782719041593415229899053922", 81 | "publicInputsRoot": "9556288233199523718547635933580645531368446012023014919412259059105459003538" 82 | } 83 | } -------------------------------------------------------------------------------- /src/amb-v2/verifier/TelepathyStorageVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import {VerifierType, IMessageVerifier} from "src/amb-v2/verifier/interfaces/IMessageVerifier.sol"; 5 | import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol"; 6 | import {StorageProof} from "src/libraries/StateProofHelper.sol"; 7 | import {Message} from "src/libraries/Message.sol"; 8 | import {BeaconVerifierBase} from "src/amb-v2/verifier/BeaconVerifierBase.sol"; 9 | 10 | /// @title TelepathyStorageV2Verifier 11 | /// @author Succinct Labs 12 | /// @notice Verifies messages using a storage proof of inclusion in the source chain's 13 | /// TelepathyRouterV2. 14 | contract TelepathyStorageVerifier is IMessageVerifier, BeaconVerifierBase { 15 | using Message for bytes; 16 | 17 | /// @notice The index of the `messages` mapping in TelepathyStorageV2.sol. 18 | /// @dev We need this when calling `executeMessage` via storage proofs, as it is used in 19 | /// getting the slot key. 20 | uint256 internal constant MESSAGES_MAPPING_STORAGE_INDEX = 1; 21 | 22 | /// @notice A cache to avoid redundant storageProof calculation. 23 | mapping(bytes32 => bytes32) public storageRootCache; 24 | 25 | error ExecutionStateRootNotSet(address lightClient, uint64 slot); 26 | error InvalidStorageProof(); 27 | 28 | /// @param _sourceChainIds The chain IDs that this contract will verify messages from. 29 | /// @param _lightClients The Beacon Light Clients, one for each sourceChainId. 30 | /// @param _telepathyRouters The sending TelepathyRouters, one for each sourceChainId. 31 | function initialize( 32 | uint32[] memory _sourceChainIds, 33 | address[] memory _lightClients, 34 | address[] memory _telepathyRouters 35 | ) external initializer { 36 | __BeaconVerifierBase_init(_sourceChainIds, _lightClients, _telepathyRouters); 37 | } 38 | 39 | function verifierType() external pure override returns (VerifierType) { 40 | return VerifierType.ZK_STORAGE; 41 | } 42 | 43 | /// @notice Verifies a message using a storage proof. 44 | /// @param _proofData The proof of the message, which contains: 45 | /// uint64 slot 46 | /// bytes calldata message 47 | /// bytes[] calldata accountProof 48 | /// bytes[] calldata storageProof 49 | /// @param _message The message to verify. 50 | function verify(bytes calldata _proofData, bytes calldata _message) 51 | external 52 | override 53 | returns (bool) 54 | { 55 | (uint64 slot, bytes[] memory accountProof, bytes[] memory storageProof) = 56 | abi.decode(_proofData, (uint64, bytes[], bytes[])); 57 | 58 | uint32 sourceChainId = _message.sourceChainId(); 59 | address lightClient = lightClients[sourceChainId]; 60 | if (lightClient == address(0)) { 61 | revert LightClientNotFound(sourceChainId); 62 | } 63 | 64 | address telepathyRouter = telepathyRouters[sourceChainId]; 65 | if (telepathyRouter == address(0)) { 66 | revert TelepathyRouterNotFound(sourceChainId); 67 | } 68 | 69 | bytes32 storageRoot; 70 | bytes32 cacheKey = keccak256(abi.encodePacked(sourceChainId, slot, telepathyRouter)); 71 | 72 | // If the cache is empty for the cacheKey, then we get the 73 | // storageRoot using the provided accountProof. 74 | if (storageRootCache[cacheKey] == 0) { 75 | bytes32 executionStateRoot = ILightClient(lightClient).executionStateRoots(slot); 76 | if (executionStateRoot == 0) { 77 | revert ExecutionStateRootNotSet(lightClient, slot); 78 | } 79 | storageRoot = 80 | StorageProof.getStorageRoot(accountProof, telepathyRouter, executionStateRoot); 81 | storageRootCache[cacheKey] = storageRoot; 82 | } else { 83 | storageRoot = storageRootCache[cacheKey]; 84 | } 85 | 86 | bytes32 slotKey = keccak256( 87 | abi.encode(keccak256(abi.encode(_message.nonce(), MESSAGES_MAPPING_STORAGE_INDEX))) 88 | ); 89 | uint256 slotValue = StorageProof.getStorageValue(slotKey, storageRoot, storageProof); 90 | 91 | if (bytes32(slotValue) != _message.getId()) { 92 | revert InvalidStorageProof(); 93 | } 94 | 95 | return true; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/amb-v2/TestUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.16; 3 | 4 | import { 5 | ITelepathyReceiverV2, 6 | MessageStatus, 7 | ITelepathyHandlerV2, 8 | ITelepathyRouterV2 9 | } from "src/amb-v2/interfaces/ITelepathy.sol"; 10 | import {TelepathyRouterV2} from "src/amb-v2/TelepathyRouter.sol"; 11 | import {TelepathyStorageVerifier} from "src/amb-v2/verifier/TelepathyStorageVerifier.sol"; 12 | import {TelepathyEventVerifier} from "src/amb-v2/verifier/TelepathyEventVerifier.sol"; 13 | import {Bytes32} from "src/libraries/Typecast.sol"; 14 | import {TelepathyEventVerifier} from "src/amb-v2/verifier/TelepathyEventVerifier.sol"; 15 | import {TelepathyAttestationVerifier} from "src/amb-v2/verifier/TelepathyAttestationVerifier.sol"; 16 | import {VerifierType} from "src/amb-v2/verifier/interfaces/IMessageVerifier.sol"; 17 | import {SuccinctFeeVault} from "@telepathy-v2/payment/SuccinctFeeVault.sol"; 18 | 19 | library WrappedInitialize { 20 | function initializeRouter( 21 | address _targetAMB, 22 | uint32 _sourceChainId, 23 | address _beaconLightClient, 24 | address _stateQueryGateway, 25 | address _sourceAMB, 26 | address _timelock, 27 | address _guardian 28 | ) internal returns (address, address, address) { 29 | TelepathyRouterV2(_targetAMB).initialize( 30 | true, true, address(new SuccinctFeeVault(_guardian)), _timelock, _guardian 31 | ); 32 | 33 | return 34 | initializeVerifiers(_sourceChainId, _beaconLightClient, _stateQueryGateway, _sourceAMB); 35 | } 36 | 37 | function initializeVerifiers( 38 | uint32 _sourceChainId, 39 | address _beaconLightClient, 40 | address _stateQueryGateway, 41 | address _sourceAMB 42 | ) internal returns (address, address, address) { 43 | uint32[] memory sourceChainIds = new uint32[](1); 44 | sourceChainIds[0] = _sourceChainId; 45 | address[] memory beaconLightClients = new address[](1); 46 | beaconLightClients[0] = _beaconLightClient; 47 | address[] memory sourceAMBs = new address[](1); 48 | sourceAMBs[0] = _sourceAMB; 49 | 50 | address storageVerifierAddr = address(new TelepathyStorageVerifier()); 51 | TelepathyStorageVerifier(storageVerifierAddr).initialize( 52 | sourceChainIds, beaconLightClients, sourceAMBs 53 | ); 54 | 55 | address eventVerifierAddr = address(new TelepathyEventVerifier()); 56 | TelepathyEventVerifier(eventVerifierAddr).initialize( 57 | sourceChainIds, beaconLightClients, sourceAMBs 58 | ); 59 | 60 | address attestationVerifierAddr = address(new TelepathyAttestationVerifier()); 61 | TelepathyAttestationVerifier(attestationVerifierAddr).initialize( 62 | _stateQueryGateway, sourceChainIds, sourceAMBs 63 | ); 64 | 65 | return (storageVerifierAddr, eventVerifierAddr, attestationVerifierAddr); 66 | } 67 | } 68 | 69 | contract SimpleHandler is ITelepathyHandlerV2 { 70 | uint32 public sourceChain; 71 | address public sourceAddress; 72 | address public targetAMB; 73 | uint256 public nonce; 74 | mapping(uint256 => bytes32) public nonceToDataHash; 75 | VerifierType public vType = VerifierType.ZK_EVENT; 76 | 77 | function setParams(uint32 _sourceChain, address _sourceAddress, address _targetAMB) public { 78 | sourceChain = _sourceChain; 79 | sourceAddress = _sourceAddress; 80 | targetAMB = _targetAMB; 81 | } 82 | 83 | function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data) 84 | external 85 | override 86 | returns (bytes4) 87 | { 88 | require(msg.sender == targetAMB, "Only Telepathy can call this function"); 89 | require(_sourceChainId == sourceChain, "Invalid source chain id"); 90 | require(_sourceAddress == sourceAddress, "Invalid source address"); 91 | nonceToDataHash[nonce++] = keccak256(_data); 92 | return ITelepathyHandlerV2.handleTelepathy.selector; 93 | } 94 | 95 | function setVerifierType(VerifierType _verifierType) external { 96 | vType = _verifierType; 97 | } 98 | 99 | function verifierType() external view returns (VerifierType) { 100 | return vType; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/amb-v2/fixtures/eventSlotClose.json: -------------------------------------------------------------------------------- 1 | { 2 | "SOURCE_CHAIN": 5, 3 | "DEST_CHAIN": 100, 4 | "sourceAMBAddress": "0x2284A1b214D800748159237464dE4D236C050377", 5 | "sourceMessageSender": "0xded0000e32f8f40414d3ab3a830f735a3553e18e", 6 | "srcSlotTxSlotPack": "0x00000000000000000000000000000000000000000000000000000000005ca7b400000000000000000000000000000000000000000000000000000000005ca7b4", 7 | "message": "0x02000000000000000000000005ded0000e32f8f40414d3ab3a830f735a3553e18e00000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064", 8 | "receiptsRootProof": [ 9 | "0xfbe56fe7f1b3e292751ac70fd61cb4a174fa665fe5ce25d738930188ec8df40b", 10 | "0x99988871bb2ebf4b105a5a5d81ab43cfafddfcf3f5bc263ccb80f62f63549678", 11 | "0x6cab55f5857dd472d76790712a418a496187f6a8c704bd749cbb52e7b444c3bf", 12 | "0x8164c4e4988d00d9be7d89d8b71aa316e8fa0d66857436ef1ea401f88eb5410b", 13 | "0x1741a80000000000000000000000000000000000000000000000000000000000", 14 | "0x3078ba9620f2bccea0c762a8c74c0689aa600cb6bba3f53e80d8517a4eec3cd3", 15 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 16 | "0xf2d898676c56b86625d3339f6aca763427b23694aed9e27aa870efcce4b80583", 17 | "0xd7ae523f757ab6b56315df24cf96fdce80ff3a6f9ca01c484c82f713f52b014d", 18 | "0x1642ee9ba634a59c527f12fdf01533bd86ac28f0d4792ba9cd06beba369b2932", 19 | "0x3a44fab47d27f2102226ebc2e7e28541cfabfa42dfd453bde7135fe16a70a1d3", 20 | "0xd69948a47a1801b29cd5325d9d1debdd04c723a7b0d866972ae210a5adabc389" 21 | ], 22 | "receiptsRoot": "0xfde7d8248cf1bb7c63e4de2341d337b499ab4ee72de6da29ff4341004c2eb31c", 23 | "receiptProof": [ 24 | "0xf891a013dac50a1c27f32bbe246112e1863ba3cffc4c80c1be0d25be2a8dcb4109b5cba001a811ec34dd545e7258e9b50b598fab8c5e661b7d0663f5f698a4e0778dfd2da00a1fd26a431028cbb11f2d5921b91de14af0d6b89f04eb90e977858521dd196e8080808080a09d322dc045913233638b59eea7bb855fe3719425bfaa3c369bbf46380ca87e748080808080808080", 25 | "0xf901f180a08587e2dfd86ad2b26104473202069457b22903a686713501553b415817b39b29a073bbedceecc959735e1dbf16bb6dcabc25caf9bb4b10b33455f2cbc3ef2fa0dfa047c1728a392ef6e6e99dce5ff632a7125d61dbc76ee071a39abb015707ee4381a01397db6b22073d5bd649d04f8767db1a2e72a5c929aa0dc3a3b741f41a68b4cca0a85f8fab0f4fa23552272d67b6a12cf426863be110a3325411214cd9e524f70fa0985c0c311c107c272ad680d953b35ea6d5f155199b475d796202d982ac5554f9a040073d8d804c72ca07595a92b1205b5e93104243a6c20bb980b3f6f01b3faac1a00ba0ce6a6632d2519ff2f0bdfa99a7d87cefaa31956c675b218e510dd60c2b0aa0e715ded2e4268d7e9c17da9a94b666160a6b2fafab7d5c384c842d8dc7fa6345a0669a429fae3ca4703466c52ca9d05131c5c449c7625811175c5d30825eb5994ca0a3a7b504375df07869c580b61429312fdcf2de36f36e244782dfd4e04e36bd12a0033eb57f873d03aa38e6d5afa6578076e8450b484e44b2173707fae2fd4d6b5fa0398cbd1ef59a41d225101d82f92a7de87b03c73f384b0983ea3222089ccdfd71a09ffec1af26d3a5413ced31e6c27800f18f88e54982c0999ceea54dcc38043b51a0795d4e0884a7dd7626ec815607949aae540549519f0fb6edf3fac85744b8084780", 26 | "0xf9027220b9026e02f9026a01833db22cb9010000000000000000000000000000000000000000000400000000000000000000000002000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000002000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020020000000000000000000000000000000000000000000000000000000000000000000f9015ff9015c942284a1b214d800748159237464de4d236c050377f863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000000a0fe0891475496de5e014b0ec90de4411fcf5da2cc6962defa209a159dc2191791b8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008502000000000000000000000005ded0000e32f8f40414d3ab3a830f735a3553e18e00000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 27 | ], 28 | "txIndexRLPEncoded": "0x0a", 29 | "logIndex": 0, 30 | "sourceSlot": 6072244, 31 | "headerRoot": "0x790af4d86e03b8cd5aa7e8b3f3d0200f5b44133f8702eafd6ac7a0f2be5798d8" 32 | } -------------------------------------------------------------------------------- /test/amb-v2/fixtures/eventSlotSame.json: -------------------------------------------------------------------------------- 1 | { 2 | "SOURCE_CHAIN": 5, 3 | "DEST_CHAIN": 100, 4 | "sourceAMBAddress": "0x2284A1b214D800748159237464dE4D236C050377", 5 | "sourceMessageSender": "0xded0000e32f8f40414d3ab3a830f735a3553e18e", 6 | "srcSlotTxSlotPack": "0x00000000000000000000000000000000000000000000000000000000005ca8b800000000000000000000000000000000000000000000000000000000005ca8b8", 7 | "message": "0x02000000000000000700000005ded0000e32f8f40414d3ab3a830f735a3553e18e00000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064", 8 | "receiptsRootProof": [ 9 | "0x1bab4c24abc2179912bb752327b18590d5edb6f4566ab8c21b3477cd5bab01b1", 10 | "0x72f81e56a261bafb242da3a1dd68985dc748503ef5863b44ab3d1aa5dbb1bbea", 11 | "0x6a3448a133c54cd2c42b29d1488f1bf8704b57c09f43258c9f5d63771de12419", 12 | "0x14f39e7246d311408c4cf8cb1abb71cda877f5fc9fd40176e109d5acc3c86419", 13 | "0xb74da80000000000000000000000000000000000000000000000000000000000", 14 | "0x9ee5abe7234c22b01d991c8e7a14b0cc20bd80434570c0b5a83c656898ec47fa", 15 | "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", 16 | "0x95256eacc30af6c7132641ca202ce6d4e4867fce48b2cd6c155640b10f5259bd", 17 | "0x9613ca80bdba46cbde67fe2d227b42b84d8f1d5c03ab0c12bafa18843c2296f3", 18 | "0x77b7571eaf8cb5674aa3b2b3abcacc358e4878a699f13820106ad60516d6acbc", 19 | "0xc7ca6e0f59c7980a37493fcfa7e9a3412406c415a85b9c51caf3f232343f1d16", 20 | "0x4a01f073abcad6f41d1af1cd3fc3474588af7be2194fc09254eb2959eeaac241" 21 | ], 22 | "receiptsRoot": "0xc21d760650786f973451ae7ffc749f01e0724663c7b49c55c916d0e82761843d", 23 | "receiptProof": [ 24 | "0xf891a0ef0fd838fe36ef123e7d5212c81993d7d05773e61fb1009825dc93ff2a7bb6c6a04d301894327a3be444f8cf8b518f45a29c420c858835bf2db992a10402ffb390a0705545458b0eb09f2e49d6e8482e9812806094e2c30dce19ae18662a1aa8a5f58080808080a0c58b4066ae6710227f8a3f3db7c7d3ad75473790e4dad2fba20e07bf3b1069458080808080808080", 25 | "0xf901f180a0f0b254a488797033b0ed3694ab55e47fd65aba8c50ef12fdd9fea98fc35f6a53a0090bd48ec1fd9e78416f942baedd486f550f62bbc39565b5a2862200cd033884a03a215948d6607056865ddb803b977828ff1a019cf731d26734f1c291602d03a9a0717ab8447ae5020c010dab3446fb38a6e065c69726041bdf22d455bdb7195ba4a068c3367ae4a86798f1942698784df7636f9d25cfd7132cbbe4ba29ae584628c1a0d21cb7f0aacbdaec0b2e49285cc7f1a14c69bfe668aa6ce50568bdceb017c694a0f6860a416394ce48721a5aa896fd9b3d3087cd0924aadc766ba66e5f511582f0a09cd9fa7819e18f856df826063e7da0c2c810de3ddd808ab4c377af2afdba43f6a002a790e115843e6aeae22bc933e9c5256e2fa9aedeebcbbc9cb7199b1f48789aa0d10945a11f45f124a737d1fc86581c42bbd0979b83783cc8dcaa039d39986b5aa008704228e9ef59398167370699687ce3cfb20b28dbb498c4455f13a4739ffdbca0058371414d94db5bcc58ec05f5d53d5499ea35d56b7b85094c6be4bf220b4b8ba0dca07dff9c7c7e9f6be9d188a851304a0d1be4f6fce61fee5a25d3cb2c2c08e0a0dbc70eafafedfb47f8cdd177de07a81d84d5a95c1f9159519e1f0375e776d9b5a0fbb4febe7b08678a4878c4190b2052e2b041c191cee4027a34f5a1c091616b8980", 26 | "0xf9027220b9026e02f9026a0183529303b9010000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000200000000000000000000000020000000000000000000000000000000000000040000002000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000100020000000000000000000010000000000000000000000000000000000000000000000000f9015ff9015c942284a1b214d800748159237464de4d236c050377f863a0e5944a34d67c652e0ebf2304b48432aae0b55e40f79ba8a21a4d7054c169ffaca00000000000000000000000000000000000000000000000000000000000000007a074b5ed63531e8eca3e390b0eeff511725f7fa7d5fc4f27a0d90db7976e472bcdb8e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008502000000000000000700000005ded0000e32f8f40414d3ab3a830f735a3553e18e00000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000" 27 | ], 28 | "txIndexRLPEncoded": "0x0f", 29 | "logIndex": 0, 30 | "sourceSlot": 6072504, 31 | "headerRoot": "0xf0882b3a887cf72f876d36860f94726c83321c49a7f3649bf5c23c53fa5b627d" 32 | } -------------------------------------------------------------------------------- /src/libraries/StateProofHelper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import {RLPReader} from "@optimism-bedrock/rlp/RLPReader.sol"; 4 | import {RLPWriter} from "@optimism-bedrock/rlp/RLPWriter.sol"; 5 | import {MerkleTrie} from "@optimism-bedrock/trie/MerkleTrie.sol"; 6 | 7 | library StorageProof { 8 | using RLPReader for RLPReader.RLPItem; 9 | using RLPReader for bytes; 10 | 11 | function getStorageValue(bytes32 slotHash, bytes32 storageRoot, bytes[] memory _stateProof) 12 | internal 13 | pure 14 | returns (uint256) 15 | { 16 | bytes memory valueRlpBytes = 17 | MerkleTrie.get(abi.encodePacked(slotHash), _stateProof, storageRoot); 18 | require(valueRlpBytes.length > 0, "Storage value does not exist"); 19 | return valueRlpBytes.toRLPItem().readUint256(); 20 | } 21 | 22 | function getStorageRoot(bytes[] memory proof, address contractAddress, bytes32 stateRoot) 23 | internal 24 | pure 25 | returns (bytes32) 26 | { 27 | bytes32 addressHash = keccak256(abi.encodePacked(contractAddress)); 28 | bytes memory acctRlpBytes = MerkleTrie.get(abi.encodePacked(addressHash), proof, stateRoot); 29 | require(acctRlpBytes.length > 0, "Account does not exist"); 30 | RLPReader.RLPItem[] memory acctFields = acctRlpBytes.toRLPItem().readList(); 31 | require(acctFields.length == 4); 32 | return bytes32(acctFields[2].readUint256()); 33 | } 34 | } 35 | 36 | library EventProof { 37 | using RLPReader for RLPReader.RLPItem; 38 | using RLPReader for bytes; 39 | 40 | function getEventTopic( 41 | bytes[] memory proof, 42 | bytes32 receiptRoot, 43 | bytes memory key, 44 | uint256 logIndex, 45 | address claimedEmitter, 46 | bytes32 eventSignature, 47 | uint256 topicIndex 48 | ) internal pure returns (bytes32) { 49 | bytes memory value = MerkleTrie.get(key, proof, receiptRoot); 50 | bytes1 txTypeOrFirstByte = value[0]; 51 | 52 | // Currently, there are three possible transaction types on Ethereum. Receipts either come 53 | // in the form "TransactionType | ReceiptPayload" or "ReceiptPayload". The currently 54 | // supported set of transaction types are 0x01 and 0x02. In this case, we must truncate 55 | // the first byte to access the payload. To detect the other case, we can use the fact 56 | // that the first byte of a RLP-encoded list will always be greater than 0xc0. 57 | // Reference 1: https://eips.ethereum.org/EIPS/eip-2718 58 | // Reference 2: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp 59 | uint256 offset; 60 | if (txTypeOrFirstByte == 0x01 || txTypeOrFirstByte == 0x02) { 61 | offset = 1; 62 | } else if (txTypeOrFirstByte >= 0xc0) { 63 | offset = 0; 64 | } else { 65 | revert("Unsupported transaction type"); 66 | } 67 | 68 | // Truncate the first byte if eneded and get the RLP decoding of the receipt. 69 | uint256 ptr; 70 | assembly { 71 | ptr := add(value, 32) 72 | } 73 | RLPReader.RLPItem memory valueAsItem = RLPReader.RLPItem({ 74 | length: value.length - offset, 75 | ptr: RLPReader.MemoryPointer.wrap(ptr + offset) 76 | }); 77 | 78 | // The length of the receipt must be at least four, as the fourth entry contains events 79 | RLPReader.RLPItem[] memory valueAsList = valueAsItem.readList(); 80 | require(valueAsList.length == 4, "Invalid receipt length"); 81 | 82 | // Read the logs from the receipts and check that it is not ill-formed 83 | RLPReader.RLPItem[] memory logs = valueAsList[3].readList(); 84 | require(logIndex < logs.length, "Log index out of bounds"); 85 | RLPReader.RLPItem[] memory relevantLog = logs[logIndex].readList(); 86 | require(relevantLog.length == 3, "Log has incorrect number of fields"); 87 | 88 | // Validate that the correct contract emitted the event 89 | address contractAddress = relevantLog[0].readAddress(); 90 | require(contractAddress == claimedEmitter, "Event was not emitted by claimedEmitter"); 91 | RLPReader.RLPItem[] memory topics = relevantLog[1].readList(); 92 | 93 | // Validate that the correct event was emitted by checking the event signature 94 | require( 95 | bytes32(topics[0].readUint256()) == eventSignature, 96 | "Event signature does not match eventSignature" 97 | ); 98 | 99 | return topics[topicIndex].readBytes32(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/lightclient/LightClientFixture.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | import "forge-std/Base.sol"; 4 | import {SSZ} from "src/libraries/SimpleSerialize.sol"; 5 | import { 6 | LightClient, 7 | Groth16Proof, 8 | LightClientStep, 9 | LightClientRotate 10 | } from "src/lightclient/LightClient.sol"; 11 | 12 | /// @notice Helper contract for parsing the JSON fixture, and converting them to the correct types. 13 | /// @dev The weird ordering here is because vm.parseJSON require alphabetical ordering of the 14 | /// fields in the struct, and odd types with conversions are due to the way the JSON is 15 | /// handled. 16 | contract LightClientFixture is CommonBase { 17 | struct Fixture { 18 | Initial initial; 19 | Rotate rotate; 20 | Step step; 21 | } 22 | 23 | struct Initial { 24 | bytes genesisTime; 25 | bytes32 genesisValidatorsRoot; 26 | uint256 secondsPerSlot; 27 | uint256 slotsPerPeriod; 28 | uint256 syncCommitteePeriod; 29 | string syncCommitteePoseidon; 30 | } 31 | 32 | struct Step { 33 | string[] a; 34 | uint256 attestedSlot; 35 | string[][] b; 36 | string[] c; 37 | bytes32 executionStateRoot; 38 | bytes32 finalizedHeaderRoot; 39 | uint256 finalizedSlot; 40 | string[] inputs; 41 | string participation; 42 | } 43 | 44 | struct Rotate { 45 | string[] a; 46 | string[][] b; 47 | string[] c; 48 | string syncCommitteePoseidon; 49 | bytes32 syncCommitteeSSZ; 50 | } 51 | 52 | function newLightClient(Initial memory initial, uint32 sourceChainId, uint16 finalityThreshold) 53 | public 54 | returns (LightClient) 55 | { 56 | return new LightClient( 57 | initial.genesisValidatorsRoot, 58 | strToUint(string(initial.genesisTime)), 59 | initial.secondsPerSlot, 60 | initial.slotsPerPeriod, 61 | initial.syncCommitteePeriod, 62 | SSZ.toLittleEndian(strToUint(initial.syncCommitteePoseidon)), 63 | sourceChainId, 64 | finalityThreshold 65 | ); 66 | } 67 | 68 | function convertToGroth16Proof(Step memory step) public pure returns (Groth16Proof memory) { 69 | uint256[2] memory a = [strToUint(step.a[0]), strToUint(step.a[1])]; 70 | uint256[2][2] memory b = [ 71 | [strToUint(step.b[0][1]), strToUint(step.b[0][0])], 72 | [strToUint(step.b[1][1]), strToUint(step.b[1][0])] 73 | ]; 74 | uint256[2] memory c = [strToUint(step.c[0]), strToUint(step.c[1])]; 75 | 76 | return Groth16Proof(a, b, c); 77 | } 78 | 79 | function convertToGroth16Proof(Rotate memory rotate) 80 | public 81 | pure 82 | returns (Groth16Proof memory) 83 | { 84 | uint256[2] memory a = [strToUint(rotate.a[0]), strToUint(rotate.a[1])]; 85 | uint256[2][2] memory b = [ 86 | [strToUint(rotate.b[0][1]), strToUint(rotate.b[0][0])], 87 | [strToUint(rotate.b[1][1]), strToUint(rotate.b[1][0])] 88 | ]; 89 | uint256[2] memory c = [strToUint(rotate.c[0]), strToUint(rotate.c[1])]; 90 | 91 | return Groth16Proof(a, b, c); 92 | } 93 | 94 | function convertToLightClientStep(Step memory step) 95 | public 96 | pure 97 | returns (LightClientStep memory) 98 | { 99 | return LightClientStep( 100 | step.attestedSlot, 101 | step.finalizedSlot, 102 | strToUint(step.participation), 103 | step.finalizedHeaderRoot, 104 | step.executionStateRoot, 105 | convertToGroth16Proof(step) 106 | ); 107 | } 108 | 109 | function convertToLightClientRotate(Step memory step, Rotate memory rotate) 110 | public 111 | pure 112 | returns (LightClientRotate memory) 113 | { 114 | return LightClientRotate( 115 | convertToLightClientStep(step), 116 | rotate.syncCommitteeSSZ, 117 | SSZ.toLittleEndian(strToUint(rotate.syncCommitteePoseidon)), 118 | convertToGroth16Proof(rotate) 119 | ); 120 | } 121 | 122 | function strToUint(string memory str) internal pure returns (uint256 res) { 123 | for (uint256 i = 0; i < bytes(str).length; i++) { 124 | if ((uint8(bytes(str)[i]) - 48) < 0 || (uint8(bytes(str)[i]) - 48) > 9) { 125 | revert(); 126 | } 127 | res += (uint8(bytes(str)[i]) - 48) * 10 ** (bytes(str).length - i - 1); 128 | } 129 | 130 | return res; 131 | } 132 | } 133 | --------------------------------------------------------------------------------