├── 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 | | Severity |
17 | Description |
18 | Bounty |
19 |
20 |
21 | | Critical |
22 |
23 |
24 | - Incorrect light client state
25 | - Incorrect message passing
26 | - Permanent freezing of the protocol
27 |
28 | |
29 | Up to $100,000 |
30 |
31 |
32 | | High |
33 |
34 |
35 | - Temporary freezing of the protocol
36 |
37 | |
38 | Up to $25,000 |
39 |
40 |
41 |
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 |
--------------------------------------------------------------------------------