├── .coderabit.yml ├── .github ├── README.md ├── dependabot.yaml └── workflows │ ├── bindings.yml │ ├── foundry.yml │ ├── lint.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .solhint.json ├── .solhintignore ├── .tool-versions ├── LICENSE ├── Makefile ├── README.md ├── bindings └── go │ ├── channel │ ├── ProtoChannel.go │ └── ProtoCounterparty.go │ ├── crossl2prover │ └── CrossL2Prover.go │ ├── dispatcher │ └── Dispatcher.go │ ├── dummylightclient │ └── DummyLightClient.go │ ├── dummyproofverifier │ └── DummyProofVerifier.go │ ├── earth │ └── Earth.go │ ├── erc1967proxy │ └── ERC1967Proxy.go │ ├── feevault │ └── FeeVault.go │ ├── ibcdispatcher │ ├── IbcDispatcher.go │ ├── IbcEventsEmitter.go │ └── IbcPacketSender.go │ ├── ibcutils │ └── IbcUtils.go │ ├── icrossl2prover │ └── ICrossL2Prover.go │ ├── idispatcher │ └── IDispatcher.go │ ├── ifeevault │ └── IFeeVault.go │ ├── ilightclient │ ├── IClientUpdates.go │ └── ILightClient.go │ ├── iproofverifier │ ├── IAppStateVerifier.go │ └── IProofVerifier.go │ ├── iuniversalchannelhandler │ └── IUniversalChannelHandler.go │ ├── mars │ ├── Mars.go │ ├── PanickingMars.go │ ├── RevertingBytesMars.go │ ├── RevertingEmptyMars.go │ ├── RevertingStringCloseChannelMars.go │ └── RevertingStringMars.go │ ├── moon │ └── Moon.go │ ├── optimisticlightclient │ └── OptimisticLightClient.go │ ├── optimisticproofverifier │ └── OptimisticProofVerifier.go │ ├── sequencersignatureverifier │ └── SequencerSignatureVerifier.go │ ├── sequencersoloclient │ └── SequencerSoloClient.go │ ├── universalchannelhandler │ └── UniversalChannelHandler.go │ └── venus │ └── Venus.go ├── bun.lockb ├── contracts ├── base │ ├── AppStateVerifier.sol │ └── GeneralMiddleware.sol ├── core │ ├── Dispatcher.sol │ ├── FeeVault.sol │ ├── OptimisticLightClient.sol │ ├── OptimisticProofVerifier.sol │ ├── SequencerSignatureVerifier.sol │ ├── SequencerSoloClient.sol │ ├── UniversalChannelHandler.sol │ └── proofAPI │ │ └── CrossL2Prover.sol ├── examples │ ├── Earth.sol │ ├── Mars.sol │ ├── Moon.sol │ ├── Pluto.sol │ └── Venus.sol ├── implementation_templates │ ├── FeeSender.sol │ └── IbcReceiverUpgradeable.sol ├── interfaces │ ├── ICrossL2Prover.sol │ ├── IDispatcher.sol │ ├── IFeeVault.sol │ ├── ILightClient.sol │ ├── IOptimisticLightClient.sol │ ├── IProofVerifier.sol │ ├── ISignatureVerifier.sol │ ├── IUniversalChannelHandler.sol │ ├── IbcDispatcher.sol │ ├── IbcMiddleware.sol │ └── IbcReceiver.sol ├── libs │ ├── Ibc.sol │ ├── IbcErrors.sol │ ├── IbcUtils.sol │ └── PolymerProofs.sol └── utils │ ├── DummyLightClient.sol │ ├── DummyProofVerifier.sol │ └── GasAudit.sol ├── diagrams ├── fee-design.jpg └── vibcContractsOverview.jpg ├── docs └── universal-channel-middleware.md ├── eslint.config.js ├── foundry.toml ├── go.mod ├── go.sum ├── lib └── proto │ ├── GoogleProtobufAny.sol │ ├── ProtoBufRuntime.sol │ └── channel.sol ├── package-lock.json ├── package.json ├── proto ├── channel.proto └── generate.sh ├── remappings.txt ├── script ├── Counter.s.sol ├── Deploy.s.sol └── README.md ├── specs ├── evm.accounts.yaml └── update.spec.yaml ├── src ├── deploy.ts ├── evm │ ├── chain.ts │ ├── contracts │ │ ├── CrossL2Prover.ts │ │ ├── Dispatcher.ts │ │ ├── DummyLightClient.ts │ │ ├── DummyProofVerifier.ts │ │ ├── ERC1967Proxy.ts │ │ ├── Earth.ts │ │ ├── FeeVault.ts │ │ ├── ICrossL2Prover.ts │ │ ├── IDispatcher.ts │ │ ├── IFeeVault.ts │ │ ├── ILightClient.sol │ │ │ ├── IClientUpdates.ts │ │ │ ├── ILightClient.ts │ │ │ └── index.ts │ │ ├── IProofVerifier.sol │ │ │ ├── IAppStateVerifier.ts │ │ │ ├── IProofVerifier.ts │ │ │ └── index.ts │ │ ├── IUniversalChannelHandler.ts │ │ ├── Ibc.ts │ │ ├── IbcDispatcher.sol │ │ │ ├── IbcDispatcher.ts │ │ │ ├── IbcEventsEmitter.ts │ │ │ ├── IbcPacketSender.ts │ │ │ └── index.ts │ │ ├── IbcUtils.ts │ │ ├── Mars.sol │ │ │ ├── Mars.ts │ │ │ ├── PanickingMars.ts │ │ │ ├── RevertingBytesMars.ts │ │ │ ├── RevertingEmptyMars.ts │ │ │ ├── RevertingStringCloseChannelMars.ts │ │ │ ├── RevertingStringMars.ts │ │ │ └── index.ts │ │ ├── Moon.ts │ │ ├── OptimisticLightClient.ts │ │ ├── OptimisticProofVerifier.ts │ │ ├── SequencerSignatureVerifier.ts │ │ ├── SequencerSoloClient.ts │ │ ├── UniversalChannelHandler.ts │ │ ├── Venus.ts │ │ ├── common.ts │ │ ├── factories │ │ │ ├── CrossL2Prover__factory.ts │ │ │ ├── Dispatcher__factory.ts │ │ │ ├── DummyLightClient__factory.ts │ │ │ ├── DummyProofVerifier__factory.ts │ │ │ ├── ERC1967Proxy__factory.ts │ │ │ ├── Earth__factory.ts │ │ │ ├── FeeVault__factory.ts │ │ │ ├── ICrossL2Prover__factory.ts │ │ │ ├── IDispatcher__factory.ts │ │ │ ├── IFeeVault__factory.ts │ │ │ ├── ILightClient.sol │ │ │ │ ├── IClientUpdates__factory.ts │ │ │ │ ├── ILightClient__factory.ts │ │ │ │ └── index.ts │ │ │ ├── IProofVerifier.sol │ │ │ │ ├── IAppStateVerifier__factory.ts │ │ │ │ ├── IProofVerifier__factory.ts │ │ │ │ └── index.ts │ │ │ ├── IUniversalChannelHandler__factory.ts │ │ │ ├── IbcDispatcher.sol │ │ │ │ ├── IbcDispatcher__factory.ts │ │ │ │ ├── IbcEventsEmitter__factory.ts │ │ │ │ ├── IbcPacketSender__factory.ts │ │ │ │ └── index.ts │ │ │ ├── IbcUtils__factory.ts │ │ │ ├── Ibc__factory.ts │ │ │ ├── Mars.sol │ │ │ │ ├── Mars__factory.ts │ │ │ │ ├── PanickingMars__factory.ts │ │ │ │ ├── RevertingBytesMars__factory.ts │ │ │ │ ├── RevertingEmptyMars__factory.ts │ │ │ │ ├── RevertingStringCloseChannelMars__factory.ts │ │ │ │ ├── RevertingStringMars__factory.ts │ │ │ │ └── index.ts │ │ │ ├── Moon__factory.ts │ │ │ ├── OptimisticLightClient__factory.ts │ │ │ ├── OptimisticProofVerifier__factory.ts │ │ │ ├── SequencerSignatureVerifier__factory.ts │ │ │ ├── SequencerSoloClient__factory.ts │ │ │ ├── UniversalChannelHandler__factory.ts │ │ │ ├── Venus__factory.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── index.ts │ └── schemas │ │ ├── account.ts │ │ ├── contract.ts │ │ ├── contractUpdate.ts │ │ ├── index.ts │ │ ├── multisig.ts │ │ ├── sendingAccount.ts │ │ ├── tx.ts │ │ └── wallet.ts ├── index.ts ├── multisig │ └── safe.ts ├── scripts │ ├── deploy-multisig.ts │ ├── execute-multisig-tx.ts │ ├── fork-deployment-test.ts │ ├── update-contracts-script.ts │ └── verify-contract-script.ts ├── tx.ts ├── updateContract.ts └── utils │ ├── cli.ts │ ├── constants.ts │ ├── index.ts │ ├── io.ts │ └── registry.ts ├── test ├── Dispatcher.gasGriefing.t.sol ├── Dispatcher │ ├── Dispatcher.ack.sol │ ├── Dispatcher.client.t.sol │ ├── Dispatcher.closeChannel.t.sol │ ├── Dispatcher.dappHandlerRevert.t.sol │ ├── Dispatcher.multiclient.sol │ ├── Dispatcher.proof.t.sol │ ├── Dispatcher.t.sol │ └── Dispatcher.timeout.t.sol ├── FeeVault.t.sol ├── Fork │ ├── Dispatcher.deploy.t.sol │ ├── contract-spec.yaml │ └── deploy.sh ├── Ibc.t.sol ├── OpConsensusStateManager.t.sol ├── SequencerSoloClient.t.sol ├── SequencerVerifier.t.sol ├── Verifier.t.sol ├── VirtualChain.sol ├── mocks │ └── GasUsingMars.sol ├── payload │ ├── ack.t.sol │ ├── channel_ack_pending_proof.hex │ ├── channel_ack_pending_sequencer_proof.hex │ ├── channel_confirm_pending_proof.hex │ ├── channel_confirm_pending_prooftest.hex │ ├── channel_confirm_pending_sequencer_proof.hex │ ├── channel_try_pending_proof.hex │ ├── channel_try_pending_sequencer_proof.hex │ ├── l1_block_0x1235261.hex │ ├── l1_block_0x4df537.hex │ ├── l1_block_0x4df537.json │ ├── l1_block_0x61a80.json │ ├── l1_block_ancestor.json │ ├── l1_block_child.json │ ├── l2_block_0x4b0.json │ ├── output_at_block_l1_0x4df537_with_proof.json │ ├── packet_ack_proof.hex │ └── packet_commitment_proof.hex ├── proofApi │ └── CrossL2EventProposer.t.sol ├── universal.channel.t.sol ├── upgradeableProxy │ ├── DispatcherUUPS.accessControl.t.sol │ ├── UpgradeUtils.t.sol │ └── upgrades │ │ ├── DispatcherV2.sol │ │ ├── DispatcherV2Initializable.sol │ │ └── UCHV2.sol └── utils │ ├── Dispatcher.base.t.sol │ ├── Proof.base.t.sol │ ├── Signing.base.t.sol │ ├── TestUtils.t.sol │ ├── extract.ts │ ├── goHelpers │ ├── cmd │ │ ├── ethCallVerify │ │ │ └── main.go │ │ └── receiptMMPTProof │ │ │ ├── EthProof.go │ │ │ ├── main.go │ │ │ └── receiptMMPTProof │ ├── go.mod │ └── go.sum │ └── query-contract.sh ├── tsconfig.json └── tsup.config.ts /.coderabit.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json 2 | language: "en-US" 3 | early_access: false 4 | reviews: 5 | profile: "chill" 6 | request_changes_workflow: false 7 | high_level_summary: true 8 | poem: true 9 | review_status: true 10 | collapse_walkthrough: false 11 | auto_review: 12 | enabled: true 13 | drafts: false 14 | path_instructions: 15 | - path: "./bindings" 16 | instructions: | 17 | Ignore all the files in this directory recursively from any form of review. 18 | - path: "./src/evm/contracts" 19 | instructions: | 20 | Ignore all the files in this directory recursively from any form of review. 21 | chat: 22 | auto_reply: true 23 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/bindings.yml: -------------------------------------------------------------------------------- 1 | name: Validate Bindings 2 | 3 | on: 4 | pull_request: 5 | branches: ["main"] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | verify-bindings: 13 | runs-on: macos-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | submodules: "recursive" 19 | 20 | - name: Setup Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version: "1.21.3" 24 | 25 | - name: Setup Foundry 26 | uses: foundry-rs/foundry-toolchain@v1.2.0 27 | 28 | - name: Install Bun 29 | uses: oven-sh/setup-bun@v2 30 | - name: Install ABIGen 31 | run: | 32 | go install github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 33 | 34 | - name: Generate Bindings 35 | run: | 36 | bun install 37 | bun run build 38 | 39 | - name: Check for Bindings Changes 40 | run: | 41 | changes=$( git status --porcelain -- ./bindings ./src/evm/contracts ) 42 | if [ -n "$changes" ]; then 43 | echo -e "Changes in Generated Bindings:\n$changes" 44 | exit 1 45 | fi 46 | - name: Upload ts bindings artifact on error 47 | if: failure() 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: ts-bindings 51 | path: ./src/evm/contracts/ 52 | - name: Upload go bindings artifact on error 53 | if: failure() 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: go-bindings 57 | path: ./bindings/ 58 | -------------------------------------------------------------------------------- /.github/workflows/foundry.yml: -------------------------------------------------------------------------------- 1 | name: Foundry 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | 21 | - name: Install Foundry 22 | uses: foundry-rs/foundry-toolchain@v1 23 | 24 | - name: Install Bun 25 | uses: oven-sh/setup-bun@v2 26 | 27 | - name: Generate Bindings 28 | run: | 29 | bun install 30 | bun run build 31 | 32 | - name: Check contract sizes 33 | run: forge build --sizes --deny-warnings 34 | 35 | - name: Tests 36 | run: forge test --no-match-contract DispatcherDeployTest 37 | - name: set Owner Address 38 | run: echo "OwnerAddress=$(cast wallet address ${{ secrets.DUMMY_DEPLOYER_PRIVATE_KEY }})" >> $GITHUB_ENV 39 | - name: Fork Deploy Test 40 | run: npx vibc-core-deploy-test 41 | env: 42 | MODULE_ROOT_PATH: "./" 43 | RPC_URL: ${{ secrets.FORK_RPC_URL }} 44 | CHAIN_NAME: "fork-test-ci" 45 | DUMMY_DEPLOYER_PRIVATE_KEY: ${{ secrets.DUMMY_DEPLOYER_PRIVATE_KEY }} 46 | DAPP_PRIVATE_KEY_1: ${{ secrets.DAPP_PRIVATE_KEY_1 }} 47 | DAPP_PRIVATE_KEY_2: ${{ secrets.DAPP_PRIVATE_KEY_2 }} 48 | DAPP_PRIVATE_KEY_3: ${{ secrets.DAPP_PRIVATE_KEY_3 }} 49 | PolymerL2OutputOracleProxyAddress: "0xB901B810B30f4d8D179FA5e4dFA73B6EC81f2dB0" 50 | L2OutputOracleProxyAddress: "0xB901B810B30f4d8D179FA5e4dFA73B6EC81f2dB0" 51 | L1BlockAddress: "0x4200000000000000000000000000000000000015" -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | submodules: "recursive" 20 | 21 | - name: Setup Go 22 | uses: actions/setup-go@v5 23 | with: 24 | go-version: "1.21.3" 25 | 26 | - name: Setup Foundry 27 | uses: foundry-rs/foundry-toolchain@v1 28 | 29 | - name: Install ABIGen 30 | run: | 31 | go install github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 32 | 33 | - name: Install Bun 34 | uses: oven-sh/setup-bun@v2 35 | 36 | - name: Install Dependencies 37 | run: | 38 | bun install 39 | bun run lint 40 | 41 | - name: Check formatting 42 | run: forge fmt --check 43 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | artifacts: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | submodules: 'recursive' 21 | 22 | - name: Install Foundry 23 | uses: foundry-rs/foundry-toolchain@v1 24 | 25 | - name: Tests 26 | run: forge test --no-match-contract DispatcherDeployTest 27 | 28 | - name: Check contract sizes 29 | run: rm -rf out && forge build contracts/ --sizes --deny-warnings 30 | 31 | - name: Save compiled contracts and ABIs 32 | if: success() 33 | run: | 34 | mkdir -p release-artifacts 35 | cp -r out release-artifacts 36 | 37 | - name: Upload artifacts 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: compiled-contracts 41 | path: release-artifacts 42 | 43 | release: 44 | runs-on: ubuntu-latest 45 | needs: artifacts 46 | environment: release 47 | permissions: 48 | contents: write 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | fetch-depth: 0 54 | submodules: "recursive" 55 | 56 | - name: Download artifacts 57 | uses: actions/download-artifact@v4 58 | with: 59 | name: compiled-contracts 60 | path: ./release-artifacts 61 | 62 | - name: Create Release 63 | uses: softprops/action-gh-release@v2 64 | if: startsWith(github.ref, 'refs/tags/') 65 | with: 66 | files: release-artifacts/** 67 | generate_release_notes: true 68 | draft: false 69 | prerelease: false 70 | token: ${{ secrets.RELEASE_TOKEN }} 71 | 72 | registries: 73 | runs-on: ubuntu-latest 74 | needs: release 75 | environment: release 76 | steps: 77 | - name: Checkout 78 | uses: actions/checkout@v4 79 | with: 80 | fetch-depth: 0 81 | submodules: "recursive" 82 | 83 | - name: Get Latest Version 84 | id: version 85 | uses: pozetroninc/github-action-get-latest-release@master 86 | with: 87 | repository: ${{ github.repository }} 88 | 89 | - name: Setup Go 90 | uses: actions/setup-go@v5 91 | with: 92 | go-version: '1.21.3' 93 | 94 | - name: Setup Foundry 95 | uses: foundry-rs/foundry-toolchain@v1 96 | 97 | - name: Install ABIGen 98 | run: | 99 | go install github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 100 | 101 | - uses: actions/setup-node@v4 102 | with: 103 | node-version: '22.3.0' 104 | registry-url: 'https://registry.npmjs.org' 105 | 106 | - name: Install Bun 107 | uses: oven-sh/setup-bun@v2 108 | 109 | # Ensure /v4 is updated as the major version changes 110 | - name: Update Go Registry 111 | run: | 112 | GOPROXY=proxy.golang.org go list \ 113 | -m github.com/open-ibc/vibc-core-smart-contracts/v4@${{ steps.version.outputs.release }} 114 | 115 | - name: Update NPM Registry 116 | run: | 117 | bun install 118 | bun run build 119 | npm publish 120 | env: 121 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | dist/ 8 | 9 | #Hardhat files 10 | cache 11 | artifacts 12 | 13 | # Foundry files 14 | out/ 15 | forge-cache/ 16 | 17 | *.tgz 18 | src/contracts.template.ts 19 | 20 | .idea/ 21 | .direnv/ 22 | forge-cache/ 23 | 24 | node_modules 25 | .env 26 | coverage 27 | coverage.json 28 | typechain 29 | typechain-types 30 | .tmp_abi_vibc 31 | 32 | #Hardhat files 33 | cache 34 | artifacts 35 | 36 | # Coverage Report 37 | report/ 38 | lcov.info 39 | -------------------------------------------------------------------------------- /.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/protobuf3-solidity-lib"] 8 | path = lib/protobuf3-solidity-lib 9 | url = https://github.com/celestiaorg/protobuf3-solidity-lib 10 | [submodule "lib/optimism"] 11 | path = lib/optimism 12 | url = https://github.com/ethereum-optimism/optimism.git 13 | [submodule "lib/base64"] 14 | path = lib/base64 15 | url = https://github.com/Brechtpd/base64.git 16 | [submodule "lib/openzeppelin-contracts-upgradeable"] 17 | path = lib/openzeppelin-contracts-upgradeable 18 | url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # specific to npm package 2 | deployments 3 | 4 | # Auto generated code Go and TypeScript bindings 5 | bindings/ 6 | 7 | .coderabit.yml 8 | node_modules 9 | .env 10 | coverage 11 | coverage.json 12 | typechain 13 | typechain-types 14 | dist/ 15 | 16 | #Hardhat files 17 | cache 18 | artifacts 19 | 20 | # Foundry files 21 | forge-cache/ 22 | 23 | *.tgz 24 | src/contracts.template.ts 25 | 26 | .idea/ 27 | .direnv/ 28 | forge-cache/ 29 | 30 | bun.lockb 31 | node_modules 32 | .env 33 | coverage 34 | coverage.json 35 | typechain 36 | typechain-types 37 | 38 | #Hardhat files 39 | cache 40 | artifacts 41 | 42 | # Coverage Report 43 | report 44 | lcov.info 45 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:all", 3 | "rules":{ 4 | "compiler-version": ["error", "^0.8.0"], 5 | "const-name-snakecase": "error", 6 | "constructor-syntax": "error", 7 | "no-global-import" : ["error"], 8 | "no-unused-import":["error"], 9 | "custom-errors": ["error"], 10 | "no-inline-assembly": "off", 11 | "named-return-values": ["error"], 12 | "private-vars-leading-underscore": ["error"], 13 | "ordering": ["error"], 14 | "func-visibility": [ 15 | "error", 16 | { 17 | "ignoreConstructors": true 18 | } 19 | 20 | ], 21 | "max-line-length": ["error", 160] 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | contracts/examples/MarsRc4.sol -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | golang 1.21.3 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .SILENT: 2 | 3 | # Hardcoded for simplicity 4 | CONTRACT_NAMES = channel \ 5 | CrossL2Prover \ 6 | Dispatcher \ 7 | DummyLightClient \ 8 | DummyProofVerifier \ 9 | Earth \ 10 | ERC1967Proxy \ 11 | FeeVault \ 12 | Ibc \ 13 | IbcDispatcher \ 14 | IbcUtils \ 15 | ICrossL2Prover \ 16 | IDispatcher \ 17 | IFeeVault \ 18 | ILightClient \ 19 | IProofVerifier \ 20 | IUniversalChannelHandler \ 21 | Mars \ 22 | Moon \ 23 | Venus \ 24 | OptimisticLightClient \ 25 | OptimisticProofVerifier \ 26 | SequencerSoloClient \ 27 | SequencerSignatureVerifier \ 28 | UniversalChannelHandler 29 | 30 | # Create the pattern for each contract 31 | CONTRACT_ABI_PATTERNS = $(addsuffix .sol/*.abi.json,$(addprefix ./out/,$(CONTRACT_NAMES))) 32 | CONTRACT_JSON_PATTERNS := $(addsuffix .sol/*.json,$(addprefix ./out/,$(CONTRACT_NAMES))) 33 | 34 | # Use wildcard to expand each pattern 35 | CONTRACT_ABI_FILES = $(foreach pattern,$(CONTRACT_ABI_PATTERNS),$(wildcard $(pattern))) 36 | CONTRACT_BOTH_FILES = $(foreach pattern,$(CONTRACT_JSON_PATTERNS),$(wildcard $(pattern))) 37 | CONTRACT_JSON_FILES = $(filter-out $(CONTRACT_ABI_FILES),$(CONTRACT_BOTH_FILES)) 38 | 39 | .PHONY: build-contracts bindings-gen-go bindings-gen-ts 40 | 41 | build-contracts: 42 | echo "Building contracts"; \ 43 | rm -frd ./out; \ 44 | forge install; \ 45 | forge build --skip test script -C contracts \ 46 | --lib-paths lib \ 47 | --extra-output-files abi --force 48 | 49 | # Libraries do not generate the correct ABI code (ChannelState enum causes errors) 50 | # So the Ibc.sol abi generated code from abigen throws errors but is not needed. 51 | # This is because the types exported are included in the ABIs of contracts and 52 | # correctly interpreted (enums -> uint8 etc), the library methods are used inside 53 | # of other contract methods and thus bindings for them do not need to be generated 54 | # as they are not publicly exposed, but rather used within the contract itself. 55 | # 56 | # ABIGen issue ref: https://github.com/ethereum/solidity/issues/9278 57 | bindings-gen-go: build-contracts 58 | echo "Generating Go vIBC bindings..."; \ 59 | rm -rfd ./bindings/go/* ; \ 60 | for abi_file in $(CONTRACT_ABI_FILES); do \ 61 | abi_base=$$(basename $$(dirname $$abi_file)); \ 62 | if [ "$$abi_base" = "Ibc.sol" ]; then \ 63 | continue; \ 64 | fi; \ 65 | type=$$(basename $$abi_file .abi.json); \ 66 | pkg=$$(basename $$abi_base .sol | tr "[:upper:]" "[:lower:]"); \ 67 | mkdir -p ./bindings/go/$$pkg; \ 68 | abigen --abi $$abi_file --pkg $$pkg --type $$type --out ./bindings/go/$$pkg/$$type.go; \ 69 | done; \ 70 | echo "Done." 71 | 72 | bindings-gen-ts: build-contracts 73 | echo "Generating TypeScript bindings..."; \ 74 | rm -rfd ./src/evm/contracts/*; \ 75 | typechain --target ethers-v6 --out-dir ./src/evm/contracts $(CONTRACT_JSON_FILES); \ 76 | echo "Done." 77 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-ibc/vibc-core-smart-contracts/e463191080973d50bf48c2bddcb34be310228445/bun.lockb -------------------------------------------------------------------------------- /contracts/base/AppStateVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity 0.8.15; 19 | 20 | import {RLPReader} from "optimism/libraries/rlp/RLPReader.sol"; 21 | import {IAppStateVerifier, Ics23Proof, OpIcs23Proof} from "../interfaces/IProofVerifier.sol"; 22 | 23 | /** 24 | * @title OptimisticProofVerifier 25 | * @notice Verifies proofs related to Optimistic Rollup state updates 26 | * @author Polymer Labs 27 | */ 28 | abstract contract AppStateVerifier is IAppStateVerifier { 29 | using RLPReader for RLPReader.RLPItem; 30 | using RLPReader for bytes; 31 | 32 | /** 33 | * @dev Prove that a given state is not part of a proof 34 | * @dev this method is mainly used for packet timeouts, which is currently not implemented 35 | */ 36 | function verifyNonMembership(bytes32, bytes calldata, Ics23Proof calldata) external pure { 37 | revert MethodNotImplemented(); 38 | } 39 | 40 | /** 41 | * @inheritdoc IAppStateVerifier 42 | * @dev verifies a chain of ICS23 proofs 43 | * Each computed subroot starting from index 0 must match the value of the next proof (hence chained proofs). 44 | * The cosmos SDK and ics23 support chained proofs to switch between different proof specs. 45 | * Custom proof specs are not supported here. Only Iavl and Tendermint or similar proof specs are supported. 46 | */ 47 | function verifyMembership(bytes32 appHash, bytes memory key, bytes memory value, Ics23Proof calldata proofs) 48 | public 49 | pure 50 | { 51 | // first check that the provided proof indeed proves the keys and values. 52 | if (keccak256(key) != keccak256(proofs.proof[0].key)) { 53 | revert InvalidProofKey(); 54 | } 55 | if (keccak256(value) != keccak256(proofs.proof[0].value)) revert InvalidProofValue(); 56 | // proofs are chained backwards. First proof in the list (proof[0]) corresponds to the packet proof, meaning 57 | // that can be checked against the next subroot value (i.e. ibc root). Once the first proof is verified, 58 | // we can check the second that corresponds to the ibc proof, that is checked against the app hash (app root) 59 | if (bytes32(proofs.proof[1].value) != _verify(proofs.proof[0])) revert InvalidPacketProof(); 60 | if (appHash != _verify(proofs.proof[1])) revert InvalidIbcStateProof(); 61 | } 62 | 63 | /** 64 | * @dev Verifies an ICS23 proof through the root hash based on the provided proof. 65 | * @dev This code was adapted from the ICS23 membership verification found here: 66 | * https://github.com/cosmos/ics23/blob/go/v0.10.0/go/ics23.go#L36 67 | * @param proof The ICS23 proof to be verified. 68 | * @return computed The computed root hash. 69 | */ 70 | function _verify(OpIcs23Proof calldata proof) internal pure returns (bytes32 computed) { 71 | bytes32 hashedData = sha256(proof.value); 72 | computed = sha256( 73 | abi.encodePacked( 74 | proof.prefix, _encodeVarint(proof.key.length), proof.key, _encodeVarint(hashedData.length), hashedData 75 | ) 76 | ); 77 | 78 | for (uint256 i = 0; i < proof.path.length; i++) { 79 | computed = sha256(abi.encodePacked(proof.path[i].prefix, computed, proof.path[i].suffix)); 80 | } 81 | } 82 | 83 | /** 84 | * @dev Encodes an integer value into a variable-length integer format. 85 | * @param value The integer value to be encoded. 86 | * @return encoded The encoded bytes array. 87 | */ 88 | function _encodeVarint(uint256 value) internal pure returns (bytes memory encoded) { 89 | bytes memory result; 90 | while (value >= 0x80) { 91 | bytes.concat(result, bytes1(uint8((value & 0x7F) | 0x80))); 92 | value >>= 7; 93 | } 94 | encoded = bytes.concat(result, bytes1(uint8(value))); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /contracts/core/SequencerSignatureVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity 0.8.15; 19 | 20 | import {RLPReader} from "optimism/libraries/rlp/RLPReader.sol"; 21 | import {ISignatureVerifier} from "../interfaces/ISignatureVerifier.sol"; 22 | import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 23 | import {AppStateVerifier} from "../base/AppStateVerifier.sol"; 24 | 25 | /** 26 | * @title SequencerSignatureVerifier 27 | * @notice Verifies ECDSA signatures from a sequencer for client updates. Is used by the SequencerSoloClient to verify 28 | * signatures on client updates. 29 | * @author Polymer Labs 30 | */ 31 | contract SequencerSignatureVerifier is AppStateVerifier, ISignatureVerifier { 32 | using RLPReader for RLPReader.RLPItem; 33 | using RLPReader for bytes; 34 | 35 | address public immutable SEQUENCER; // The trusted sequencer address that polymer p2p signer holds the private key 36 | // to 37 | bytes32 public immutable CHAIN_ID; // Chain ID of the L2 chain for which the sequencer signs over 38 | 39 | constructor(address sequencer_, bytes32 chainId_) { 40 | SEQUENCER = sequencer_; 41 | CHAIN_ID = chainId_; 42 | } 43 | 44 | /** 45 | * @notice Verifies that the sequencer signature is valid for a given l1 origin. This is used by the 46 | * SequencerSoloClient update client method. 47 | * @param l2BlockNumber The block number of the L2 block that the state update is for 48 | * @param appHash The app hash of the state update to be saved in the parent soloClient contract 49 | * @param l1BlockHash The hash of the L1 origin that the peptide height corresponds to 50 | * @param signature The sequencer's ECDSA over the state update 51 | */ 52 | function verifyStateUpdate(uint256 l2BlockNumber, bytes32 appHash, bytes32 l1BlockHash, bytes calldata signature) 53 | external 54 | view 55 | { 56 | _verifySequencerSignature(l2BlockNumber, appHash, l1BlockHash, signature); 57 | } 58 | 59 | /** 60 | * @notice Verify the ECDSA signature of the sequencer over the given l2BLockNumber, peptideAppHash, and origin 61 | * l1BlockHash 62 | */ 63 | function _verifySequencerSignature( 64 | uint256 l2BlockNumber, 65 | bytes32 appHash, 66 | bytes32 l1BlockHash, 67 | bytes calldata signature 68 | ) internal view { 69 | // Signature will be the keccak256 hash of a + b, where: 70 | // a = (empty bytes 32 corresponding to the Domain) + ( peptide chain Id ) 71 | // b = keccak((l2 block number ) + ( l2 apphash) + ( l1 origin hash)) 72 | if ( 73 | ECDSA.recover( 74 | keccak256( 75 | bytes.concat( 76 | bytes32(0), CHAIN_ID, keccak256(bytes.concat(bytes32(l2BlockNumber), appHash, l1BlockHash)) 77 | ) 78 | ), 79 | signature 80 | ) != SEQUENCER 81 | ) { 82 | revert InvalidSequencerSignature(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /contracts/examples/Moon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.9; 19 | 20 | import {Mars} from "./Mars.sol"; 21 | import {IbcDispatcher} from "../interfaces/IbcDispatcher.sol"; 22 | 23 | /** 24 | * @title Moon 25 | * @notice Moon is a simple IBC receiver contract that receives packets and sends acks. 26 | * For now, it is a copy of Mars.sol, but may be extended in the future. 27 | * @dev This contract is used for only testing IBC functionality and as an example for dapp developers 28 | * on how to integrate with the vibc protocol. 29 | */ 30 | contract Moon is Mars { 31 | constructor(IbcDispatcher _dispatcher) Mars(_dispatcher) {} 32 | } 33 | -------------------------------------------------------------------------------- /contracts/examples/Pluto.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.9; 19 | 20 | import {AckPacket} from "../libs/Ibc.sol"; 21 | import {Mars} from "./Mars.sol"; 22 | import {IbcPacket} from "../interfaces/IbcReceiver.sol"; 23 | import {IbcDispatcher} from "../interfaces/IbcDispatcher.sol"; 24 | 25 | /** 26 | * @title Pluto 27 | * @notice Pluto is a simple IBC receiver contract that receives packets without sending acks. 28 | * @dev This contract is used for only testing IBC functionality and as an example for dapp developers 29 | * on how to integrate with the vibc protocol. 30 | */ 31 | contract Pluto is Mars { 32 | constructor(IbcDispatcher _dispatcher) Mars(_dispatcher) {} 33 | 34 | /** 35 | * @notice Callback for receiving a packet; triggered when a counterparty sends an an IBC packet 36 | * @param packet The IBC packet received 37 | * @return ackPacket The acknowledgement packet generated in response 38 | * @dev Make sure to validate packet's source and destiation channels and ports. 39 | */ 40 | function onRecvPacket(IbcPacket memory packet) 41 | external 42 | override 43 | onlyIbcDispatcher 44 | returns (AckPacket memory ackPacket, bool skipAck) 45 | { 46 | recvedPackets.push(packet); 47 | 48 | // solhint-disable-next-line quotes 49 | return (AckPacket(true, abi.encodePacked('{ "account": "account", "reply": "got the message" }')), true); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contracts/implementation_templates/FeeSender.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity 0.8.15; 19 | 20 | import {IbcDispatcher} from "../interfaces/IbcDispatcher.sol"; 21 | import {ChannelOrder} from "../libs/Ibc.sol"; 22 | 23 | /** 24 | */ 25 | /** 26 | * @title FeeSender 27 | * @notice This contract provides methods to deposit fees for sendPacket and channelOpenInit calls, so that they can be 28 | * relayed by polymer. 29 | * @notice Contracts don't need to inherit from this contract if they plan to relay their own packets and channel 30 | * handshake txs. 31 | * @notice Use the Polymer fee estimation api to get the required fees to ensure that enough fees are sent. 32 | */ 33 | abstract contract FeeSender { 34 | /** 35 | * @notice Deposits the send packet relayer fee for a given channel and sequence into the FeeVault. 36 | * @notice If you are relaying your own packets, you should not call this method. 37 | * @param dispatcher The dispatcher contract that the corresponding sendPacket will be called on. 38 | * @param channelId The channel id to deposit fees for that the packet is sent over. 39 | * @param sequence The sequence of the packet to deposit fees for. This is returned from the sendPacket call. 40 | * @param gasLimits An array containing two gas limit values: 41 | * - gasLimits[0] for `recvPacket` fees 42 | * - gasLimits[1] for `ackPacket` fees. 43 | * @param gasPrices An array containing two gas price values: 44 | * - gasPrices[0] for `recvPacket` fees, for the dest chain 45 | * - gasPrices[1] for `ackPacket` fees, for the src chain 46 | * @notice The total fees sent in the msg.value should be equal to the total gasLimits[0] * gasPrices[0] + 47 | * @notice Use the Polymer fee estimation api to get the required fees to ensure that enough fees are sent. 48 | * @dev Note: We have to have gasLimits and gasPrices as memory arrays. We cannot have them as calldata arrays 49 | * because solidity has weird behavior with using too much calldata in stacked calls 50 | * gasLimits[1] * gasPrices[1]. The transaction will revert if a higher or lower value is sent 51 | */ 52 | function _depositSendPacketFee( 53 | IbcDispatcher dispatcher, 54 | bytes32 channelId, 55 | uint64 sequence, 56 | uint256[2] memory gasLimits, 57 | uint256[2] memory gasPrices 58 | ) internal { 59 | dispatcher.feeVault().depositSendPacketFee{value: msg.value}(channelId, sequence, gasLimits, gasPrices); 60 | } 61 | 62 | /** 63 | * @notice Deposits the open channel fee for a given channel and sequence into the FeeVault. 64 | * @notice If you are relaying your own channelHandshake transactions, you should not call this method. 65 | * @param dispatcher The dispatcher contract the channelOpenInit was called on. 66 | * @param version The version of the channelOpenInit call, used to identify which chanOpenInit call fees were 67 | * deposited for. 68 | * @param ordering The ordering of the channelOpenInit call, used to identify which chanOpenInit call fees were 69 | * deposited for. 70 | * @param connectionHops The connection hops for the channelOpenInit call, used to identify which channelOpenInit 71 | * call fees were deposited for . 72 | * @param counterpartyPortId The counterparty port ID for the channelOpenInit call, used to identify which 73 | * channelOpenInit call fees were deposited for . 74 | */ 75 | function _depositOpenChannelFee( 76 | IbcDispatcher dispatcher, 77 | string memory version, 78 | ChannelOrder ordering, 79 | string[] calldata connectionHops, 80 | string calldata counterpartyPortId 81 | ) internal { 82 | dispatcher.feeVault().depositOpenChannelFee{value: msg.value}( 83 | address(this), version, ordering, connectionHops, counterpartyPortId 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /contracts/implementation_templates/IbcReceiverUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.9; 19 | 20 | import {Ownable2StepUpgradeable} from "@openzeppelin-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; 21 | import {IbcDispatcher} from "../interfaces/IbcDispatcher.sol"; 22 | 23 | contract IbcReceiverBaseUpgradeable is Ownable2StepUpgradeable { 24 | IbcDispatcher public dispatcher; 25 | 26 | error notIbcDispatcher(); 27 | error invalidAddress(); 28 | error UnsupportedVersion(); 29 | error ChannelNotFound(); 30 | 31 | /** 32 | * @dev Modifier to restrict access to only the IBC dispatcher. 33 | * Only the address with the IBC_ROLE can execute the function. 34 | * Should add this modifier to all IBC-related callback functions. 35 | */ 36 | modifier onlyIbcDispatcher() { 37 | if (msg.sender != address(dispatcher)) { 38 | revert notIbcDispatcher(); 39 | } 40 | _; 41 | } 42 | 43 | constructor() { 44 | _disableInitializers(); 45 | } 46 | 47 | /// This function is called for plain Ether transfers, i.e. for every call with empty calldata. 48 | // An empty function body is sufficient to receive packet fee refunds. 49 | receive() external payable {} 50 | 51 | /** 52 | * @dev initializer function that takes an IbcDispatcher address and grants the IBC_ROLE to the Polymer IBC 53 | * Dispatcher. 54 | * @param _dispatcher The address of the IbcDispatcher contract. 55 | */ 56 | function __IbcReceiverBase_init(IbcDispatcher _dispatcher) internal onlyInitializing { 57 | __Ownable2Step_init(); 58 | dispatcher = _dispatcher; 59 | } 60 | 61 | /** 62 | * @dev This empty reserved space is put in place to allow future versions to add new 63 | * variables without shifting down storage in the inheritance chain. 64 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 65 | */ 66 | // solhint-disable-next-line ordering 67 | uint256[49] private __gap; 68 | } 69 | -------------------------------------------------------------------------------- /contracts/interfaces/IDispatcher.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {IbcDispatcher, IbcEventsEmitter} from "./IbcDispatcher.sol"; 20 | 21 | import {Ics23Proof} from "./IProofVerifier.sol"; 22 | import {Channel, ChannelEnd, ChannelOrder, IbcPacket} from "../libs/Ibc.sol"; 23 | import {ILightClient} from "./ILightClient.sol"; 24 | import {IFeeVault} from "./IFeeVault.sol"; 25 | 26 | interface IDispatcher is IbcDispatcher, IbcEventsEmitter { 27 | function setPortPrefix(string calldata _portPrefix) external; 28 | 29 | function updateClient(bytes calldata proof, uint256 height, uint256 appHash, string calldata connection) external; 30 | 31 | /** 32 | * This function is called by a 'relayer' on behalf of a dApp. 33 | */ 34 | function channelOpenInit( 35 | string calldata version, 36 | ChannelOrder ordering, 37 | bool feeEnabled, 38 | string[] calldata connectionHops, 39 | string calldata counterpartyPortId 40 | ) external; 41 | 42 | function setClientForConnection(string calldata connection, ILightClient lightClient) external; 43 | 44 | function removeConnection(string calldata connection) external; 45 | 46 | /** 47 | * This function is called by a 'relayer' on behalf of a dApp. The dApp should implement IbcChannelHandler's 48 | * onChanOpenTry. If the callback succeeds, the dApp should return the selected version and the emitted event 49 | * will be relayed to the IBC/VIBC hub chain. 50 | */ 51 | function channelOpenTry( 52 | ChannelEnd calldata local, 53 | ChannelOrder ordering, 54 | bool feeEnabled, 55 | string[] calldata connectionHops, 56 | ChannelEnd calldata counterparty, 57 | Ics23Proof calldata proof 58 | ) external; 59 | 60 | /** 61 | * This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event. 62 | * The dApp should implement the onChannelConnect method to handle the third channel handshake method: ChanOpenAck 63 | */ 64 | function channelOpenAck( 65 | ChannelEnd calldata local, 66 | string[] calldata connectionHops, 67 | ChannelOrder ordering, 68 | bool feeEnabled, 69 | ChannelEnd calldata counterparty, 70 | Ics23Proof calldata proof 71 | ) external; 72 | 73 | /** 74 | * This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event. 75 | * The dApp should implement the onChannelConnect method to handle the last channel handshake method: 76 | * ChannelOpenConfirm 77 | */ 78 | function channelOpenConfirm( 79 | ChannelEnd calldata local, 80 | string[] calldata connectionHops, 81 | ChannelOrder ordering, 82 | bool feeEnabled, 83 | ChannelEnd calldata counterparty, 84 | Ics23Proof calldata proof 85 | ) external; 86 | 87 | function channelCloseConfirm(address portAddress, bytes32 channelId, Ics23Proof calldata proof) external; 88 | function channelCloseInit(bytes32 channelId) external; 89 | 90 | function sendPacket(bytes32 channelId, bytes calldata packet, uint64 timeoutTimestamp) 91 | external 92 | returns (uint64 sequence); 93 | 94 | function acknowledgement(IbcPacket calldata packet, bytes calldata ack, Ics23Proof calldata proof) external; 95 | 96 | function timeout(IbcPacket calldata packet, Ics23Proof calldata proof) external; 97 | 98 | function writeTimeoutPacket(IbcPacket calldata packet, Ics23Proof calldata proof) external; 99 | 100 | function recvPacket(IbcPacket calldata packet, Ics23Proof calldata proof) external; 101 | function feeVault() external returns (IFeeVault feeVault); 102 | 103 | function getState(uint256 height, string calldata connection) external view returns (uint256 appHash); 104 | 105 | function getChannel(address portAddress, bytes32 channelId) external view returns (Channel memory channel); 106 | } 107 | -------------------------------------------------------------------------------- /contracts/interfaces/IFeeVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity 0.8.15; 19 | 20 | import {ChannelOrder} from "../libs/Ibc.sol"; 21 | 22 | struct GasFee { 23 | uint256 gasLimit; 24 | uint256 gasPrice; 25 | } 26 | 27 | struct SendpacketFeeDeposited { 28 | uint256[2] gasLimits; 29 | uint256[2] gasPrices; 30 | } 31 | 32 | interface IFeeVault { 33 | event SendPacketFeeDeposited( 34 | bytes32 indexed channelId, uint64 indexed sequence, uint256[2] gasLimits, uint256[2] gasPrices 35 | ); 36 | event OpenChannelFeeDeposited( 37 | address sourceAddress, 38 | string version, 39 | ChannelOrder ordering, 40 | string[] connectionHops, 41 | string counterpartyPortId, 42 | uint256 feeAmount 43 | ); 44 | 45 | error SenderNotDispatcher(); 46 | error FeeThresholdNotMet(); 47 | error IncorrectFeeSent(uint256 expected, uint256 sent); 48 | 49 | function depositSendPacketFee( 50 | bytes32 channelId, 51 | uint64 sequence, 52 | uint256[2] calldata gasLimits, 53 | uint256[2] calldata gasPrices 54 | ) external payable; 55 | 56 | function depositOpenChannelFee( 57 | address sender, 58 | string memory version, 59 | ChannelOrder ordering, 60 | string[] calldata connectionHops, 61 | string memory counterpartyPortId 62 | ) external payable; 63 | 64 | function withdrawFeesToOwner() external; 65 | } 66 | -------------------------------------------------------------------------------- /contracts/interfaces/ILightClient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {Ics23Proof} from "./IProofVerifier.sol"; 20 | 21 | enum LightClientType { 22 | SimTestLightClient, // Note: not deployed on any mainnets 23 | OptimisticLightClient, // Our native opstack light client 24 | SequencerLightClient, // Our native sequencer light client, which does not check l1 origin check to cut down on 25 | // latency 26 | ReOrgResistantSequencerLightClient // Our native sequencer light client, which checks for l1 origin checks to be 27 | // re-org resistant 28 | 29 | } 30 | 31 | interface IClientUpdates { 32 | /** 33 | * @dev Adds an appHash to the internal store, after verifying the client update proof associated with the light 34 | * client implementation. 35 | * @param proof A generic byte array that contains proof data to prove the apphash client update. This can differ 36 | * depending on the light client type. E.g. this can be an abi.encoded struct which contains an OpL2StateProof and 37 | * L1Block from the IProofVerifier 38 | * interface. 39 | * @param appHash App hash (state root) to be verified 40 | */ 41 | function updateClient(bytes calldata proof, uint256 height, uint256 appHash) external; 42 | 43 | /* 44 | * Returns the type of the light client, useful for relayers to know which light client implementation is at which 45 | address. 46 | */ 47 | function LIGHT_CLIENT_TYPE() external view returns (LightClientType); 48 | } 49 | 50 | /** 51 | * @title ILightClient 52 | * @author Polymer Labs 53 | * @notice A contract that manages the merkle root(appHash) at different block heights of a chain and tracks the fraud 54 | * proof end time for them. 55 | * @notice This is used for state inclusion proofs 56 | */ 57 | interface ILightClient is IClientUpdates { 58 | /** 59 | * @dev Checks if the current trusted optimistic consensus state 60 | * can be used to perform the membership test and if so, verifies the proof 61 | * @dev reverts if the proof is not valid (i.e. if the key is not included in the proof) 62 | */ 63 | function verifyMembership(Ics23Proof calldata proof, bytes calldata key, bytes memory expectedValue) external; 64 | 65 | /** 66 | * @dev Verifies that the given key is not included in the proof 67 | */ 68 | function verifyNonMembership(Ics23Proof calldata proof, bytes calldata key) external; 69 | 70 | /** 71 | * Returns the apphash at a given height 72 | */ 73 | function getState(uint256 height) external view returns (uint256); 74 | } 75 | -------------------------------------------------------------------------------- /contracts/interfaces/IOptimisticLightClient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {ILightClient} from "./ILightClient.sol"; 20 | 21 | /** 22 | * @title ILightClient 23 | * @author Polymer Labs 24 | * @notice A contract that manages the merkle root(appHash) at different block heights of a chain and tracks the fraud 25 | * proof end time for them. 26 | * @notice This is used for state inclusion proofs 27 | */ 28 | interface IOptimisticLightClient is ILightClient { 29 | /** 30 | * @dev Returns the fraud proof end time at a given block 31 | * 0 is returned if there isn't an appHash with the given l2 height. 32 | */ 33 | function getFraudProofEndtime(uint256 height) external returns (uint256 endTime); 34 | 35 | /** 36 | * @dev Returns the apphash at a given block height, as well as the fraud proof end time and whether the fraud proof 37 | * has ended 38 | */ 39 | function getStateAndEndTime(uint256 height) 40 | external 41 | view 42 | returns (uint256 appHash, uint256 fraudProofEndTime, bool ended); 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/IProofVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | // OpIcs23ProofPath represents a commitment path in an ICS23 proof, which consists of a commitment prefix and a suffix. 20 | struct OpIcs23ProofPath { 21 | bytes prefix; 22 | bytes suffix; 23 | } 24 | 25 | // OpIcs23Proof represents an ICS23 proof 26 | struct OpIcs23Proof { 27 | OpIcs23ProofPath[] path; 28 | bytes key; 29 | bytes value; 30 | bytes prefix; 31 | } 32 | 33 | // the Ics23 proof related structs are used to do membership verification. These are not the actual Ics23 34 | // format but a "solidity friendly" version of it - data is the same just packaged differently 35 | struct Ics23Proof { 36 | OpIcs23Proof[] proof; 37 | uint256 height; 38 | } 39 | 40 | // This is the proof we use to verify the apphash (state) updates. 41 | struct OpL2StateProof { 42 | bytes[] accountProof; 43 | bytes[] outputRootProof; 44 | bytes32 l2OutputProposalKey; 45 | bytes32 l2BlockHash; 46 | } 47 | 48 | // The `header` field is a list of RLP encoded L1 header fields. Both stateRoot and number are not 49 | // encoded for easy usage. They must match with their RLP encoded counterparty versions. 50 | struct L1Header { 51 | bytes[] header; 52 | bytes32 stateRoot; 53 | uint64 number; 54 | } 55 | 56 | interface IAppStateVerifier { 57 | error InvalidL1BlockNumber(); 58 | error InvalidL1BlockHash(); 59 | error InvalidRLPEncodedL1BlockNumber(); 60 | error InvalidRLPEncodedL1StateRoot(); 61 | error InvalidAppHash(); 62 | error InvalidProofKey(); 63 | error InvalidProofValue(); 64 | error InvalidPacketProof(); 65 | error InvalidIbcStateProof(); 66 | error MethodNotImplemented(); 67 | 68 | /** 69 | * @dev verifies the provided ICS23 proof given the trusted app hash. Reverts in case of failure. 70 | * 71 | * @param appHash trusted l2 app hash (state root) 72 | * @param key key to be proven 73 | * @param value value to be proven 74 | * @param proof ICS23 membership proof 75 | */ 76 | function verifyMembership(bytes32 appHash, bytes calldata key, bytes calldata value, Ics23Proof calldata proof) 77 | external 78 | pure; 79 | 80 | /** 81 | * @dev verifies the provided ICS23 proof given the trusted app hash. Reverts in case of failure. 82 | * 83 | * @param appHash trusted l2 app hash (state root) 84 | * @param key key to be proven non-existing 85 | * @param proof ICS23 non-membership proof 86 | */ 87 | function verifyNonMembership(bytes32 appHash, bytes calldata key, Ics23Proof calldata proof) external pure; 88 | } 89 | 90 | /** 91 | * @title IProofVerifier 92 | * @author Polymer Labs 93 | * @notice An interface that abstracts away proof verification logic for light clients 94 | */ 95 | interface IProofVerifier is IAppStateVerifier { 96 | /** 97 | * @dev verifies if a state update (apphash) is valid, given the provided proofs. 98 | * Reverts in case of failure. 99 | * 100 | * @param l1header RLP "encoded" version of the L1 header that matches with the trusted hash and number 101 | * @param proof l2 state proof. It includes the keys, hashes and storage proofs required to verify the app hash 102 | * @param appHash l2 app hash (state root) to be verified 103 | * @param trustedL1BlockHash trusted L1 block hash. Provided L1 header must match with it. 104 | * @param trustedL1BlockNumber trusted L1 block number. Provided L1 header must match with it. 105 | */ 106 | function verifyStateUpdate( 107 | L1Header calldata l1header, 108 | OpL2StateProof calldata proof, 109 | bytes32 appHash, 110 | bytes32 trustedL1BlockHash, 111 | uint64 trustedL1BlockNumber 112 | ) external view; 113 | } 114 | -------------------------------------------------------------------------------- /contracts/interfaces/ISignatureVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {IAppStateVerifier} from "./IProofVerifier.sol"; 20 | 21 | /** 22 | * @title ISignatureVerifier 23 | * @author Polymer Labs 24 | * @notice An interface that abstracts away proof verification logic for light clients 25 | */ 26 | interface ISignatureVerifier is IAppStateVerifier { 27 | error InvalidSequencerSignature(); 28 | 29 | function verifyStateUpdate(uint256 l2BlockNumber, bytes32 appHash, bytes32 l1BlockHash, bytes calldata signature) 30 | external; 31 | 32 | function SEQUENCER() external view returns (address); 33 | function CHAIN_ID() external view returns (bytes32); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniversalChannelHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {IbcDispatcher} from "./IbcDispatcher.sol"; 20 | 21 | import {IbcUniversalChannelMW} from "./IbcMiddleware.sol"; 22 | import {ChannelOrder} from "../libs/Ibc.sol"; 23 | 24 | interface IUniversalChannelHandler is IbcUniversalChannelMW { 25 | function registerMwStack(uint256 mwBitmap, address[] calldata mwAddrs) external; 26 | function triggerChannelInit( 27 | string calldata version, 28 | ChannelOrder ordering, 29 | bool feeEnabled, 30 | string[] calldata connectionHops, 31 | string calldata counterpartyPortIdentifier 32 | ) external; 33 | function closeChannel(bytes32 channelId) external; 34 | function setDispatcher(IbcDispatcher dispatcher) external; 35 | function dispatcher() external returns (IbcDispatcher dispatcher); 36 | function connectedChannels(uint256) external view returns (bytes32 channel); 37 | } 38 | -------------------------------------------------------------------------------- /contracts/libs/IbcErrors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity 0.8.15; 18 | 19 | library IBCErrors { 20 | // Misc errors 21 | error invalidCounterParty(); 22 | error invalidCounterPartyPortId(); 23 | error invalidHexStringLength(); 24 | error invalidRelayerAddress(); 25 | error consensusStateVerificationFailed(); 26 | error packetNotTimedOut(); 27 | error invalidAddress(); 28 | error invalidPortPrefix(); 29 | error invalidConnectionHops(); 30 | error notEnoughGas(); 31 | error invalidCharacter(); 32 | error invalidPacket(); 33 | error invalidVersion(); 34 | 35 | // packet sequence related errors. 36 | error invalidPacketSequence(); 37 | error unexpectedPacketSequence(); 38 | 39 | // channel related errors. 40 | error channelNotOwnedBySender(); 41 | error channelNotOwnedByPortAddress(); 42 | 43 | // client related errors. 44 | error clientAlreadyCreated(); 45 | error clientNotCreated(); 46 | 47 | // packet commitment related errors. 48 | error packetCommitmentNotFound(); 49 | error ackPacketCommitmentAlreadyExists(); 50 | error packetReceiptAlreadyExists(); 51 | 52 | // receiver related errors. 53 | error receiverNotIntendedPacketDestination(); 54 | error receiverNotOriginPacketSender(); 55 | 56 | error invalidChannelType(string channelType); 57 | 58 | // related to clients 59 | error lightClientNotFound(string connection); 60 | error channelIdNotFound(bytes32 channelId); 61 | error invalidConnection(string connection); 62 | } 63 | -------------------------------------------------------------------------------- /contracts/libs/PolymerProofs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.0; 19 | 20 | // import {Ibc} from "./Ibc.sol"; 21 | 22 | /** 23 | * A library for helpers for proving peptide state 24 | */ 25 | // library PolymerProofs { 26 | 27 | // } 28 | -------------------------------------------------------------------------------- /contracts/utils/DummyLightClient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {ILightClient, Ics23Proof, LightClientType} from "../interfaces/ILightClient.sol"; 20 | 21 | /** 22 | * @title DummyLightClient 23 | * @dev This contract is a dummy implementation of a consensus state manager. 24 | * It should only be used for testing purposes. 25 | * The logic for checking if the proof length is greater than zero is naive. 26 | */ 27 | contract DummyLightClient is ILightClient { 28 | uint8 private _LightClientType = uint8(LIGHT_CLIENT_TYPE); // Also redundantly stored as a private mutable type in 29 | 30 | // we still need this array thoguh it is unused for storage mocks 31 | mapping(uint256 => uint256) public consensusStates; 32 | 33 | // cheap on-chain use case it needs to be accessed in any proofs 34 | LightClientType public constant LIGHT_CLIENT_TYPE = LightClientType.SimTestLightClient; // Stored as a constant for 35 | 36 | error InvalidDummyMembershipProof(); 37 | error InvalidDummyNonMembershipProof(); 38 | 39 | constructor() {} 40 | 41 | function updateClient(bytes calldata, uint256, uint256) external pure override {} 42 | 43 | function getState(uint256) external pure override returns (uint256 appHash) { 44 | return (0); 45 | } 46 | 47 | function verifyMembership(Ics23Proof calldata proof, bytes memory, bytes memory) external pure override { 48 | if (proof.height == 0) revert InvalidDummyMembershipProof(); 49 | } 50 | 51 | function verifyNonMembership(Ics23Proof calldata proof, bytes memory) external pure override { 52 | if (proof.height == 0) revert InvalidDummyNonMembershipProof(); 53 | } 54 | 55 | function getLightClientType() external pure returns (LightClientType) { 56 | return LightClientType.SimTestLightClient; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/utils/DummyProofVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.0; 18 | 19 | import {IProofVerifier, L1Header, OpL2StateProof, Ics23Proof} from "../interfaces/IProofVerifier.sol"; 20 | 21 | /** 22 | * @title DummyProofVerifier 23 | * @dev A dummy implementation of the IProofVerifier interface for testing purposes. Does not actually verify any 24 | * proofs. 25 | */ 26 | contract DummyProofVerifier is IProofVerifier { 27 | function verifyStateUpdate(L1Header calldata, OpL2StateProof calldata, bytes32, bytes32, uint64) external pure {} 28 | 29 | function verifyMembership(bytes32, bytes calldata, bytes calldata, Ics23Proof calldata) external pure {} 30 | 31 | function verifyNonMembership(bytes32, bytes calldata, Ics23Proof calldata) external pure {} 32 | } 33 | -------------------------------------------------------------------------------- /contracts/utils/GasAudit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.9; 19 | 20 | contract GasAudit { 21 | mapping(bytes32 => bool) public channelIds1; 22 | mapping(string => bool) public channelIds2; 23 | 24 | event OpenIbcChannel1( 25 | address indexed portAddress, bytes32 indexed channelId, string counterpartyPortId, bytes32 coutnerpartyChannelId 26 | ); 27 | 28 | event OpenIbcChannel2( 29 | address indexed portAddress, string channelId, string counterpartyPortId, string coutnerpartyChannelId 30 | ); 31 | 32 | function callWithBytes32( 33 | address portAddress, 34 | bytes32 channelId, 35 | string calldata counterpartyPortId, 36 | bytes32 coutnerpartyChannelId 37 | ) external { 38 | channelIds1[channelId] = true; 39 | emit OpenIbcChannel1(portAddress, channelId, counterpartyPortId, coutnerpartyChannelId); 40 | } 41 | 42 | function callWithString( 43 | address portAddress, 44 | string calldata channelId, 45 | string calldata counterpartyPortId, 46 | string calldata coutnerpartyChannelId 47 | ) external { 48 | channelIds2[channelId] = true; 49 | emit OpenIbcChannel2(portAddress, channelId, counterpartyPortId, coutnerpartyChannelId); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /diagrams/fee-design.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-ibc/vibc-core-smart-contracts/e463191080973d50bf48c2bddcb34be310228445/diagrams/fee-design.jpg -------------------------------------------------------------------------------- /diagrams/vibcContractsOverview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-ibc/vibc-core-smart-contracts/e463191080973d50bf48c2bddcb34be310228445/diagrams/vibcContractsOverview.jpg -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import pluginJs from '@eslint/js'; 3 | // import tseslint from "typescript-eslint"; 4 | import tseslint, { parser } from 'typescript-eslint' 5 | 6 | export default [ 7 | {files: ['**/src/*.{ts}']}, 8 | {ignores:['**/lib/**', '**/node_modules/**', '**/dist/**']}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | { 13 | rules:{ 14 | '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], 15 | '@/quotes': ['error', 'single', { avoidEscape: true }], 16 | '@/jsx-quotes': ['error', 'prefer-double'], 17 | '@/eol-last': ['error', 'always'], 18 | '@/no-unused-vars': ['warn'], 19 | '@typescript-eslint/no-unused-vars': ['warn'], 20 | '@/no-undef': ['off'], 21 | '@/no-case-declarations': ['off'], 22 | } 23 | } 24 | ]; 25 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | auto_detect_solc = true 3 | src = 'contracts' 4 | out = 'out' 5 | libs = ['lib'] 6 | test = 'test' 7 | cache_path = 'forge-cache' 8 | gas_reports = ['*'] 9 | fs_permissions = [{ access = 'read', path = './test/payload'}, { access = 'read', path = './out'}] 10 | [fmt] 11 | wrap_comments = true 12 | number_underscore = "thousands" 13 | ignore = ["lib/*"] 14 | 15 | # See more config options https://book.getfoundry.sh/reference/config.html 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/open-ibc/vibc-core-smart-contracts/v4 2 | 3 | go 1.21 4 | 5 | toolchain go1.21.3 6 | 7 | require github.com/ethereum/go-ethereum v1.13.5 8 | 9 | require ( 10 | github.com/Microsoft/go-winio v0.6.1 // indirect 11 | github.com/bits-and-blooms/bitset v1.13.0 // indirect 12 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 13 | github.com/consensys/bavard v0.1.13 // indirect 14 | github.com/consensys/gnark-crypto v0.12.1 // indirect 15 | github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect 16 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 17 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 18 | github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240103191009-655947053753 // indirect 19 | github.com/ethereum/c-kzg-4844 v0.4.0 // indirect 20 | github.com/fsnotify/fsnotify v1.7.0 // indirect 21 | github.com/go-ole/go-ole v1.2.6 // indirect 22 | github.com/go-stack/stack v1.8.1 // indirect 23 | github.com/google/uuid v1.6.0 // indirect 24 | github.com/gorilla/websocket v1.5.0 // indirect 25 | github.com/holiman/uint256 v1.2.4 // indirect 26 | github.com/mmcloughlin/addchain v0.4.0 // indirect 27 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect 28 | github.com/supranational/blst v0.3.11 // indirect 29 | github.com/tklauser/go-sysconf v0.3.12 // indirect 30 | github.com/tklauser/numcpus v0.6.1 // indirect 31 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 32 | golang.org/x/crypto v0.31.0 // indirect 33 | golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect 34 | golang.org/x/mod v0.14.0 // indirect 35 | golang.org/x/sync v0.7.0 // indirect 36 | golang.org/x/sys v0.28.0 // indirect 37 | golang.org/x/tools v0.16.0 // indirect 38 | gopkg.in/yaml.v3 v3.0.1 // indirect 39 | rsc.io/tmplfunc v0.0.3 // indirect 40 | ) 41 | 42 | replace github.com/ethereum/go-ethereum v1.13.5 => github.com/ethereum-optimism/op-geth v1.101305.2-rc.2.0.20240117002010-d5f142e54a0a 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@open-ibc/vibc-core-smart-contracts", 3 | "version": "4.0.22", 4 | "main": "dist/index.js", 5 | "bin": { 6 | "verify-vibc-core-smart-contracts": "./dist/scripts/verify-contract-script.js", 7 | "update-vibc-core-smart-contracts": "./dist/scripts/update-contracts-script.js", 8 | "vibc-core-deploy-test": "./dist/scripts/fork-deployment-test.js", 9 | "vibc-core-deploy-multisig": "./dist/scripts/deploy-multisig.js", 10 | "vibc-core-execute-multisig-tx": "./dist/scripts/execute-multisig-tx.js" 11 | }, 12 | "license": "MIT", 13 | "dependencies": { 14 | "@commander-js/extra-typings": "^12.1.0", 15 | "@safe-global/api-kit": "^2.5.2", 16 | "@safe-global/protocol-kit": "^4.0.3", 17 | "@safe-global/safe-core-sdk-types": "^5.1.0", 18 | "@typechain/ethers-v6": "^0.5.1", 19 | "ethers": "^6.13.2", 20 | "nunjucks": "^3.2.4", 21 | "solhint": "^5.0.3", 22 | "typechain": "^8.3.2", 23 | "yaml": "^2.6.1", 24 | "winston": "^3.17.0", 25 | "yargs": "^17.7.2", 26 | "zod": "^3.23.8", 27 | "zx": "^8.1.9" 28 | }, 29 | "devDependencies": { 30 | "@eslint/js": "^9.16.0", 31 | "@types/eslint__js": "^8.42.3", 32 | "@types/js-yaml": "^4.0.9", 33 | "@types/node": "^22.10.2", 34 | "@types/nunjucks": "^3.2.6", 35 | "@types/yargs": "^17.0.33", 36 | "@typescript-eslint/eslint-plugin": "^8.18.0", 37 | "@typescript-eslint/parser": "^8.11.0", 38 | "chai": "^5.1.1", 39 | "eslint": "^8.57.1", 40 | "globals": "^15.9.0", 41 | "solidity-coverage": "^0.8.12", 42 | "tslib": "^2.7.0", 43 | "tsup": "^8.3.0", 44 | "typescript": "^5.5.4", 45 | "typescript-eslint": "^8.11.0" 46 | }, 47 | "scripts": { 48 | "lint": "solhint contracts/**/*.sol", 49 | "test": "forge test --no-match-contract DispatcherDeployTest", 50 | "build": "make bindings-gen-go bindings-gen-ts && tsup", 51 | "build-contracts": "make build-contracts", 52 | "deploy-contracts": "npm run build && node dist/deploy.js", 53 | "deploy-simple": "node dist/deploy.js", 54 | "prepublishOnly": "npm run build", 55 | "lint-write": "eslint --fix .", 56 | "lint-check": "eslint ." 57 | }, 58 | "keywords": [ 59 | "evm", 60 | "cosmos", 61 | "rollup", 62 | "op-stack", 63 | "interoperability", 64 | "solidity" 65 | ], 66 | "author": "Polymer Labs", 67 | "exports": { 68 | ".": { 69 | "require": "./dist/index.js", 70 | "import": "./dist/index.js", 71 | "types": "./dist/index.d.ts" 72 | }, 73 | "./contracts": { 74 | "require": "./dist/evm/contracts/index.js", 75 | "import": "./dist/evm/contracts/index.js", 76 | "types": "./dist/evm/contracts/d.ts" 77 | }, 78 | "./contracts/*": { 79 | "require": "./dist/evm/contracts/*.js", 80 | "import": "./dist/evm/contracts/*.js", 81 | "types": "./dist/evm/contracts/*.d.ts" 82 | }, 83 | "./evm": { 84 | "require": "./dist/evm/index.js", 85 | "import": "./dist/evm/index.js", 86 | "types": "./dist/evm/index.d.ts" 87 | }, 88 | "./evm/chain": "./dist/evm/chain.js", 89 | "./schemas": "./dist/evm/schemas/index.js", 90 | "./schemas/contract": "./dist/evm/schemas/contract.js", 91 | "./schemas/tx": "./dist/evm/schemas/tx.js", 92 | "./schemas/contractUpdate": "./dist/evm/schemas/contractUpdate.js", 93 | "./schemas/account": "./dist/evm/schemas/account.js", 94 | "./utils": { 95 | "require": "./dist/utils/index.js", 96 | "import": "./dist/utils/index.js", 97 | "types": "./dist/utils/index.d.ts" 98 | }, 99 | "./utils/cli": "./dist/utils/cli.js", 100 | "./utils/io": "./dist/utils/io.js", 101 | "./constants": { 102 | "require": "./dist/utils/constants.js", 103 | "import": "./dist/utils/constants.js", 104 | "types": "./dist/utils/constants.d.ts" 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /proto/channel.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // this file is copied from ibc-go and stripped from unnecessary clutter. Note that the Channel and 4 | // CounterParty messages have been renamed to ProtoChannel and ProtoCounterParty to avoid collisions with 5 | // already existent types of the same name. 6 | 7 | // Channel defines pipeline for exactly-once packet delivery between specific 8 | // modules on separate blockchains, which has at least one end capable of 9 | // sending packets and one end capable of receiving packets. 10 | message ProtoChannel { 11 | // current state of the channel end 12 | int32 state = 1; 13 | // whether the channel is ordered or unordered 14 | int32 ordering = 2; 15 | // counterparty channel end 16 | ProtoCounterparty counterparty = 3; 17 | // list of connection identifiers, in order, along which packets sent on 18 | // this channel will travel 19 | repeated string connection_hops = 4; 20 | // opaque channel version, which is agreed upon during the handshake 21 | string version = 5; 22 | } 23 | 24 | 25 | // Counterparty defines a channel end counterparty 26 | message ProtoCounterparty { 27 | // port on the counterparty chain which owns the other end of the channel. 28 | string port_id = 1; 29 | // channel end on the counterparty chain 30 | string channel_id = 2; 31 | } 32 | -------------------------------------------------------------------------------- /proto/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # before running this script, make sure to follow the readme file in the solidity-protobuf repo 6 | # and install the dependencies 7 | 8 | ROOT_DIR="$( realpath "$(dirname "$0")" )" 9 | SOLIDITY_PROTOBUF_DIR="$ROOT_DIR/../../solidity-protobuf/" 10 | PROTO_OUTDIR="$ROOT_DIR/../lib/proto" 11 | SOLC_VERSION='0.8.15' 12 | 13 | if [[ ! -d "$SOLIDITY_PROTOBUF_DIR" ]] ; then 14 | echo "Cannot find solidity protobuf code generator. Tried $SOLIDITY_PROTOBUF_DIR" 15 | echo "Clone the repo from: https://github.com/datachainlab/solidity-protobuf/" 16 | exit 1 17 | fi 18 | 19 | for F in "$ROOT_DIR"/*.proto; do 20 | if [[ ! -f "$F" ]]; then 21 | continue 22 | fi 23 | 24 | echo "Generating $F" 25 | protoc \ 26 | -I"$ROOT_DIR" \ 27 | -I"$SOLIDITY_PROTOBUF_DIR/protobuf-solidity/src/protoc/include" \ 28 | --plugin=protoc-gen-sol="$SOLIDITY_PROTOBUF_DIR/protobuf-solidity/src/protoc/plugin/gen_sol.py" \ 29 | --sol_out="gen_runtime=./ProtoBufRuntime.sol&solc_version=$SOLC_VERSION:$PROTO_OUTDIR" \ 30 | "$F" 31 | done 32 | 33 | # to run these, install prettier and the solidity plugin with 34 | # npm install --global prettier prettier-plugin-solidity 35 | for F in "$PROTO_OUTDIR"/*.sol; do 36 | prettier --config "$ROOT_DIR/../.prettierrc.yml" --write "$F" 37 | done 38 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | @openzeppelin/=lib/openzeppelin-contracts/ 2 | @openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/ 3 | @lazyledger/protobuf3-solidity-lib/=lib/protobuf3-solidity-lib/ 4 | ds-test/=lib/forge-std/lib/ds-test/src/ 5 | forge-std/=lib/forge-std/src/ 6 | optimism/=lib/optimism/packages/contracts-bedrock/src/ 7 | base64/=lib/base64 8 | proto/=lib/proto 9 | -------------------------------------------------------------------------------- /script/Counter.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | contract CounterScript is Script { 7 | function setUp() public {} 8 | 9 | function run() public { 10 | vm.broadcast(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | ## Deployment 2 | 1. Set the env vars in .env file 3 | ```bash 4 | export IMPL_SALT=$(openssl rand -hex 32) 5 | export PRIVATE_KEY= 6 | export ETHERSCAN_API_KEY= 7 | export L2_OUTPUT_ORACLE_ADDRESS= 8 | export OP_RPC_URL= 9 | export BASE_RPC_URL= 10 | ``` 11 | 2. Run source command to set the env vars 12 | ```bash 13 | source .env 14 | ``` 15 | 3. Deploy the contracts with 16 | ```bash 17 | CHAIN= forge script script/Deploy.s.sol --rpc-url $OP_RPC_URL --broadcast --private-key $PRIVATE_KEY --verifier-url https://< chain >-sepolia.blockscout.com/api --verifier blockscout --verify 18 | ``` 19 | can be `optimism` or `base`. 20 | 21 | ### Deploying a single contract 22 | All the functions responsible for deploying a single contract are public, allowing the utilization of the --sig argument in the forge script to specifically direct the deployment of a single contract. 23 | For example, to deploy a dummy proof verifier contract, the following command can be used: 24 | ```bash 25 | forge script script/Deploy.s.sol --rpc-url $OP_RPC_URL --broadcast --private-key $PRIVATE_KEY --sig 'deployDummyProofVerifier()' 26 | ``` 27 | 28 | ### Troubleshooting 29 | In the event of encountering an ambiguous error containing `EvmError: Revert` it is probable that adjusting the IMPL_SALT environment variable is necessary. 30 | This variable plays a crucial role in determining the addresses of smart contracts deployed through CREATE2. 31 | Deploying the same contracts with the identical IMPL_SALT value twice will result in a failure during the second deployment. 32 | To generate a new IMPL_SALT value you can run `source .env`. -------------------------------------------------------------------------------- /specs/evm.accounts.yaml: -------------------------------------------------------------------------------- 1 | # These accounts are derived from a test mnemonic by Anvil/Hardhat and used for testing purposes only. 2 | - name: 'KEY_DEPLOYER' 3 | privateKey: '{{ DUMMY_DEPLOYER_PRIVATE_KEY }}' 4 | 5 | # Dapp accounts 6 | - name: 'KEY_DAPP1' 7 | privateKey: '{{ DAPP_PRIVATE_KEY_1 }}' 8 | - name: 'KEY_DAPP2' 9 | privateKey: '{{ DAPP_PRIVATE_KEY_2 }}' 10 | - name: 'KEY_DAPP3' 11 | privateKey: '{{ DAPP_PRIVATE_KEY_3 }}' 12 | -------------------------------------------------------------------------------- /src/evm/chain.ts: -------------------------------------------------------------------------------- 1 | import { parseZodSchema } from "../utils/io"; 2 | import { Registry } from "../utils/registry"; 3 | import { z } from "zod"; 4 | 5 | export const ChainConfigSchema = z.object({ 6 | chainName: z.string().min(1), 7 | chainId: z.number().int().min(1), 8 | vmType: z.enum(["evm", "polymer", "cosmos"]).optional().default("evm"), 9 | description: z.optional(z.string()), 10 | rpc: z.string().min(1), 11 | deploymentEnvironment: z 12 | .enum(["local", "staging", "production", "mainnet"]) 13 | .optional() 14 | .default("local"), 15 | }); 16 | 17 | // Reduced version of chain 18 | export const ChainFolderSchema = z.object({ 19 | chainId: z.number().int().min(1), 20 | deploymentEnvironment: z 21 | .enum(["local", "staging", "production", "mainnet"]) 22 | .optional() 23 | .default("local"), 24 | }); 25 | 26 | export const chainRegistrySchema = z.array(ChainConfigSchema).min(1); 27 | 28 | export type ChainRegistry = Registry>; 29 | export type Chain = ChainRegistry["Element"] & object; // Chains must specify base data but are used as env properties so can specify any arbitrary data 30 | 31 | // load chain registry from a config object 32 | export function loadChainRegistry(config: z.input) { 33 | const chainRegistry = parseZodSchema( 34 | "ChainRegistry", 35 | config, 36 | chainRegistrySchema.parse 37 | ); 38 | return new Registry(chainRegistry, { nameFunc: (c) => c.chainName }); 39 | } 40 | -------------------------------------------------------------------------------- /src/evm/contracts/ILightClient.sol/IClientUpdates.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumberish, 7 | BytesLike, 8 | FunctionFragment, 9 | Result, 10 | Interface, 11 | ContractRunner, 12 | ContractMethod, 13 | Listener, 14 | } from "ethers"; 15 | import type { 16 | TypedContractEvent, 17 | TypedDeferredTopicFilter, 18 | TypedEventLog, 19 | TypedListener, 20 | TypedContractMethod, 21 | } from "../common"; 22 | 23 | export interface IClientUpdatesInterface extends Interface { 24 | getFunction( 25 | nameOrSignature: "LIGHT_CLIENT_TYPE" | "updateClient" 26 | ): FunctionFragment; 27 | 28 | encodeFunctionData( 29 | functionFragment: "LIGHT_CLIENT_TYPE", 30 | values?: undefined 31 | ): string; 32 | encodeFunctionData( 33 | functionFragment: "updateClient", 34 | values: [BytesLike, BigNumberish, BigNumberish] 35 | ): string; 36 | 37 | decodeFunctionResult( 38 | functionFragment: "LIGHT_CLIENT_TYPE", 39 | data: BytesLike 40 | ): Result; 41 | decodeFunctionResult( 42 | functionFragment: "updateClient", 43 | data: BytesLike 44 | ): Result; 45 | } 46 | 47 | export interface IClientUpdates extends BaseContract { 48 | connect(runner?: ContractRunner | null): IClientUpdates; 49 | waitForDeployment(): Promise; 50 | 51 | interface: IClientUpdatesInterface; 52 | 53 | queryFilter( 54 | event: TCEvent, 55 | fromBlockOrBlockhash?: string | number | undefined, 56 | toBlock?: string | number | undefined 57 | ): Promise>>; 58 | queryFilter( 59 | filter: TypedDeferredTopicFilter, 60 | fromBlockOrBlockhash?: string | number | undefined, 61 | toBlock?: string | number | undefined 62 | ): Promise>>; 63 | 64 | on( 65 | event: TCEvent, 66 | listener: TypedListener 67 | ): Promise; 68 | on( 69 | filter: TypedDeferredTopicFilter, 70 | listener: TypedListener 71 | ): Promise; 72 | 73 | once( 74 | event: TCEvent, 75 | listener: TypedListener 76 | ): Promise; 77 | once( 78 | filter: TypedDeferredTopicFilter, 79 | listener: TypedListener 80 | ): Promise; 81 | 82 | listeners( 83 | event: TCEvent 84 | ): Promise>>; 85 | listeners(eventName?: string): Promise>; 86 | removeAllListeners( 87 | event?: TCEvent 88 | ): Promise; 89 | 90 | LIGHT_CLIENT_TYPE: TypedContractMethod<[], [bigint], "view">; 91 | 92 | updateClient: TypedContractMethod< 93 | [proof: BytesLike, height: BigNumberish, appHash: BigNumberish], 94 | [void], 95 | "nonpayable" 96 | >; 97 | 98 | getFunction( 99 | key: string | FunctionFragment 100 | ): T; 101 | 102 | getFunction( 103 | nameOrSignature: "LIGHT_CLIENT_TYPE" 104 | ): TypedContractMethod<[], [bigint], "view">; 105 | getFunction( 106 | nameOrSignature: "updateClient" 107 | ): TypedContractMethod< 108 | [proof: BytesLike, height: BigNumberish, appHash: BigNumberish], 109 | [void], 110 | "nonpayable" 111 | >; 112 | 113 | filters: {}; 114 | } 115 | -------------------------------------------------------------------------------- /src/evm/contracts/ILightClient.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { IClientUpdates } from "./IClientUpdates"; 5 | export type { ILightClient } from "./ILightClient"; 6 | -------------------------------------------------------------------------------- /src/evm/contracts/IProofVerifier.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { IAppStateVerifier } from "./IAppStateVerifier"; 5 | export type { IProofVerifier } from "./IProofVerifier"; 6 | -------------------------------------------------------------------------------- /src/evm/contracts/IbcDispatcher.sol/IbcPacketSender.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumberish, 7 | BytesLike, 8 | FunctionFragment, 9 | Result, 10 | Interface, 11 | ContractRunner, 12 | ContractMethod, 13 | Listener, 14 | } from "ethers"; 15 | import type { 16 | TypedContractEvent, 17 | TypedDeferredTopicFilter, 18 | TypedEventLog, 19 | TypedListener, 20 | TypedContractMethod, 21 | } from "../common"; 22 | 23 | export interface IbcPacketSenderInterface extends Interface { 24 | getFunction(nameOrSignature: "sendPacket"): FunctionFragment; 25 | 26 | encodeFunctionData( 27 | functionFragment: "sendPacket", 28 | values: [BytesLike, BytesLike, BigNumberish] 29 | ): string; 30 | 31 | decodeFunctionResult(functionFragment: "sendPacket", data: BytesLike): Result; 32 | } 33 | 34 | export interface IbcPacketSender extends BaseContract { 35 | connect(runner?: ContractRunner | null): IbcPacketSender; 36 | waitForDeployment(): Promise; 37 | 38 | interface: IbcPacketSenderInterface; 39 | 40 | queryFilter( 41 | event: TCEvent, 42 | fromBlockOrBlockhash?: string | number | undefined, 43 | toBlock?: string | number | undefined 44 | ): Promise>>; 45 | queryFilter( 46 | filter: TypedDeferredTopicFilter, 47 | fromBlockOrBlockhash?: string | number | undefined, 48 | toBlock?: string | number | undefined 49 | ): Promise>>; 50 | 51 | on( 52 | event: TCEvent, 53 | listener: TypedListener 54 | ): Promise; 55 | on( 56 | filter: TypedDeferredTopicFilter, 57 | listener: TypedListener 58 | ): Promise; 59 | 60 | once( 61 | event: TCEvent, 62 | listener: TypedListener 63 | ): Promise; 64 | once( 65 | filter: TypedDeferredTopicFilter, 66 | listener: TypedListener 67 | ): Promise; 68 | 69 | listeners( 70 | event: TCEvent 71 | ): Promise>>; 72 | listeners(eventName?: string): Promise>; 73 | removeAllListeners( 74 | event?: TCEvent 75 | ): Promise; 76 | 77 | sendPacket: TypedContractMethod< 78 | [channelId: BytesLike, payload: BytesLike, timeoutTimestamp: BigNumberish], 79 | [bigint], 80 | "nonpayable" 81 | >; 82 | 83 | getFunction( 84 | key: string | FunctionFragment 85 | ): T; 86 | 87 | getFunction( 88 | nameOrSignature: "sendPacket" 89 | ): TypedContractMethod< 90 | [channelId: BytesLike, payload: BytesLike, timeoutTimestamp: BigNumberish], 91 | [bigint], 92 | "nonpayable" 93 | >; 94 | 95 | filters: {}; 96 | } 97 | -------------------------------------------------------------------------------- /src/evm/contracts/IbcDispatcher.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { IbcDispatcher } from "./IbcDispatcher"; 5 | export type { IbcEventsEmitter } from "./IbcEventsEmitter"; 6 | export type { IbcPacketSender } from "./IbcPacketSender"; 7 | -------------------------------------------------------------------------------- /src/evm/contracts/IbcUtils.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumberish, 7 | BytesLike, 8 | FunctionFragment, 9 | Result, 10 | Interface, 11 | ContractRunner, 12 | ContractMethod, 13 | Listener, 14 | } from "ethers"; 15 | import type { 16 | TypedContractEvent, 17 | TypedDeferredTopicFilter, 18 | TypedEventLog, 19 | TypedListener, 20 | TypedContractMethod, 21 | } from "./common"; 22 | 23 | export type UniversalPacketStruct = { 24 | srcPortAddr: BytesLike; 25 | mwBitmap: BigNumberish; 26 | destPortAddr: BytesLike; 27 | appData: BytesLike; 28 | }; 29 | 30 | export type UniversalPacketStructOutput = [ 31 | srcPortAddr: string, 32 | mwBitmap: bigint, 33 | destPortAddr: string, 34 | appData: string 35 | ] & { 36 | srcPortAddr: string; 37 | mwBitmap: bigint; 38 | destPortAddr: string; 39 | appData: string; 40 | }; 41 | 42 | export interface IbcUtilsInterface extends Interface { 43 | getFunction( 44 | nameOrSignature: "fromUniversalPacketBytes" | "hexStrToAddress" 45 | ): FunctionFragment; 46 | 47 | encodeFunctionData( 48 | functionFragment: "fromUniversalPacketBytes", 49 | values: [BytesLike] 50 | ): string; 51 | encodeFunctionData( 52 | functionFragment: "hexStrToAddress", 53 | values: [string] 54 | ): string; 55 | 56 | decodeFunctionResult( 57 | functionFragment: "fromUniversalPacketBytes", 58 | data: BytesLike 59 | ): Result; 60 | decodeFunctionResult( 61 | functionFragment: "hexStrToAddress", 62 | data: BytesLike 63 | ): Result; 64 | } 65 | 66 | export interface IbcUtils extends BaseContract { 67 | connect(runner?: ContractRunner | null): IbcUtils; 68 | waitForDeployment(): Promise; 69 | 70 | interface: IbcUtilsInterface; 71 | 72 | queryFilter( 73 | event: TCEvent, 74 | fromBlockOrBlockhash?: string | number | undefined, 75 | toBlock?: string | number | undefined 76 | ): Promise>>; 77 | queryFilter( 78 | filter: TypedDeferredTopicFilter, 79 | fromBlockOrBlockhash?: string | number | undefined, 80 | toBlock?: string | number | undefined 81 | ): Promise>>; 82 | 83 | on( 84 | event: TCEvent, 85 | listener: TypedListener 86 | ): Promise; 87 | on( 88 | filter: TypedDeferredTopicFilter, 89 | listener: TypedListener 90 | ): Promise; 91 | 92 | once( 93 | event: TCEvent, 94 | listener: TypedListener 95 | ): Promise; 96 | once( 97 | filter: TypedDeferredTopicFilter, 98 | listener: TypedListener 99 | ): Promise; 100 | 101 | listeners( 102 | event: TCEvent 103 | ): Promise>>; 104 | listeners(eventName?: string): Promise>; 105 | removeAllListeners( 106 | event?: TCEvent 107 | ): Promise; 108 | 109 | fromUniversalPacketBytes: TypedContractMethod< 110 | [data: BytesLike], 111 | [UniversalPacketStructOutput], 112 | "view" 113 | >; 114 | 115 | hexStrToAddress: TypedContractMethod<[hexStr: string], [string], "view">; 116 | 117 | getFunction( 118 | key: string | FunctionFragment 119 | ): T; 120 | 121 | getFunction( 122 | nameOrSignature: "fromUniversalPacketBytes" 123 | ): TypedContractMethod< 124 | [data: BytesLike], 125 | [UniversalPacketStructOutput], 126 | "view" 127 | >; 128 | getFunction( 129 | nameOrSignature: "hexStrToAddress" 130 | ): TypedContractMethod<[hexStr: string], [string], "view">; 131 | 132 | filters: {}; 133 | } 134 | -------------------------------------------------------------------------------- /src/evm/contracts/Mars.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { Mars } from "./Mars"; 5 | export type { PanickingMars } from "./PanickingMars"; 6 | export type { RevertingBytesMars } from "./RevertingBytesMars"; 7 | export type { RevertingEmptyMars } from "./RevertingEmptyMars"; 8 | export type { RevertingStringCloseChannelMars } from "./RevertingStringCloseChannelMars"; 9 | export type { RevertingStringMars } from "./RevertingStringMars"; 10 | -------------------------------------------------------------------------------- /src/evm/contracts/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | FunctionFragment, 6 | Typed, 7 | EventFragment, 8 | ContractTransaction, 9 | ContractTransactionResponse, 10 | DeferredTopicFilter, 11 | EventLog, 12 | TransactionRequest, 13 | LogDescription, 14 | } from "ethers"; 15 | 16 | export interface TypedDeferredTopicFilter<_TCEvent extends TypedContractEvent> 17 | extends DeferredTopicFilter {} 18 | 19 | export interface TypedContractEvent< 20 | InputTuple extends Array = any, 21 | OutputTuple extends Array = any, 22 | OutputObject = any 23 | > { 24 | (...args: Partial): TypedDeferredTopicFilter< 25 | TypedContractEvent 26 | >; 27 | name: string; 28 | fragment: EventFragment; 29 | getFragment(...args: Partial): EventFragment; 30 | } 31 | 32 | type __TypechainAOutputTuple = T extends TypedContractEvent< 33 | infer _U, 34 | infer W 35 | > 36 | ? W 37 | : never; 38 | type __TypechainOutputObject = T extends TypedContractEvent< 39 | infer _U, 40 | infer _W, 41 | infer V 42 | > 43 | ? V 44 | : never; 45 | 46 | export interface TypedEventLog 47 | extends Omit { 48 | args: __TypechainAOutputTuple & __TypechainOutputObject; 49 | } 50 | 51 | export interface TypedLogDescription 52 | extends Omit { 53 | args: __TypechainAOutputTuple & __TypechainOutputObject; 54 | } 55 | 56 | export type TypedListener = ( 57 | ...listenerArg: [ 58 | ...__TypechainAOutputTuple, 59 | TypedEventLog, 60 | ...undefined[] 61 | ] 62 | ) => void; 63 | 64 | export type MinEthersFactory = { 65 | deploy(...a: ARGS[]): Promise; 66 | }; 67 | 68 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 69 | infer C, 70 | any 71 | > 72 | ? C 73 | : never; 74 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 75 | ? Parameters 76 | : never; 77 | 78 | export type StateMutability = "nonpayable" | "payable" | "view"; 79 | 80 | export type BaseOverrides = Omit; 81 | export type NonPayableOverrides = Omit< 82 | BaseOverrides, 83 | "value" | "blockTag" | "enableCcipRead" 84 | >; 85 | export type PayableOverrides = Omit< 86 | BaseOverrides, 87 | "blockTag" | "enableCcipRead" 88 | >; 89 | export type ViewOverrides = Omit; 90 | export type Overrides = S extends "nonpayable" 91 | ? NonPayableOverrides 92 | : S extends "payable" 93 | ? PayableOverrides 94 | : ViewOverrides; 95 | 96 | export type PostfixOverrides, S extends StateMutability> = 97 | | A 98 | | [...A, Overrides]; 99 | export type ContractMethodArgs< 100 | A extends Array, 101 | S extends StateMutability 102 | > = PostfixOverrides<{ [I in keyof A]-?: A[I] | Typed }, S>; 103 | 104 | export type DefaultReturnType = R extends Array ? R[0] : R; 105 | 106 | // export interface ContractMethod = Array, R = any, D extends R | ContractTransactionResponse = R | ContractTransactionResponse> { 107 | export interface TypedContractMethod< 108 | A extends Array = Array, 109 | R = any, 110 | S extends StateMutability = "payable" 111 | > { 112 | (...args: ContractMethodArgs): S extends "view" 113 | ? Promise> 114 | : Promise; 115 | 116 | name: string; 117 | 118 | fragment: FunctionFragment; 119 | 120 | getFragment(...args: ContractMethodArgs): FunctionFragment; 121 | 122 | populateTransaction( 123 | ...args: ContractMethodArgs 124 | ): Promise; 125 | staticCall( 126 | ...args: ContractMethodArgs 127 | ): Promise>; 128 | send(...args: ContractMethodArgs): Promise; 129 | estimateGas(...args: ContractMethodArgs): Promise; 130 | staticCallResult(...args: ContractMethodArgs): Promise; 131 | } 132 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/ICrossL2Prover__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Interface, type ContractRunner } from "ethers"; 6 | import type { 7 | ICrossL2Prover, 8 | ICrossL2ProverInterface, 9 | } from "../ICrossL2Prover"; 10 | 11 | const _abi = [ 12 | { 13 | type: "function", 14 | name: "LIGHT_CLIENT_TYPE", 15 | inputs: [], 16 | outputs: [ 17 | { 18 | name: "", 19 | type: "uint8", 20 | internalType: "enum LightClientType", 21 | }, 22 | ], 23 | stateMutability: "view", 24 | }, 25 | { 26 | type: "function", 27 | name: "getState", 28 | inputs: [ 29 | { 30 | name: "height", 31 | type: "uint256", 32 | internalType: "uint256", 33 | }, 34 | ], 35 | outputs: [ 36 | { 37 | name: "", 38 | type: "uint256", 39 | internalType: "uint256", 40 | }, 41 | ], 42 | stateMutability: "view", 43 | }, 44 | { 45 | type: "function", 46 | name: "updateClient", 47 | inputs: [ 48 | { 49 | name: "proof", 50 | type: "bytes", 51 | internalType: "bytes", 52 | }, 53 | { 54 | name: "height", 55 | type: "uint256", 56 | internalType: "uint256", 57 | }, 58 | { 59 | name: "appHash", 60 | type: "uint256", 61 | internalType: "uint256", 62 | }, 63 | ], 64 | outputs: [], 65 | stateMutability: "nonpayable", 66 | }, 67 | { 68 | type: "function", 69 | name: "validateEvent", 70 | inputs: [ 71 | { 72 | name: "logIndex", 73 | type: "uint256", 74 | internalType: "uint256", 75 | }, 76 | { 77 | name: "proof", 78 | type: "bytes", 79 | internalType: "bytes", 80 | }, 81 | ], 82 | outputs: [ 83 | { 84 | name: "chainId", 85 | type: "string", 86 | internalType: "string", 87 | }, 88 | { 89 | name: "emittingContract", 90 | type: "address", 91 | internalType: "address", 92 | }, 93 | { 94 | name: "topics", 95 | type: "bytes[]", 96 | internalType: "bytes[]", 97 | }, 98 | { 99 | name: "unindexedData", 100 | type: "bytes", 101 | internalType: "bytes", 102 | }, 103 | ], 104 | stateMutability: "view", 105 | }, 106 | { 107 | type: "function", 108 | name: "validateReceipt", 109 | inputs: [ 110 | { 111 | name: "proof", 112 | type: "bytes", 113 | internalType: "bytes", 114 | }, 115 | ], 116 | outputs: [ 117 | { 118 | name: "srcChainId", 119 | type: "string", 120 | internalType: "string", 121 | }, 122 | { 123 | name: "receiptRLP", 124 | type: "bytes", 125 | internalType: "bytes", 126 | }, 127 | ], 128 | stateMutability: "view", 129 | }, 130 | ] as const; 131 | 132 | export class ICrossL2Prover__factory { 133 | static readonly abi = _abi; 134 | static createInterface(): ICrossL2ProverInterface { 135 | return new Interface(_abi) as ICrossL2ProverInterface; 136 | } 137 | static connect( 138 | address: string, 139 | runner?: ContractRunner | null 140 | ): ICrossL2Prover { 141 | return new Contract(address, _abi, runner) as unknown as ICrossL2Prover; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/ILightClient.sol/IClientUpdates__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Interface, type ContractRunner } from "ethers"; 6 | import type { 7 | IClientUpdates, 8 | IClientUpdatesInterface, 9 | } from "../../ILightClient.sol/IClientUpdates"; 10 | 11 | const _abi = [ 12 | { 13 | type: "function", 14 | name: "LIGHT_CLIENT_TYPE", 15 | inputs: [], 16 | outputs: [ 17 | { 18 | name: "", 19 | type: "uint8", 20 | internalType: "enum LightClientType", 21 | }, 22 | ], 23 | stateMutability: "view", 24 | }, 25 | { 26 | type: "function", 27 | name: "updateClient", 28 | inputs: [ 29 | { 30 | name: "proof", 31 | type: "bytes", 32 | internalType: "bytes", 33 | }, 34 | { 35 | name: "height", 36 | type: "uint256", 37 | internalType: "uint256", 38 | }, 39 | { 40 | name: "appHash", 41 | type: "uint256", 42 | internalType: "uint256", 43 | }, 44 | ], 45 | outputs: [], 46 | stateMutability: "nonpayable", 47 | }, 48 | ] as const; 49 | 50 | export class IClientUpdates__factory { 51 | static readonly abi = _abi; 52 | static createInterface(): IClientUpdatesInterface { 53 | return new Interface(_abi) as IClientUpdatesInterface; 54 | } 55 | static connect( 56 | address: string, 57 | runner?: ContractRunner | null 58 | ): IClientUpdates { 59 | return new Contract(address, _abi, runner) as unknown as IClientUpdates; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/ILightClient.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { IClientUpdates__factory } from "./IClientUpdates__factory"; 5 | export { ILightClient__factory } from "./ILightClient__factory"; 6 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/IProofVerifier.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { IAppStateVerifier__factory } from "./IAppStateVerifier__factory"; 5 | export { IProofVerifier__factory } from "./IProofVerifier__factory"; 6 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/IbcDispatcher.sol/IbcPacketSender__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Interface, type ContractRunner } from "ethers"; 6 | import type { 7 | IbcPacketSender, 8 | IbcPacketSenderInterface, 9 | } from "../../IbcDispatcher.sol/IbcPacketSender"; 10 | 11 | const _abi = [ 12 | { 13 | type: "function", 14 | name: "sendPacket", 15 | inputs: [ 16 | { 17 | name: "channelId", 18 | type: "bytes32", 19 | internalType: "bytes32", 20 | }, 21 | { 22 | name: "payload", 23 | type: "bytes", 24 | internalType: "bytes", 25 | }, 26 | { 27 | name: "timeoutTimestamp", 28 | type: "uint64", 29 | internalType: "uint64", 30 | }, 31 | ], 32 | outputs: [ 33 | { 34 | name: "sequence", 35 | type: "uint64", 36 | internalType: "uint64", 37 | }, 38 | ], 39 | stateMutability: "nonpayable", 40 | }, 41 | ] as const; 42 | 43 | export class IbcPacketSender__factory { 44 | static readonly abi = _abi; 45 | static createInterface(): IbcPacketSenderInterface { 46 | return new Interface(_abi) as IbcPacketSenderInterface; 47 | } 48 | static connect( 49 | address: string, 50 | runner?: ContractRunner | null 51 | ): IbcPacketSender { 52 | return new Contract(address, _abi, runner) as unknown as IbcPacketSender; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/IbcDispatcher.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { IbcDispatcher__factory } from "./IbcDispatcher__factory"; 5 | export { IbcEventsEmitter__factory } from "./IbcEventsEmitter__factory"; 6 | export { IbcPacketSender__factory } from "./IbcPacketSender__factory"; 7 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/Mars.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { Mars__factory } from "./Mars__factory"; 5 | export { PanickingMars__factory } from "./PanickingMars__factory"; 6 | export { RevertingBytesMars__factory } from "./RevertingBytesMars__factory"; 7 | export { RevertingEmptyMars__factory } from "./RevertingEmptyMars__factory"; 8 | export { RevertingStringCloseChannelMars__factory } from "./RevertingStringCloseChannelMars__factory"; 9 | export { RevertingStringMars__factory } from "./RevertingStringMars__factory"; 10 | -------------------------------------------------------------------------------- /src/evm/contracts/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export * as iLightClientSol from "./ILightClient.sol"; 5 | export * as iProofVerifierSol from "./IProofVerifier.sol"; 6 | export * as ibcDispatcherSol from "./IbcDispatcher.sol"; 7 | export * as marsSol from "./Mars.sol"; 8 | export { CrossL2Prover__factory } from "./CrossL2Prover__factory"; 9 | export { Dispatcher__factory } from "./Dispatcher__factory"; 10 | export { DummyLightClient__factory } from "./DummyLightClient__factory"; 11 | export { DummyProofVerifier__factory } from "./DummyProofVerifier__factory"; 12 | export { ERC1967Proxy__factory } from "./ERC1967Proxy__factory"; 13 | export { Earth__factory } from "./Earth__factory"; 14 | export { FeeVault__factory } from "./FeeVault__factory"; 15 | export { ICrossL2Prover__factory } from "./ICrossL2Prover__factory"; 16 | export { IDispatcher__factory } from "./IDispatcher__factory"; 17 | export { IFeeVault__factory } from "./IFeeVault__factory"; 18 | export { IUniversalChannelHandler__factory } from "./IUniversalChannelHandler__factory"; 19 | export { Ibc__factory } from "./Ibc__factory"; 20 | export { IbcUtils__factory } from "./IbcUtils__factory"; 21 | export { Moon__factory } from "./Moon__factory"; 22 | export { OptimisticLightClient__factory } from "./OptimisticLightClient__factory"; 23 | export { OptimisticProofVerifier__factory } from "./OptimisticProofVerifier__factory"; 24 | export { SequencerSignatureVerifier__factory } from "./SequencerSignatureVerifier__factory"; 25 | export { SequencerSoloClient__factory } from "./SequencerSoloClient__factory"; 26 | export { UniversalChannelHandler__factory } from "./UniversalChannelHandler__factory"; 27 | export { Venus__factory } from "./Venus__factory"; 28 | -------------------------------------------------------------------------------- /src/evm/index.ts: -------------------------------------------------------------------------------- 1 | export * as chain from "./chain"; -------------------------------------------------------------------------------- /src/evm/schemas/contract.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Registry } from "../../utils/registry"; 3 | import { parseZodSchema } from "../../utils/io"; 4 | 5 | // A contract may or may not be deployed (null address). 6 | export const ContractItemSchema = z 7 | .object({ 8 | name: z.string().min(1), 9 | description: z.optional(z.string()), 10 | factoryName: z.optional(z.string()), 11 | deployArgs: z.optional(z.array(z.any())), 12 | libraries: z.optional( 13 | z.array( 14 | z.object({ 15 | name: z.string().min(1), 16 | address: z.string().min(1), 17 | }) 18 | ) 19 | ), 20 | // either a account name from account registry, or a private key or a mnemonic signer 21 | deployer: z.string().nullish(), 22 | address: z.string().nullish(), 23 | init: z.optional( 24 | z.object({ 25 | signature: z.string().min(1), 26 | args: z.array(z.string().min(1)), 27 | }) 28 | ), 29 | abi: z.optional(z.any()), 30 | solcVersion: z.optional(z.string()), 31 | }) 32 | .strict(); 33 | 34 | const ContractItemList = z.array(ContractItemSchema); 35 | const registryName = "contracts"; 36 | 37 | const MultiChainContractRegistrySchema = z.array( 38 | z.object({ chainName: z.string().min(1), [registryName]: ContractItemList }) 39 | ); 40 | 41 | export type ContractItem = z.infer; 42 | 43 | // export type ContractRegistry = Registry 44 | export type MultiChainContractRegistry = Registry<{ 45 | chainName: string; 46 | [registryName]: ContractRegistry; 47 | }>; 48 | 49 | export class ContractRegistry extends Registry {} 50 | 51 | export class ContractRegistryLoader { 52 | static loadSingle(config: any): ContractRegistry { 53 | return loadContractRegistry(config); 54 | } 55 | 56 | static loadMultiple(config: any): MultiChainContractRegistry { 57 | return loadMultiChainContractRegistry(config); 58 | } 59 | 60 | static emptyMultiple(): MultiChainContractRegistry { 61 | // @ts-ignore 62 | return new Registry<{ chainName: string; contracts: ContractRegistry }>( 63 | [], 64 | { 65 | // @ts-ignore 66 | toObj: (c) => { 67 | return { 68 | chainName: c.chainName, 69 | [registryName]: c.contracts.serialize(), 70 | }; 71 | }, 72 | } 73 | ); 74 | } 75 | 76 | static newMultiple( 77 | items: { chainName: string; contracts: ContractRegistry }[] 78 | ): MultiChainContractRegistry { 79 | // @ts-ignore 80 | return new Registry(items, { 81 | nameFunc: (c) => c.chainName, 82 | toObj: (c) => { 83 | return { 84 | chainName: c.chainName, 85 | [registryName]: c.contracts.serialize(), 86 | }; 87 | }, 88 | }); 89 | } 90 | 91 | static emptySingle(): ContractRegistry { 92 | return new ContractRegistry([], { nameInParent: registryName }); 93 | } 94 | } 95 | 96 | function loadContractRegistry(config: any): ContractRegistry { 97 | const parsed = parseZodSchema( 98 | "ContractRegistry", 99 | config, 100 | ContractItemList.parse 101 | ); 102 | return new ContractRegistry(parsed, { 103 | nameFunc: (c) => c.name, 104 | nameInParent: registryName, 105 | }); 106 | } 107 | 108 | function loadMultiChainContractRegistry( 109 | config: any 110 | ): MultiChainContractRegistry { 111 | const parsed = parseZodSchema( 112 | "MultiChainContractRegistry", 113 | config, 114 | MultiChainContractRegistrySchema.parse 115 | ); 116 | const contractRegistries = parsed.map((item) => ({ 117 | chainName: item.chainName, 118 | contracts: loadContractRegistry(item.contracts), 119 | })); 120 | return ContractRegistryLoader.newMultiple(contractRegistries); 121 | } 122 | -------------------------------------------------------------------------------- /src/evm/schemas/contractUpdate.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { TxItemSchema } from "./tx"; 3 | import { ContractItemSchema } from "./contract"; 4 | import { Registry } from "../../utils/registry"; 5 | 6 | // Represents the updates we can make to a contract, either deploying a new one or sending a tx to an existing one. 7 | export const UpdateContractConfigSchema = z.union([ 8 | TxItemSchema, 9 | ContractItemSchema, 10 | ]); 11 | type UpdateContractConfig = z.infer; 12 | export class UpdateContractRegistry extends Registry {} 13 | 14 | // Load a parsed config file into a contract update registry that can be passed into the updateContract method 15 | export function loadContractUpdateRegistry( 16 | config: any 17 | ): UpdateContractRegistry { 18 | const parsed = config.map((c: any) => { 19 | const parsed = UpdateContractConfigSchema.safeParse(c); 20 | if (!parsed.success) { 21 | throw new Error(`Invalid contract update config: ${parsed.error.errors}`); 22 | } 23 | return parsed.data; 24 | }); 25 | return new UpdateContractRegistry(parsed, { 26 | nameFunc: (c) => c.name, 27 | nameInParent: "updateContracts", 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/evm/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export * as contractUpdate from "./contractUpdate" 2 | export * as tx from "./tx" 3 | export * as account from "./account" 4 | export * as contract from "./contract" -------------------------------------------------------------------------------- /src/evm/schemas/multisig.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { wallet } from './wallet'; 3 | 4 | // defined in an account spec, which will be converted into an initialized multisig config once we deploy the multisig contract 5 | export const uninitializedMultisigConfig = z 6 | .object({ 7 | name: z.string().min(1), 8 | privateKey: z.string().min(1), 9 | owners: z.array(z.string().min(1)), 10 | threshold: z.number(), 11 | txServiceUrl: z.string().optional() 12 | }) 13 | .strict(); 14 | 15 | // Defined in an account spec, which is not necessarily in a config 16 | export const initializedMultisigConfig = z 17 | .object({ 18 | name: z.string().min(1), 19 | chainId: z.number(), 20 | privateKey: z.string().min(1), 21 | safeAddress: z.string().min(1), 22 | txServiceUrl: z.string().optional(), 23 | }) 24 | .strict(); 25 | 26 | // Multisig which is described in an account spec but is not yet initialized. (i.e. multisig contract has not been deployed yet) 27 | export const unInitializedMultisig = z.object({ 28 | name: z.string().min(1), 29 | privateKey: z.string().min(1), 30 | owners: z.array(z.string().min(1)), 31 | threshold: z.number(), 32 | wallet: wallet, 33 | }); 34 | 35 | // Multisig which has been deployed & can be used to propose transactions. This is the type that loadEvmAccounts will return for multisig types 36 | export const initializedMultisig = z.object({ 37 | name: z.string().min(1), 38 | chainId: z.number(), 39 | privateKey: z.string().min(1), 40 | safeAddress: z.string().min(1), 41 | wallet: wallet, 42 | txServiceUrl: z.string().optional(), 43 | }); 44 | 45 | export type UninitializedMultisigConfig = z.infer< 46 | typeof uninitializedMultisigConfig 47 | >; 48 | export type InitializedMultisigConfig = z.infer< 49 | typeof initializedMultisigConfig 50 | >; 51 | export type UninitializedMultisig = z.infer; 52 | export type InitializedMultisig = z.infer; 53 | 54 | // Type Guards 55 | export const isUninitializedMultisigConfig = ( 56 | account: unknown 57 | ): account is UninitializedMultisigConfig => { 58 | return uninitializedMultisigConfig.safeParse(account).success; 59 | }; 60 | 61 | export const isUninitializedMultisig = ( 62 | account: unknown 63 | ): account is UninitializedMultisig => { 64 | return unInitializedMultisig.safeParse(account).success; 65 | }; 66 | 67 | export const isInitializedMultisigConfig = ( 68 | account: unknown 69 | ): account is InitializedMultisigConfig => { 70 | return initializedMultisigConfig.safeParse(account).success; 71 | }; 72 | 73 | export const isInitializedMultisig = ( 74 | account: unknown 75 | ): account is InitializedMultisig => { 76 | return initializedMultisig.safeParse(account).success; 77 | }; 78 | 79 | export const isMultisig = ( 80 | account: unknown 81 | ): account is InitializedMultisig | UninitializedMultisig => { 82 | return isInitializedMultisig(account) || isUninitializedMultisig(account); 83 | }; 84 | 85 | export const isMultisigConfig = ( 86 | account: unknown 87 | ): account is InitializedMultisigConfig | UninitializedMultisigConfig => { 88 | return ( 89 | isInitializedMultisigConfig(account) || 90 | isUninitializedMultisigConfig(account) 91 | ); 92 | }; 93 | -------------------------------------------------------------------------------- /src/evm/schemas/tx.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Registry } from "../../utils/registry"; 3 | import { parseZodSchema } from "../../utils/io"; 4 | 5 | export const TxItemSchema = z 6 | .object({ 7 | name: z.string().min(1), 8 | description: z.optional(z.string()), 9 | // either a account name from account registry, or a private key or a mnemonic signer 10 | deployer: z.string().nullish(), 11 | signature: z.string().min(1), 12 | address: z.string().nullish(), 13 | factoryName: z.optional(z.string()), 14 | args: z.optional(z.array(z.any())), 15 | init: z.optional( 16 | z.object({ 17 | signature: z.string().min(1), 18 | args: z.array(z.string().min(1)), 19 | }) 20 | ), 21 | }) 22 | .strict(); 23 | 24 | export type TxItem = z.infer; 25 | const TxItemList = z.array(TxItemSchema); 26 | 27 | export class TxRegistry extends Registry {} 28 | 29 | export function loadTxRegistry(config: any): TxRegistry { 30 | const parsed = parseZodSchema("TxRegistry", config, TxItemList.parse); 31 | return new TxRegistry(parsed, { 32 | nameFunc: (c) => c.name, 33 | nameInParent: "transactions", 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /src/evm/schemas/wallet.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { z } from 'zod'; 3 | 4 | export const wallet = z.union([ 5 | z.instanceof(ethers.Wallet), 6 | z.instanceof(ethers.HDNodeWallet), 7 | ]); 8 | 9 | export const privateKey = z 10 | .object({ 11 | name: z.string().min(1), 12 | // privateKey should be a hex string prefixed with 0x 13 | privateKey: z.string().min(1), 14 | }) 15 | .strict(); 16 | 17 | export const mnemonic = z 18 | .object({ 19 | name: z.string().min(1), 20 | // a 12-word mnemonic; or more words per BIP-39 spec 21 | mnemonic: z.string().min(1), 22 | path: z.optional(z.string().min(1)), 23 | index: z.optional(z.number().int().min(0)), 24 | }) 25 | .strict(); 26 | 27 | export const singleSigAccount = z.union([privateKey, mnemonic]); 28 | 29 | export type PrivateKey = z.infer; 30 | export type Mnemonic = z.infer; 31 | export type SingleSigAccount = z.infer; 32 | 33 | export const isPrivateKey = (account: unknown): account is PrivateKey=> { 34 | return privateKey.safeParse(account).success; 35 | }; 36 | 37 | export const isMnemonic = (account: unknown): account is Mnemonic=> { 38 | return mnemonic.safeParse(account).success; 39 | }; 40 | 41 | export const isSingleSigAccount = ( 42 | account: unknown 43 | ): account is SingleSigAccount=> { 44 | return singleSigAccount.safeParse(account).success; 45 | }; 46 | 47 | export const isWallet = (account: unknown): account is Wallet => { 48 | return wallet.safeParse(account).success; 49 | } 50 | 51 | export type Wallet = ethers.Wallet | ethers.HDNodeWallet; 52 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { SingleSigAccountRegistry} from "./evm/schemas/account"; 2 | import { Chain } from "./evm/chain"; 3 | import { Registry } from "./utils/registry"; 4 | import { ContractRegistryLoader } from "./evm/schemas/contract"; 5 | import { parseObjFromFile } from "./utils/io"; 6 | import { loadEvmAccounts } from "./evm/schemas/account"; 7 | import { updateContractsForChain } from "./updateContract"; 8 | 9 | export { 10 | updateContractsForChain, 11 | Chain, 12 | Registry, 13 | loadEvmAccounts, 14 | parseObjFromFile, 15 | SingleSigAccountRegistry, 16 | ContractRegistryLoader, 17 | }; 18 | -------------------------------------------------------------------------------- /src/scripts/deploy-multisig.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { ethers, toBigInt } from "ethers"; 3 | import { SingleSigAccountRegistry, parseObjFromFile } from ".."; 4 | import { newSafeFromOwner } from "../multisig/safe"; 5 | 6 | import { 7 | parseMultisigInitArgsFromCLI, 8 | saveMultisigAddressToAccountsSpec, 9 | } from "../utils/io"; 10 | import { SendingAccountRegistry } from "../evm/schemas/sendingAccount"; 11 | import { isUninitializedMultisig } from "../evm/schemas/multisig"; 12 | 13 | async function main() { 14 | const { rpcUrl, initiator, accountsSpecPath, chainId } = 15 | await parseMultisigInitArgsFromCLI(); 16 | 17 | const accountConfigFromYaml = { 18 | name: "multisig-accounts", 19 | registry: parseObjFromFile(accountsSpecPath), 20 | }; 21 | 22 | const accounts = SendingAccountRegistry.loadMultiple([ 23 | accountConfigFromYaml, 24 | ]).mustGet("multisig-accounts"); 25 | 26 | const multisigAccount = accounts.mustGet(initiator); 27 | 28 | if (!isUninitializedMultisig(multisigAccount)) { 29 | throw new Error( 30 | "Account read from yaml but isn't a multisig account that needs to be initialized." 31 | ); 32 | } 33 | 34 | const senderPrivateKey = accounts.getSinglePrivateKeyFromAccount(initiator); 35 | if (!senderPrivateKey) { 36 | throw new Error(`Could not find private key for owner ${initiator}`); 37 | } 38 | 39 | const provider = new ethers.JsonRpcProvider(rpcUrl); 40 | const providerChainId = (await provider.getNetwork()).chainId; 41 | if (!providerChainId || providerChainId !== toBigInt(chainId)) { 42 | throw new Error( 43 | `Chain id mismatch between multisig account and rpc url. ${chainId} is specified in accounts spec, but ${providerChainId} is the chain id of the rpc url` 44 | ); 45 | } 46 | 47 | const newSafeAddress = await newSafeFromOwner( 48 | rpcUrl, 49 | senderPrivateKey, 50 | multisigAccount.owners, 51 | multisigAccount.threshold 52 | ); 53 | 54 | await saveMultisigAddressToAccountsSpec( 55 | newSafeAddress, 56 | accountsSpecPath, 57 | chainId, 58 | initiator 59 | ); 60 | } 61 | 62 | main(); 63 | -------------------------------------------------------------------------------- /src/scripts/execute-multisig-tx.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { parseObjFromFile } from ".."; 3 | import { executeMultisigTx } from "../multisig/safe"; 4 | 5 | import { parseExecuteMultisigTxArgsFromCLI } from "../utils/io"; 6 | import { isInitializedMultisig } from "../evm/schemas/multisig"; 7 | import { SendingAccountRegistry } from "../evm/schemas/sendingAccount"; 8 | 9 | async function main() { 10 | const { executor, rpcUrl, txIndex, accountsSpecPath } = 11 | await parseExecuteMultisigTxArgsFromCLI(); 12 | 13 | const accounts = SendingAccountRegistry.load( 14 | parseObjFromFile(accountsSpecPath), 15 | "multisig-accounts" 16 | ); 17 | 18 | const multisigAccount = accounts.mustGet(executor); 19 | if (!isInitializedMultisig(multisigAccount)) { 20 | throw new Error("Can only execute transactions on a multisig wallet"); 21 | } 22 | 23 | executeMultisigTx( 24 | multisigAccount, 25 | rpcUrl, 26 | txIndex 27 | ); 28 | } 29 | 30 | main(); 31 | -------------------------------------------------------------------------------- /src/scripts/fork-deployment-test.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { createWriteStream } from "fs"; 4 | import { $ } from "zx"; 5 | import { MODULE_ROOT_PATH } from "../utils/constants"; 6 | import { 7 | parseArgsFromCLI, 8 | parseObjFromFile, 9 | readDeploymentFilesIntoEnv, 10 | } from "../utils/io"; 11 | import { ContractRegistryLoader } from "../evm/schemas/contract"; 12 | import { getOutputLogger } from "../utils/cli"; 13 | import { loadContractUpdateRegistry } from "../evm/schemas/contractUpdate"; 14 | import { updateContractsForChain } from "../updateContract"; 15 | 16 | const main = async () => { 17 | const { chain, accounts, updateSpecs, anvilPort } = await parseArgsFromCLI(); 18 | 19 | const { anvilProcess } = await startAnvilServer( 20 | chain.rpc, 21 | anvilPort, 22 | `anvil-${anvilPort}.out` 23 | ); 24 | const anvilUrl = `http://127.0.0.1:${anvilPort}`; 25 | 26 | const forkedChain = { ...chain, rpc: anvilUrl }; 27 | 28 | const contractUpdates = loadContractUpdateRegistry( 29 | parseObjFromFile(updateSpecs) 30 | ); 31 | 32 | await updateContractsForChain( 33 | forkedChain, 34 | accounts.mustGet(chain.chainName), 35 | ContractRegistryLoader.emptySingle(), 36 | contractUpdates, 37 | getOutputLogger(), 38 | { 39 | dryRun: false, 40 | forceDeployNewContracts: false, 41 | writeContracts: true, 42 | } 43 | ); 44 | 45 | let env = {}; 46 | env = await readDeploymentFilesIntoEnv(env, chain); // Read deployment files from non-forked chain to get live addresses 47 | 48 | $.env = { 49 | ...process.env, 50 | ...env, 51 | }; 52 | 53 | await $`cd ${MODULE_ROOT_PATH} && forge test --match-contract DispatcherDeployTest --fork-url ${anvilUrl} -vvvv `.pipe( 54 | createWriteStream("fork-test.out") 55 | ); 56 | 57 | await anvilProcess.kill(); 58 | }; 59 | 60 | // Starts anvil server from an RPC fork, and waits until it's started 61 | const startAnvilServer = async ( 62 | rpcUrl: string, 63 | port: string, 64 | outFileName: string 65 | ) => { 66 | const p = $`anvil --port ${port} --fork-url ${rpcUrl}` 67 | 68 | p.pipe( 69 | createWriteStream(outFileName) 70 | ); 71 | await waitForAnvilServer(outFileName); 72 | return { anvilProcess: p }; 73 | }; 74 | 75 | const waitForAnvilServer = async (anvilOutFile: string) => { 76 | const checkOutPutContainsListening = async () => { 77 | const out = await $`cat ${anvilOutFile}`; 78 | if (out.stdout.includes("Listening")) { 79 | return true; 80 | } 81 | return false; 82 | }; 83 | // var intervalId = await setInterval(checkOutPutContainsListening, 1000); 84 | while (!(await checkOutPutContainsListening())) { 85 | await setTimeout(() => {}, 1000); 86 | } 87 | return; 88 | }; 89 | 90 | main(); 91 | -------------------------------------------------------------------------------- /src/scripts/update-contracts-script.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { ContractRegistryLoader, parseObjFromFile } from ".."; 3 | import { loadContractUpdateRegistry } from "../evm/schemas/contractUpdate"; 4 | import { updateContractsForChain } from "../updateContract"; 5 | 6 | import { getOutputLogger } from "../utils/cli"; 7 | import { UPDATE_SPECS_PATH } from "../utils/constants"; 8 | import { parseArgsFromCLI } from "../utils/io"; 9 | 10 | async function main() { 11 | const { chain, accounts, args, extraBindingsPath, externalContractsPath } = 12 | await parseArgsFromCLI(); 13 | const updateSpecs = (args.UPDATE_SPECS_PATH as string) || UPDATE_SPECS_PATH; 14 | 15 | const contractUpdates = loadContractUpdateRegistry( 16 | parseObjFromFile(updateSpecs) 17 | ); 18 | 19 | let extraContractFactories: Record | null = null; 20 | if (extraBindingsPath) { 21 | try { 22 | extraContractFactories = await require(extraBindingsPath); 23 | } catch (e) { 24 | throw new Error( 25 | `Failed to import extra contract factories from ${extraBindingsPath}: ${e}` 26 | ); 27 | } 28 | } 29 | 30 | const existingContracts = externalContractsPath 31 | ? ContractRegistryLoader.loadSingle(externalContractsPath) 32 | : ContractRegistryLoader.emptySingle(); 33 | 34 | updateContractsForChain( 35 | chain, 36 | accounts.mustGet(chain.chainName), 37 | existingContracts, 38 | contractUpdates, 39 | getOutputLogger(), 40 | { 41 | dryRun: false, 42 | forceDeployNewContracts: false, 43 | writeContracts: true, 44 | extraContractFactories: extraContractFactories ?? undefined, 45 | } 46 | ); 47 | } 48 | main(); 49 | -------------------------------------------------------------------------------- /src/scripts/verify-contract-script.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { 3 | ChainFolder, 4 | parseObjFromFile, 5 | parseVerifyArgsFromCLI, 6 | readFromDeploymentFile, 7 | readMetadata, 8 | } from "../utils/io"; 9 | import { $, cd } from "zx"; 10 | import { MODULE_ROOT_PATH } from "../utils/constants"; 11 | import { Logger } from "winston"; 12 | import { getMainLogger } from "../utils/cli"; 13 | import { ContractItem, ContractItemSchema } from "../evm/schemas/contract"; 14 | import { loadContractUpdateRegistry } from "../evm/schemas/contractUpdate"; 15 | 16 | const verifyContract = async ( 17 | deployedContract: ContractItem, 18 | chainFolder: ChainFolder, 19 | etherscanApiKey: string, 20 | verifierUrl: string, 21 | logger: Logger 22 | ) => { 23 | // Read deployment file, so that we can find path to artifact 24 | const deployment = await readFromDeploymentFile(deployedContract.name, chainFolder); 25 | const metadata = await readMetadata(deployment.factory, deployedContract.solcVersion); 26 | const compilationTarget = JSON.parse(metadata).settings.compilationTarget; 27 | const contractFile = Object.keys(compilationTarget)[0]; 28 | const contractPath = `${contractFile}:${compilationTarget[contractFile]}`; 29 | 30 | const args = [ 31 | `${deployment.address}`, 32 | `${contractPath}`, 33 | `--etherscan-api-key`, 34 | `${etherscanApiKey}`, 35 | `--verifier-url=${verifierUrl}`, 36 | ]; 37 | // Find libraries into string that foundry verification expects if there are any 38 | let libraries: Record | null = null; 39 | if (deployment.libraries && deployment.libraries.length > 0) { 40 | libraries = deployment.libraries[0] as Record; // Have to parse this out because typechain = weird and stores libs in a 1-length array 41 | Object.keys(libraries).forEach((lib) => { 42 | args.push(`--libraries=${lib}:${libraries![lib]}`); 43 | }); 44 | } 45 | 46 | logger.info( 47 | `verifying ${deployedContract.name}'s deployment with ${deployment.factory} ${ 48 | libraries ? `and libraries ${libraries}` : `` 49 | }` 50 | ); 51 | 52 | // Run foundry verification command 53 | let command; 54 | 55 | // TODO : Add check here for if already in MODULE_ROOT 56 | 57 | cd(MODULE_ROOT_PATH); // cd to contracts directory 58 | try { 59 | command = await $`forge verify-contract ${args} 2>&1 | tee verify-out.txt`; // command = await $`cd ${MODULE_ROOT_PATH} && forge verify-contract ${ 60 | } catch (e) { 61 | logger.error("error ", e); 62 | } 63 | 64 | logger.info("verification result ", command); 65 | }; 66 | /** 67 | * Take a deployment name, and verify the result to blockscout api 68 | */ 69 | async function main() { 70 | const { verifierUrl, chainFolder, etherscanApiKey, updateSpecs } = 71 | await parseVerifyArgsFromCLI(); 72 | 73 | // Fetch spec from contract 74 | 75 | const contractUpdates = loadContractUpdateRegistry( 76 | parseObjFromFile(updateSpecs) 77 | ); 78 | 79 | const logger = getMainLogger(); 80 | for (const contractUpdate of contractUpdates.values()) { 81 | const parsed = ContractItemSchema.safeParse(contractUpdate); 82 | if (parsed.success) { 83 | // Only try to verify contractName if it matches the deploymentName 84 | try { 85 | await verifyContract( 86 | parsed.data, 87 | chainFolder, 88 | etherscanApiKey, 89 | verifierUrl, 90 | logger 91 | ); 92 | } catch (e) { 93 | logger.error( 94 | `Failed to verify contract ${parsed.data.name} with error: ${e}` 95 | ); 96 | } 97 | } 98 | } 99 | } 100 | 101 | main(); 102 | -------------------------------------------------------------------------------- /src/tx.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import { Chain } from "./evm/chain"; 3 | import { TxItem } from "./evm/schemas/tx"; 4 | import { Logger } from "./utils/cli"; 5 | import { StringToStringMap, readFactoryAbi, renderArgs } from "./utils/io"; 6 | import { DEFAULT_DEPLOYER } from "./utils/constants"; 7 | import { ContractRegistry } from "./evm/schemas/contract"; 8 | import { updateNoncesForSender } from "./deploy"; 9 | import { fetchNonceFromSafeAddress, proposeTransaction } from "./multisig/safe"; 10 | import * as vibcContractFactories from "./evm/contracts/index"; 11 | import { isInitializedMultisig } from "./evm/schemas/multisig"; 12 | import { SingleSigAccountRegistry } from "./evm/schemas/account"; 13 | import { SendingAccountRegistry } from "./evm/schemas/sendingAccount"; 14 | import { isWallet } from "./evm/schemas/wallet"; 15 | 16 | export async function readAbiFromDeployedContract( 17 | existingContractOverrides: ContractRegistry, 18 | factoryName: string, 19 | factories: Record = {} 20 | ) { 21 | const existingContractOverride = existingContractOverrides.get(factoryName); 22 | 23 | // Read from overrides first 24 | if (existingContractOverride && existingContractOverride.abi) { 25 | return existingContractOverride.abi; 26 | } 27 | 28 | return readFactoryAbi(factoryName, factories); 29 | } 30 | 31 | export async function sendTx( 32 | chain: Chain, 33 | accountRegistry: SingleSigAccountRegistry | SendingAccountRegistry, 34 | existingContractOverrides: ContractRegistry, 35 | tx: TxItem, 36 | logger: Logger, 37 | dryRun: boolean = false, 38 | nonces: Record, 39 | env: StringToStringMap, 40 | extraContractFactories: Record = {} 41 | ) { 42 | try { 43 | const factoryName = tx.factoryName ? tx.factoryName : tx.name; 44 | const contractFactories = { 45 | ...vibcContractFactories, 46 | ...extraContractFactories, 47 | }; 48 | 49 | const deployedContractAbi = await readAbiFromDeployedContract( 50 | existingContractOverrides, 51 | factoryName, 52 | contractFactories 53 | ); 54 | if (!deployedContractAbi) { 55 | throw new Error(`Could not find ABI for contract ${factoryName}`); 56 | } 57 | 58 | const account = accountRegistry.mustGet( 59 | tx.deployer ? tx.deployer : DEFAULT_DEPLOYER 60 | ); 61 | 62 | const deployedContractAddress = renderArgs([tx.address], tx.init, env)[0]; 63 | 64 | const args = renderArgs(tx.args, tx.init, env); 65 | if (isInitializedMultisig(account)) { 66 | const updatedNonces = await updateNoncesForSender( 67 | nonces, 68 | account.safeAddress, 69 | () => fetchNonceFromSafeAddress(chain.rpc, account.safeAddress) 70 | ); 71 | // If multisig, we don't directly send the tx, but instead propose it to the safe tx service 72 | const contractInterface = new ethers.Interface(deployedContractAbi); 73 | const callData = contractInterface.encodeFunctionData( 74 | tx.signature!, 75 | args 76 | ); 77 | 78 | proposeTransaction( 79 | account, 80 | deployedContractAddress, 81 | callData, 82 | chain.rpc, 83 | updatedNonces[account.safeAddress] 84 | ); 85 | } else if (isWallet(account)) { 86 | // Send from single account 87 | const deployer = account; 88 | const ethersContract = new ethers.Contract( 89 | deployedContractAddress, 90 | deployedContractAbi, 91 | account 92 | ); 93 | 94 | logger.info( 95 | `calling ${tx.signature} on ${tx.name} @:${deployedContractAddress} with args: \n [${args}]` 96 | ); 97 | if (!dryRun) { 98 | const updatedNonces = await updateNoncesForSender( 99 | nonces, 100 | deployer.address, 101 | () => deployer.getNonce() 102 | ); 103 | const overrides = { 104 | nonce: updatedNonces[deployer.address], 105 | }; 106 | const sentTx = await ethersContract.getFunction(tx.signature!)( 107 | ...args, 108 | overrides 109 | ); 110 | try { 111 | await sentTx.wait(); 112 | } catch (err) { 113 | logger.error( 114 | `[${chain.chainName}-${chain.deploymentEnvironment}] sendTx ${tx.name} failed: ${err}` 115 | ); 116 | throw err; 117 | } 118 | } 119 | } else { 120 | throw new Error(`Unknown account type: ${account}`); 121 | } 122 | } catch (err) { 123 | logger.error( 124 | `[${chain.chainName}-${chain.deploymentEnvironment}] sendTx ${tx.name} failed: ${err}` 125 | ); 126 | throw err; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/updateContract.ts: -------------------------------------------------------------------------------- 1 | import { sendTx } from "./tx"; 2 | import { deployContract } from "./deploy"; 3 | import { Chain } from "./evm/chain"; 4 | import { SingleSigAccountRegistry } from "./evm/schemas/account"; 5 | import { ContractItemSchema, ContractRegistry } from "./evm/schemas/contract"; 6 | import { 7 | UpdateContractRegistry, 8 | loadContractUpdateRegistry, 9 | } from "./evm/schemas/contractUpdate"; 10 | import { Logger } from "winston"; 11 | import { readAccountsIntoEnv, readDeploymentFilesIntoEnv } from "./utils/io"; 12 | import { TxItemSchema } from "./evm/schemas/tx"; 13 | import { SendingAccountRegistry } from "./evm/schemas/sendingAccount"; 14 | 15 | type UpdateContractsOpts = { 16 | dryRun?: boolean; 17 | forceDeployNewContracts?: boolean; 18 | writeContracts?: boolean; 19 | extraContractFactories?: Record; 20 | } 21 | 22 | // Combination of sendTxToChain and deployContracts. Can do both from a single deploy file, and uses zod to parse the schema. 23 | export async function updateContractsForChain( 24 | chain: Chain, 25 | accountRegistry: SingleSigAccountRegistry | SendingAccountRegistry, 26 | existingContracts: ContractRegistry, 27 | updateSpec: UpdateContractRegistry, 28 | logger: Logger, 29 | options: UpdateContractsOpts = { 30 | dryRun: false, 31 | forceDeployNewContracts: false, // True if you want to use existing deployments when possible 32 | writeContracts: true, // True if you want to save persisted artifact files. 33 | extraContractFactories: {}, 34 | } 35 | ) { 36 | const { 37 | dryRun, 38 | forceDeployNewContracts, 39 | writeContracts, 40 | extraContractFactories, 41 | } = options; 42 | 43 | logger.info( 44 | `updating ${updateSpec.size} contract(s) for chain ${chain.chainName}-${ 45 | chain.deploymentEnvironment 46 | } with contractNames: [${updateSpec.keys()}]` 47 | ); 48 | 49 | const nonces: Record = {}; // Maps addresses to nonces. Used to avoid nonce too low errors. 50 | 51 | if (!dryRun) { 52 | accountRegistry.connectProviderAccounts(chain.rpc); 53 | } 54 | 55 | const existingContractAddresses: Record = {}; 56 | existingContracts.toList().forEach((item) => { 57 | existingContractAddresses[item.name] = item.address ? item.address : "0x"; 58 | }); 59 | 60 | let env = await readDeploymentFilesIntoEnv({}, chain); // Read from existing deployment files first, then overwrite with explicitly given contract addresses 61 | env = await readAccountsIntoEnv(env, accountRegistry); // Read from rendered accounts, useful for accessing things like multisig address from a signer, etc. 62 | env = { ...process.env, chain, ...existingContractAddresses, ...env }; 63 | if (!forceDeployNewContracts) { 64 | // Only read from existing contract files if we want to deploy new ones 65 | await readDeploymentFilesIntoEnv(env, chain); 66 | } 67 | 68 | // result is the final contract registry after deployment, modified in place 69 | const cleanedRegistry = loadContractUpdateRegistry( 70 | JSON.parse(JSON.stringify(updateSpec.serialize())) 71 | ); 72 | 73 | const result = new UpdateContractRegistry([]); 74 | 75 | for (const updateContract of cleanedRegistry.values()) { 76 | const parsedContractSchema = ContractItemSchema.safeParse(updateContract); 77 | if (parsedContractSchema.success) { 78 | const deployed = await deployContract( 79 | chain, 80 | accountRegistry, 81 | parsedContractSchema.data, 82 | logger, 83 | dryRun, 84 | writeContracts, 85 | extraContractFactories, 86 | nonces, 87 | env 88 | ); 89 | result.set(deployed.name, deployed); 90 | continue; 91 | } 92 | 93 | // If not a valid contract schema, then it should be a valid tx schema 94 | const parsedTxItem = TxItemSchema.safeParse(updateContract); 95 | if (parsedTxItem.success) { 96 | await sendTx( 97 | chain, 98 | accountRegistry, 99 | existingContracts, 100 | parsedTxItem.data, 101 | logger, 102 | dryRun, 103 | nonces, 104 | env, 105 | extraContractFactories 106 | ); 107 | continue; 108 | } 109 | logger.error("Invalid schema! Aborting contract setup."); 110 | throw new Error("Invalid schema! Aborting contract setup."); 111 | } 112 | 113 | logger.info( 114 | `[${chain.chainName}-${chain.deploymentEnvironment}]: finished updating ${updateSpec.size} contracts` 115 | ); 116 | 117 | return { 118 | chainName: chain.chainName, 119 | updatedContracts: result, 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | export const PACKAGE_VERSION = require("../../package.json").version; 4 | 5 | // Defaults 6 | export const DEFAULT_DEPLOYER = "default"; 7 | export const DEFAULT_RPC_URL = "http://127.0.0.1:8545"; 8 | export const DEFAULT_CHAIN_ID = "31337"; 9 | export const DEFAULT_CHAIN_NAME = "local"; 10 | export const DEFAULT_VERIFIER = "blockscout"; 11 | export const MODULE_ROOT_PATH = 12 | process.env.MODULE_ROOT_PATH || path.resolve(__dirname, "..", ".."); // Note: this is impacted by the tsup config since that can potential change where this file ends up being built to 13 | export const PARENT_MODULE_ROOT_PATH = path.dirname(path.resolve("package.json")); // Path of module using this package; path.resolve uses cwd so this should typically be the root of the module that's running the bin 14 | const DEFAULT_ARTIFACTS_PATH = path.resolve(MODULE_ROOT_PATH, "out"); 15 | const DEFAULT_DEPLOYMENTS_PATH = path.resolve( 16 | PARENT_MODULE_ROOT_PATH, 17 | "deployments" 18 | ); 19 | const DEFAULT_SPECS_PATH = path.resolve(MODULE_ROOT_PATH, "./specs"); 20 | const DEFAULT_DEPLOYMENT_ENVIRONMENT = "local"; // Deployment Environment disambiguates between deployment environments on the same chains (e.g. staging testnet vs prod testnet) This should be one of the following: local, staging, prod, mainnet 21 | 22 | export const CHAIN_NAME = process.env.CHAIN_NAME || DEFAULT_CHAIN_NAME; 23 | export const CHAIN_ID = parseInt(process.env.CHAIN_ID || DEFAULT_CHAIN_ID); 24 | export const RPC_URL = process.env.RPC_URL || DEFAULT_RPC_URL; 25 | export const ANVIL_PORT = process.env.ANVIL_PORT || "8545"; 26 | export const DEPLOYMENT_ENVIRONMENT = 27 | process.env.DEPLOYMENT_ENVIRONMENT || DEFAULT_DEPLOYMENT_ENVIRONMENT; 28 | 29 | // The path where we access artifacts for already deployed contracts 30 | export const ARTIFACTS_PATH = process.env.ARTIFACTS_PATH 31 | ? process.env.ARTIFACTS_PATH 32 | : DEFAULT_ARTIFACTS_PATH; // Used for importing both 33 | 34 | // The path where we save deployments 35 | export const DEPLOYMENTS_PATH = process.env.DEPLOYMENTS_PATH 36 | ? process.env.DEPLOYMENTS_PATH 37 | : DEFAULT_DEPLOYMENTS_PATH; 38 | 39 | const SPECS_BASE_PATH = process.env.SPECS_BASE_PATH 40 | ? process.env.SPECS_BASE_PATH 41 | : DEFAULT_SPECS_PATH; 42 | 43 | export const UPDATE_SPECS_PATH = process.env.UPDATE_SPECS_PATH 44 | ? process.env.UPDATE_SPECS_PATH 45 | : path.resolve(SPECS_BASE_PATH, "update.spec.yaml"); 46 | 47 | export const ACCOUNT_SPECS_PATH = process.env.ACCOUNT_SPECS_PATH 48 | ? process.env.ACCOUNT_SPECS_PATH 49 | : path.resolve(SPECS_BASE_PATH, "evm.accounts.yaml"); 50 | 51 | export const EXTRA_BINDINGS_PATH = process.env.EXTRA_BINDINGS_PATH; 52 | 53 | // The path where we access external artifacts for already deployed contracts 54 | export const EXTRA_ARTIFACTS_PATH = process.env.EXTRA_ARTIFACTS_PATH; 55 | 56 | // Path where we can load an existing contract registry from 57 | export const EXTERNAL_CONTRACTS_PATH= process.env.EXTERNAL_CONTRACTS_PATH; 58 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * as cli from "./cli"; 2 | export * as io from "./io"; 3 | export * as registry from "./registry"; 4 | -------------------------------------------------------------------------------- /test/Dispatcher.gasGriefing.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {Base} from "./utils/Dispatcher.base.t.sol"; 5 | import {GasUsingMars} from "./mocks/GasUsingMars.sol"; 6 | import {IbcEndpoint, ChannelEnd, IbcPacket} from "../contracts/libs/Ibc.sol"; 7 | import {IBCErrors} from "../contracts/libs/IbcErrors.sol"; 8 | import {IbcUtils} from "../contracts/libs/IbcUtils.sol"; 9 | 10 | import {TestUtilsTest} from "./utils/TestUtils.t.sol"; 11 | 12 | contract DispatcherGasGriefing is Base { 13 | IbcEndpoint src = IbcEndpoint("polyibc.bsc.58b604DB8886656695442374D8E940D814F2eDd4", "channel-99"); 14 | IbcEndpoint dest; 15 | bytes payload = bytes("msgPayload"); 16 | bytes appAck = abi.encodePacked('{ "account": "account", "reply": "got the message" }'); 17 | 18 | GasUsingMars gasUsingMars; 19 | ChannelEnd ch0 = 20 | ChannelEnd("polyibc.eth.71C95911E9a5D330f4D621842EC243EE1343292e", IbcUtils.toBytes32("channel-0"), "1.0"); 21 | ChannelEnd ch1 = 22 | ChannelEnd("polyibc.eth.71C95911E9a5D330f4D621842EC243EE1343292e", IbcUtils.toBytes32("channel-1"), "1.0"); 23 | 24 | function setUp() public override { 25 | (dispatcherProxy, dispatcherImplementation) = TestUtilsTest.deployDispatcherProxyAndImpl(portPrefix, feeVault); 26 | gasUsingMars = new GasUsingMars(3_000_000, dispatcherProxy); // Set arbitrarily high gas useage in mars contract 27 | bytes32 connectionStr = bytes32(0x636f6e6e656374696f6e2d310000000000000000000000000000000000000018); // connection-1 28 | // in hex 29 | _storeChannelidToConnectionMapping(ch1.channelId, connectionStr); 30 | dispatcherProxy.setClientForConnection("connection-1", dummyLightClient); 31 | } 32 | 33 | function test_GasGriefing() public { 34 | IbcPacket memory packet; 35 | packet.data = bytes("packet-1"); 36 | packet.timeoutTimestamp = 15_566_401_733_896_437_760; 37 | packet.dest.channelId = ch1.channelId; 38 | packet.dest.portId = string(abi.encodePacked(portPrefix, IbcUtils.toHexStr(address(gasUsingMars)))); 39 | packet.src.portId = ch0.portId; 40 | packet.src.channelId = ch0.channelId; 41 | packet.sequence = 1; 42 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.notEnoughGas.selector)); 43 | dispatcherProxy.recvPacket{gas: 2_000_000}(packet, validProof); // Should be enough gas to run out in the 44 | // callback but still finish execution 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/Dispatcher/Dispatcher.ack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.15; 3 | 4 | import "../utils/Dispatcher.base.t.sol"; 5 | import "../../contracts/examples/Mars.sol"; 6 | import "../../contracts/examples/Earth.sol"; 7 | import "../../contracts/libs/Ibc.sol"; 8 | import {IbcUtils} from "../../contracts/libs/IbcUtils.sol"; 9 | import {IBCErrors} from "../../contracts/libs/IbcErrors.sol"; 10 | import {IbcReceiver} from "../../contracts/interfaces/IbcReceiver.sol"; 11 | import {PacketSenderTestBase} from "./Dispatcher.t.sol"; 12 | 13 | // Test Chain A receives an acknowledgement packet from Chain B 14 | contract DispatcherAckPacketTestSuite is PacketSenderTestBase { 15 | function test_success() public { 16 | for (uint64 index = 0; index < 3; index++) { 17 | sendPacket(); 18 | 19 | vm.expectEmit(true, true, false, true, address(dispatcherProxy)); 20 | emit Acknowledgement(address(mars), channelId, sentPacket.sequence); 21 | dispatcherProxy.acknowledgement(sentPacket, ackPacket, validProof); 22 | // confirm dapp recieved the ack 23 | (bool success, bytes memory data) = mars.ackPackets(sentPacket.sequence - 1); 24 | AckPacket memory parsed = Ibc.parseAckData(ackPacket); 25 | assertEq(success, parsed.success); 26 | assertEq(data, parsed.data); 27 | } 28 | } 29 | 30 | // cannot ack packets if packet commitment is missing 31 | function test_missingPacket() public { 32 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.packetCommitmentNotFound.selector)); 33 | dispatcherProxy.acknowledgement(genPacket(1), genAckPacket("1"), validProof); 34 | 35 | sendPacket(); 36 | dispatcherProxy.acknowledgement(sentPacket, ackPacket, validProof); 37 | 38 | // packet commitment is removed after ack 39 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.packetCommitmentNotFound.selector)); 40 | dispatcherProxy.acknowledgement(sentPacket, ackPacket, validProof); 41 | } 42 | 43 | // cannot recieve ack packets out of order for ordered channel 44 | function test_outOfOrder() public { 45 | for (uint64 index = 0; index < 3; index++) { 46 | sendPacket(); 47 | } 48 | // 1st ack is ok 49 | dispatcherProxy.acknowledgement(genPacket(1), genAckPacket("1"), validProof); 50 | 51 | // only 2nd ack is allowed; so the 3rd ack fails 52 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.unexpectedPacketSequence.selector)); 53 | 54 | dispatcherProxy.acknowledgement(genPacket(3), genAckPacket("3"), validProof); 55 | } 56 | 57 | function test_invalidPort() public { 58 | Mars earth = new Mars(dispatcherProxy); 59 | string memory earthPort = string(abi.encodePacked(portPrefix, getHexBytes(address(earth)))); 60 | IbcEndpoint memory earthEnd = IbcEndpoint(earthPort, channelId); 61 | 62 | sendPacket(); 63 | 64 | // another valid packet but not the same port 65 | IbcPacket memory packetEarth = sentPacket; 66 | packetEarth.src = earthEnd; 67 | 68 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.packetCommitmentNotFound.selector)); 69 | dispatcherProxy.acknowledgement(packetEarth, ackPacket, validProof); 70 | } 71 | 72 | // ackPacket fails if channel doesn't match 73 | function test_invalidChannel() public { 74 | sendPacket(); 75 | 76 | IbcEndpoint memory invalidSrc = IbcEndpoint(src.portId, "channel-invalid"); 77 | IbcPacket memory packet = sentPacket; 78 | packet.src = invalidSrc; 79 | 80 | vm.expectRevert(abi.encodeWithSelector(IBCErrors.channelIdNotFound.selector, packet.src.channelId)); 81 | 82 | dispatcherProxy.acknowledgement(packet, ackPacket, validProof); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/Dispatcher/Dispatcher.client.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "../../contracts/libs/Ibc.sol"; 5 | import {Dispatcher} from "../../contracts/core/Dispatcher.sol"; 6 | import {IDispatcher} from "../../contracts/interfaces/IDispatcher.sol"; 7 | import {IbcEventsEmitter} from "../../contracts/interfaces/IbcDispatcher.sol"; 8 | import {IbcReceiver} from "../../contracts/interfaces/IbcReceiver.sol"; 9 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 10 | import "../../contracts/examples/Mars.sol"; 11 | import "../../contracts/core/OptimisticLightClient.sol"; 12 | import "../utils/Dispatcher.base.t.sol"; 13 | 14 | abstract contract DispatcherUpdateClientTestSuite is Base { 15 | function test_updateOptimisticConsensusState_success() public { 16 | // trick the L1Block contract into thinking it is updated with the right l1 header 17 | setL1BlockAttributes(keccak256(RLPWriter.writeList(l1header.header)), l1header.number); 18 | dispatcherProxy.updateClient(abi.encode(l1header, validStateProof), 1, uint256(apphash), "connection-0"); 19 | } 20 | 21 | function test_updateOptimisticConsensusState_failure() public { 22 | setL1BlockAttributes(keccak256(RLPWriter.writeList(l1header.header)), l1header.number); 23 | vm.expectRevert("MerkleTrie: ran out of proof elements"); 24 | dispatcherProxy.updateClient(abi.encode(l1header, invalidStateProof), 1, uint256(apphash), "connection-0"); 25 | } 26 | } 27 | 28 | contract DispatcherUpdateClientTest is DispatcherUpdateClientTestSuite { 29 | function setUp() public virtual override { 30 | (dispatcherProxy, dispatcherImplementation) = deployDispatcherProxyAndImpl(portPrefix, feeVault); 31 | dispatcherProxy.setClientForConnection("connection-0", opLightClient); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/FeeVault.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {ChannelOpenTestBaseSetup} from "./Dispatcher/Dispatcher.t.sol"; 5 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 6 | import {IFeeVault} from "../contracts/interfaces/IFeeVault.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | contract FeeVaultTest is ChannelOpenTestBaseSetup { 10 | address notOwner = vm.addr(2); 11 | address owner; 12 | Ownable feeVaultOwnable; // FeeVault address but from Ownable interface 13 | uint256 feePerGreet = 600_000 * 60 gwei + 700_000 * 70 gwei; 14 | 15 | function setUp() public override { 16 | super.setUp(); 17 | 18 | feeVaultOwnable = Ownable(address(feeVault)); 19 | greetMarsWithFee(); 20 | } 21 | 22 | // Fuzz test for always reverting if incorrect fee is sent. 23 | function testFuzz_sendPacket_FeesAddUpToValue( 24 | uint104 gasFee1, 25 | uint104 gasFee2, 26 | uint104 gasLimit1, 27 | uint104 gasLimit2, 28 | bool shouldRevert, 29 | uint56 fuzz 30 | ) public { 31 | gasFee1 = uint104(bound(gasFee1, 1 gwei, 2 ** 104 - 1)); 32 | gasLimit1 = uint104(bound(gasLimit1, 1, 2 ** 104 - 1)); 33 | fuzz = uint56(bound(fuzz, 1, 2 ** 56 - 1)); 34 | fuzz = uint56(bound(fuzz, 1, gasFee1)); 35 | 36 | uint256 feesToSend = uint256(gasFee1) * uint256(gasLimit1) + uint256(gasFee2) * uint256(gasLimit2); 37 | vm.deal(address(this), feesToSend + fuzz); 38 | 39 | if (shouldRevert) { 40 | // Fuzz the fees to make sure it reverts 41 | if (fuzz % 2 == 0) { 42 | feesToSend += uint256(fuzz); 43 | } else { 44 | feesToSend -= uint256(fuzz); 45 | } 46 | vm.expectRevert(); 47 | } 48 | feeVault.depositSendPacketFee{value: feesToSend}( 49 | channelId, uint64(1), [uint256(gasFee1), uint256(gasFee2)], [uint256(gasLimit1), uint256(gasLimit2)] 50 | ); 51 | } 52 | 53 | function test_withdrawAlwaysGoesToOwner() public { 54 | assertEq(address(feeVault).balance, feePerGreet); 55 | uint256 startingBalance = address(this).balance; 56 | feeVault.withdrawFeesToOwner(); 57 | assertEq(address(this).balance, startingBalance + feePerGreet); 58 | 59 | greetMarsWithFee(); // send more fees to feeVault 60 | // Non Owners can call but it should sill go to the owner (i.e. this address) 61 | vm.prank(notOwner); 62 | feeVault.withdrawFeesToOwner(); 63 | assertEq(address(feeVault).balance, 0); 64 | assertEq(address(this).balance, startingBalance + (feePerGreet * 2)); 65 | } 66 | 67 | function testRevert_BelowFeeThreshold() public { 68 | vm.deal(address(this), 20); 69 | 70 | vm.expectRevert(IFeeVault.FeeThresholdNotMet.selector); 71 | feeVault.depositSendPacketFee{value: 2}(channelId, 1, [uint256(1), uint256(1)], [uint256(1), uint256(1)]); 72 | } 73 | 74 | function greetMarsWithFee() internal { 75 | vm.deal(address(mars), feePerGreet); 76 | vm.prank(address(mars)); 77 | 78 | // Expect the event, ignoring the sequence (second parameter) 79 | vm.expectEmit(true, false, true, true, address(feeVault)); 80 | emit SendPacketFeeDeposited( 81 | channelId, 82 | 0, // Use 0 or any placeholder value for the ignored sequence 83 | [uint256(600_000), uint256(700_000)], 84 | [uint256(60 gwei), uint256(70 gwei)] 85 | ); 86 | 87 | mars.greetWithFee{value: feePerGreet}( 88 | "hello", channelId, maxTimeout, [uint256(600_000), uint256(700_000)], [uint256(60 gwei), uint256(70 gwei)] 89 | ); 90 | } 91 | 92 | // This test contract needs to have a receive function to accept funds sent from the FeeVault 93 | // Note: our multisig should have a fallback to accept funds in general 94 | receive() external payable {} 95 | } 96 | -------------------------------------------------------------------------------- /test/Fork/contract-spec.yaml: -------------------------------------------------------------------------------- 1 | # spec for deploying contracts 2 | # {{name}} is replaced with one of the following, whichever matches first 3 | # - the deployed contract address whose name matches `name` (not factoryName) 4 | # - variables of the running chain, e.g. {{chain.chainName}}, {{chain.chainId}} 5 | # - deployment factory names from written deployment files 6 | # NOTE: order of the contracts matters, as some contracts depend on others 7 | # contracts with no deps should be placed before those with deps 8 | 9 | ## The following arguments can be specified in contracts spec: 10 | # name: name of key that will be stored in the contract registry 11 | # deployer: must be # name of key that will be stored in the contract registry a valid name in accountRegistry; default to 'default' if not specified 12 | # description: description to be stored in the contract registry 13 | # factoryName: the name of the typechain factory used to deploy the contract 14 | # deployer: deployer key, should correspond to either a private key or one that can be looked up in the evm.accounts.yaml 15 | # libraries: if a contract depends on libraries, the location of the library file & the deployed library address can be specified here, as an array with 2 elements 16 | # deployArgs: The arguments that will be called in the contract constructor. Note: if $INITARGS is passed in as an argument, it will be abi.encode the arguments passed to the init paramater 17 | # init: any arguments that need to be abi encoded (e.g. for calling upgradeToAndCall for ERC1967Proxy). These will be rendered in the place of $INITARGS 18 | 19 | 20 | - name: LightClient 21 | description: 'DummyLightClient' 22 | factoryName: 'DummyLightClient' 23 | deployer: 'KEY_DEPLOYER' 24 | 25 | - name: Ibc 26 | description: 'IBC library' 27 | factoryName: 'Ibc' 28 | deployer: 'KEY_DEPLOYER' 29 | 30 | - name: IbcUtils 31 | description: 'IBC utils library' 32 | factoryName: 'IbcUtils' 33 | deployer: 'KEY_DEPLOYER' 34 | 35 | - name: Dispatcher 36 | description: 'IBC Core contract' 37 | factoryName: 'Dispatcher' 38 | libraries: 39 | - name: 'contracts/libs/Ibc.sol:Ibc' 40 | address: '{{Ibc}}' 41 | - name: 'contracts/libs/IbcUtils.sol:IbcUtils' 42 | address: '{{IbcUtils}}' 43 | deployer: 'KEY_DEPLOYER' 44 | 45 | - name: FeeVault 46 | description: 'FeeVault' 47 | factoryName: 'FeeVault' 48 | deployer: 'KEY_DEPLOYER' 49 | 50 | - name: DispatcherProxy 51 | description: 'Dispatcher proxy contract' 52 | factoryName: 'ERC1967Proxy' 53 | deployArgs: 54 | - '{{Dispatcher}}' 55 | - '$INITARGS' 56 | init: 57 | signature: 'initialize(string,address)' 58 | args: 59 | - 'polyibc.{{chain.chainName}}.' 60 | - '{{FeeVault}}' 61 | deployer: 'KEY_DEPLOYER' 62 | 63 | - name: UC 64 | description: 'Universal Chanel IBC-middleware contract' 65 | factoryName: 'UniversalChannelHandler' 66 | deployer: 'KEY_DEPLOYER' 67 | libraries: 68 | - name: 'contracts/libs/IbcUtils.sol:IbcUtils' 69 | address: '{{IbcUtils}}' 70 | 71 | - name: UCProxy 72 | description: 'Universal Chanel IBC-middleware proxy' 73 | factoryName: 'ERC1967Proxy' 74 | deployer: 'KEY_DEPLOYER' 75 | deployArgs: 76 | - '{{UC}}' 77 | - '$INITARGS' 78 | init: 79 | signature: 'initialize(address)' 80 | args: 81 | - '{{DispatcherProxy}}' 82 | 83 | # dApp contracts for testing and as examples 84 | - name: Mars 85 | description: 'Mars contract directly owns a IBC channel' 86 | deployArgs: 87 | - '{{DispatcherProxy}}' 88 | deployer: 'KEY_DAPP1' 89 | 90 | - name: Earth 91 | description: 'Earth contract uses shared universal channel' 92 | deployArgs: 93 | - '{{UCProxy}}' 94 | deployer: 'KEY_DAPP2' 95 | -------------------------------------------------------------------------------- /test/Fork/deploy.sh: -------------------------------------------------------------------------------- 1 | 2 | export MODULE_ROOT_PATH="./" 3 | anvil --fork-url ${OP_SEPOLIA_RPC_URL} 4 | & 5 | npx deploy-vibc-core-smart-contracts --ACCOUNT_SPECS_PATH="test/Fork/deployment-accounts-spec.yaml" --DEPLOY_SPECS_PATH="test/Fork/contract-spec.yaml" 6 | -------------------------------------------------------------------------------- /test/OpConsensusStateManager.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../contracts/core/OptimisticLightClient.sol"; 6 | import "../contracts/utils/DummyProofVerifier.sol"; 7 | import "./utils/Proof.base.t.sol"; 8 | 9 | contract OptimisticLightClientTest is ProofBase { 10 | OptimisticLightClient manager; 11 | IProofVerifier verifier; 12 | 13 | constructor() { 14 | verifier = new DummyProofVerifier(); 15 | } 16 | 17 | function setUp() public override { 18 | super.setUp(); 19 | manager = new OptimisticLightClient(1, verifier, l1BlockProvider); 20 | } 21 | 22 | function test_addOpConsensusState_newOpConsensusStateCreatedWithPendingStatus() public { 23 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 24 | (,, bool ended) = manager.getStateAndEndTime(1); 25 | 26 | assertEq(false, ended); 27 | } 28 | 29 | function test_addOpConsensusState_addingAlreadyTrustedOpConsensusStateIsNoop() public { 30 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 31 | 32 | // fast forward time. 33 | vm.warp(block.timestamp + 100); 34 | 35 | // the fraud proof window has passed. 36 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 37 | 38 | (,, bool ended) = manager.getStateAndEndTime(1); 39 | assertEq(true, ended); 40 | } 41 | 42 | function test_addOpConsensusState_addingPendingOpConsensusStateWithDifferentValuesIsError() public { 43 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 44 | 45 | vm.expectRevert(OptimisticLightClient.CannotUpdatePendingOptimisticConsensusState.selector); 46 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 2); 47 | } 48 | 49 | function test_addOpConsensusState_addingSameOpConsensusStateIsNoop() public { 50 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 51 | 52 | (, uint256 originalFraudProofEndTime,) = manager.getStateAndEndTime(1); 53 | 54 | vm.warp(block.timestamp + 1); 55 | 56 | // adding the same appHash later doesn't update the fraud 57 | // proof end time. 58 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 59 | (, uint256 newFraudProofEndTime,) = manager.getStateAndEndTime(1); 60 | assertEq(originalFraudProofEndTime, newFraudProofEndTime); 61 | } 62 | 63 | function test_zero_proof_window() public { 64 | manager = new OptimisticLightClient(0, verifier, l1BlockProvider); 65 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, 1); 66 | (,, bool ended) = manager.getStateAndEndTime(1); 67 | assertEq(true, ended); 68 | } 69 | 70 | function test_getState_nonExist() public { 71 | (uint256 appHash,, bool ended) = manager.getStateAndEndTime(1); 72 | (uint256 appHash1) = manager.getState(1); 73 | assertEq(0, appHash); 74 | assertEq(0, appHash1); 75 | assertEq(false, ended); 76 | } 77 | } 78 | 79 | contract OptimisticLightClientWithRealVerifierTest is ProofBase { 80 | OptimisticLightClient manager; 81 | 82 | function setUp() public override { 83 | super.setUp(); 84 | manager = new OptimisticLightClient(1, opProofVerifier, l1BlockProvider); 85 | } 86 | 87 | function test_addOpConsensusState_newAppHashWithValidProof() public { 88 | // trick the L1Block contract into thinking it is updated with the right l1 header 89 | setL1BlockAttributes(keccak256(RLPWriter.writeList(l1header.header)), l1header.number); 90 | 91 | manager.updateClient(abi.encode(l1header, validStateProof), 1, uint256(apphash)); 92 | 93 | // since we are setting using an already known apphash, the proof is ignored 94 | manager.updateClient(abi.encode(emptyl1header, invalidStateProof), 1, uint256(apphash)); 95 | } 96 | 97 | function test_addOpConsensusState_newAppHashWithInvalidProof() public { 98 | setL1BlockAttributes(keccak256(RLPWriter.writeList(l1header.header)), l1header.number); 99 | vm.expectRevert("MerkleTrie: ran out of proof elements"); 100 | manager.updateClient(abi.encode(l1header, invalidStateProof), 1, uint256(apphash)); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/SequencerSoloClient.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import {SequencerSoloClient} from "../contracts/core/SequencerSoloClient.sol"; 6 | import {SequencerSignatureVerifier, ISignatureVerifier} from "../contracts/core/SequencerSignatureVerifier.sol"; 7 | import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 8 | import {SigningBase} from "./utils/Signing.base.t.sol"; 9 | 10 | contract OptimisticLightClientWithRealVerifierTest is SigningBase { 11 | SequencerSoloClient sequencerClient; 12 | bytes32 ancestorBlockPayloadHash; 13 | bytes validChildSignature; 14 | bytes validAncestorSignature; 15 | bytes invalidSignature; 16 | 17 | function setUp() public { 18 | sequencerClient = new SequencerSoloClient(sigVerifier, l1BlockProvider); 19 | 20 | // Used for l1 origin check tests, represents the child block. 21 | bytes32 ancestorPayloadHash = 22 | keccak256(bytes.concat(bytes32(peptideBlockNumber), peptideAppHash, l1AncestorBlockHash)); 23 | bytes32 ancestorHashToSign = keccak256(bytes.concat(domain, PEPTIDE_CHAIN_ID, ancestorPayloadHash)); 24 | (uint8 v0, bytes32 r0, bytes32 s0) = vm.sign(sequencerPkey, ancestorHashToSign); 25 | validAncestorSignature = abi.encodePacked(r0, s0, v0); // Annoingly, v, r, s are in a different order than those 26 | 27 | (uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(notSequencerPkey, ancestorHashToSign); 28 | invalidSignature = abi.encodePacked(r1, s1, v1); // Annoingly, v, r, s are in a different order than those 29 | } 30 | 31 | function test_UpdateValidSequencerClientState() public { 32 | // In this case, peptide l1 origin is ahead of dest chain's l1 origin. Relayer queries the 33 | // signed block from sequencer and waits for dest chain to come to same l1 origin. 34 | // We don't need a checkpointing solution in this case, so we call the update method directly. 35 | 36 | setBlockAttributesFromJson("/test/payload/l1_block_ancestor.json"); 37 | sequencerClient.updateClient( 38 | abi.encodePacked(l1AncestorBlockHash, validAncestorSignature), peptideBlockNumber, uint256(peptideAppHash) 39 | ); 40 | assertEq(sequencerClient.getState(peptideBlockNumber), uint256(peptideAppHash)); 41 | 42 | // A subsequent client update should be a noop and wouldn't revert even with an invalid sig 43 | sequencerClient.updateClient( 44 | abi.encodePacked(l1AncestorBlockHash, invalidSignature), peptideBlockNumber, uint256(peptideAppHash) 45 | ); 46 | assertEq(sequencerClient.getState(peptideBlockNumber), uint256(peptideAppHash)); 47 | } 48 | 49 | // 1.) test that hte block update without the l1b lock header is invalid, or perhaps signatuer is invalid as well 50 | function test_revert_invalidSequencerClientState() public { 51 | // This mocks the dest chain moving ahead of the signed l1 origin, or a re-org occuring which means that the 52 | // blockhash returned by l1 block info isn't equal to that of the signed blockhash. i.e. We'd expect this update 53 | // client tx to revert. 54 | setBlockAttributesFromJson("/test/payload/l1_block_child.json"); 55 | console.logBytes32(l1BlockProvider.hash()); 56 | vm.expectRevert(SequencerSoloClient.InvalidL1Origin.selector); 57 | sequencerClient.updateClient( 58 | abi.encodePacked(l1AncestorBlockHash, validAncestorSignature), peptideBlockNumber, uint256(peptideAppHash) 59 | ); 60 | assertEq(sequencerClient.getState(peptideBlockNumber), 0); 61 | 62 | setBlockAttributesFromJson("/test/payload/l1_block_ancestor.json"); 63 | // Peptide l1 origin being the same as dest chain l1 origin is necessary but not sufficient. We also need to 64 | // check that the signature is valid as well, and it will revert if it isn't valid. 65 | vm.expectRevert(ISignatureVerifier.InvalidSequencerSignature.selector); 66 | sequencerClient.updateClient( 67 | abi.encodePacked(l1AncestorBlockHash, invalidSignature), peptideBlockNumber, uint256(peptideAppHash) 68 | ); 69 | assertEq(sequencerClient.getState(peptideBlockNumber), 0); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/SequencerVerifier.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "../contracts/core/OptimisticProofVerifier.sol"; 5 | import "../contracts/libs/Ibc.sol"; 6 | import {IbcUtils} from "../contracts/libs/IbcUtils.sol"; 7 | import "forge-std/Test.sol"; 8 | import "./utils/Signing.base.t.sol"; 9 | 10 | contract SequencerProofVerifierStateUpdate is SigningBase { 11 | function test_verify_signature_state_update_sucess() public view { 12 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign); 13 | 14 | bytes memory signature = abi.encodePacked(r, s, v); // Annoingly, v, r, s are in a different order than those 15 | // returned from vm.sign 16 | 17 | // This call should not revert as long as it's a valid sequencer signature 18 | sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signature); 19 | } 20 | 21 | // Call should revert if invalid signature values are passed 22 | function test_verify_state_update_invalid_signature() public { 23 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign); 24 | 25 | bytes memory signatureTooShort = abi.encodePacked(r, s); 26 | bytes memory signatureTooLong = abi.encodePacked(r, s, v, uint256(123)); 27 | 28 | // This call should not revert as long as it's a valid sequencer signature 29 | vm.expectRevert("ECDSA: invalid signature length"); 30 | sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signatureTooShort); 31 | vm.expectRevert("ECDSA: invalid signature length"); 32 | sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signatureTooLong); 33 | } 34 | 35 | // Valid signature but from the wrong signer should revert 36 | function test_verify_state_update_not_signer() public { 37 | (, uint256 notSequencerPkey) = makeAddrAndKey(unicode"😳"); 38 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(notSequencerPkey, hashToSign); 39 | 40 | bytes memory wrongSignerSignature = abi.encodePacked(r, s, v); 41 | 42 | vm.expectRevert(abi.encodeWithSelector(ISignatureVerifier.InvalidSequencerSignature.selector)); 43 | sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, wrongSignerSignature); 44 | } 45 | 46 | // Valid signature but on the wrong header should revert 47 | function test_verify_state_update_incorrect_header() public { 48 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign); 49 | 50 | bytes memory signature = abi.encodePacked(r, s, v); 51 | 52 | vm.expectRevert(abi.encodeWithSelector(ISignatureVerifier.InvalidSequencerSignature.selector)); 53 | sigVerifier.verifyStateUpdate(peptideBlockNumber + 1, peptideAppHash, l1BlockHash, signature); 54 | } 55 | } 56 | 57 | // Note: Membership verification tests are done in verifier.t.sol, as it is part of the AppStateVerifier abstract 58 | // contract 59 | -------------------------------------------------------------------------------- /test/mocks/GasUsingMars.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {IDispatcher} from "../../contracts/interfaces/IDispatcher.sol"; 5 | import {Mars} from "../../contracts/examples/Mars.sol"; 6 | import {IbcPacket, AckPacket} from "../../contracts/libs/Ibc.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | /** 10 | * Testing mock contrantract to test gas useage. 11 | * This contract 12 | * We can label the true gas cost of the parent transaction sent to the dispatcher as g = a + b + c . 13 | * 14 | * Solidity automatically allocates 63/64 of the remaining gas left in a tx for low level calls, so (g-a)*63/64 will be 15 | * allocated for b, 16 | * and the remaining (g-a)/64 will be allocated for c. 17 | * If (g-a) * b > c and a malicious user sends a transaction with a gas limit g' such that a + c < g'< (g' - 18 | * a)*(63/64), 19 | * this can result in a gas griefing attack. 20 | */ 21 | contract GasUsingMars is Mars { 22 | uint256 private gasToUse; 23 | 24 | constructor(uint256 _gasToUse, IDispatcher _ibcDispatcher) Mars(_ibcDispatcher) { 25 | gasToUse = _gasToUse; 26 | } 27 | 28 | function onRecvPacket(IbcPacket memory packet) 29 | external 30 | virtual 31 | override 32 | onlyIbcDispatcher 33 | returns (AckPacket memory ackPacket, bool skipAck) 34 | { 35 | recvedPackets.push(packet); 36 | 37 | _useGas(); 38 | // solhint-disable-next-line quotes 39 | return (AckPacket(true, abi.encodePacked('{ "account": "account", "reply": "got the message" }')), false); 40 | } 41 | 42 | function _useGas() internal view { 43 | // This function is used to test the gas usage of the contract 44 | uint256 startingGas = gasleft(); 45 | uint256 dummyInt = 0; 46 | 47 | // It will use the gasToUse amount of gas 48 | while (startingGas - gasleft() < gasToUse) { 49 | dummyInt += 1; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/payload/ack.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import "../../contracts/libs/Ibc.sol"; 7 | 8 | contract AckContract { 9 | // getAckPacketBytes returns the bytes representation of the AckPacket 10 | function getAckPacketBytes(AckPacket calldata ack) external pure returns (bytes memory) { 11 | return abi.encodePacked(ack.success, ack.data); 12 | } 13 | 14 | // getIncentivizedAckPacketBytes returns the bytes representation of the IncentivizedAckPacket 15 | function getIncentivizedAckPacketBytes(IncentivizedAckPacket calldata ack) external pure returns (bytes memory) { 16 | bytes memory relayerBytes = bytes(ack.relayer); 17 | return abi.encodePacked(ack.success, uint32(relayerBytes.length), relayerBytes, ack.data); 18 | } 19 | } 20 | 21 | contract AckTest is Test { 22 | AckContract ackContract = new AckContract(); 23 | 24 | bytes ackPayload1 = hex"0161636b2d64617461"; 25 | AckPacket ackPacket1 = AckPacket({success: true, data: bytes("ack-data")}); 26 | bytes32 ackPacket1Hash = hex"863b46bf0508a780d8954b8f22b37e91a76c6427a2dcdff98076f977adda55b7"; 27 | 28 | bytes ackPayload2 = hex"00000000147e5f4552091a69125d5dfcb7b8c2659029395bdf61636b2d64617461"; 29 | IncentivizedAckPacket ackPacket2 = IncentivizedAckPacket({ 30 | success: false, 31 | // this is the address vm.addr(0x01) where 0x01 is the private key. Testing ONLY! 32 | relayer: hex"7e5f4552091a69125d5dfcb7b8c2659029395bdf", 33 | data: bytes("ack-data") 34 | }); 35 | bytes32 ackPacket2Hash = hex"5037447b5f5cf30dac6895cca07855054abe38b989e16837ef3b8d929f523e0a"; 36 | 37 | function setUp() public {} 38 | 39 | // testAckPacketBytes tests the bytes representation of AckPacket 40 | function testAckPacketBytes() public { 41 | bytes memory ackPacketBytes = ackContract.getAckPacketBytes(ackPacket1); 42 | assertEq(ackPacketBytes, ackPayload1, "ackPacketBytes should be equal to ackPayload1"); 43 | assertEq(ackPacket1Hash, sha256(ackPacketBytes), "ackPacket1Hash should be equal to keccak256(ackPacketBytes)"); 44 | } 45 | 46 | // testIncentivizedAckPacketBytes tests the bytes representation of IncentivizedAckPacket 47 | function testIncentivizedAckPacketBytes() public { 48 | bytes memory ackPacketBytes = ackContract.getIncentivizedAckPacketBytes(ackPacket2); 49 | assertEq(ackPacket2.relayer, abi.encodePacked(vm.addr(0x01))); 50 | 51 | // convert ack.relayer to EVM address 52 | require(ackPacket2.relayer.length == 20, "invalid relayer address length"); 53 | address relayer = address(bytes20(ackPacket2.relayer)); 54 | assertEq(relayer, vm.addr(0x01), "relayer should be equal to vm.addr(0x01)"); 55 | 56 | assertEq(ackPacketBytes, ackPayload2, "ackPacketBytes should be equal to ackPayload2"); 57 | assertEq(ackPacket2Hash, sha256(ackPacketBytes), "ackPacket2Hash should be equal to keccak256(ackPacketBytes)"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/payload/l1_block_0x1235261.hex: -------------------------------------------------------------------------------- 1 | 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060c2129ff0212b15912b882be130f5e7b143b3a943693b073f74991f2dfbde9973000000000000000000000000000000000000000000000000000000000123526100000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000007e00000000000000000000000000000000000000000000000000000000000000021a03215c71324450eb71b32c08e82ff4090ffbfb96a0f3b326bb5a157c954139160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000159495222290dd7278aa3ddd389cc1e1d165cc4bafe500000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0c2129ff0212b15912b882be130f5e7b143b3a943693b073f74991f2dfbde9973000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0f7b6511cc742e2fe31d79c8b780945b5316ef2464a591967678e54a4c35b115e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0978db577776af953ae8fc01d1d532c3a9d3a0812ea686de7afc7793ac5b847a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103b901006ca91004498341d000797640810190ab921eca61f4650c25a12592a544329180001034a1087080f570288fa7641059042b1380c0ab68a16e0fd03e258bbda042f885868d49808cb81c90c62c05a100f08ca15039534f1900e9045a848d649c46a2104167e26e40cc100c5718c82078c444c88d150bdc9602c2524cd2b44a19211b34034138fe06d8045323a9c8125ca7aa4053073b0139aa2c8049d0805188488e8793440b052a2c120299801c809f9896089e821321ba2a74220fea01080b8159d208b6101838580e06081764962c9832406c01b02862917725149a1125e082317ce09881c1789ac54e5509c3d290a04c28111b548ee1421009095070044e080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005840123526100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058401c9c38000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048397e7bc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058465b3e23300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000108f6265617665726275696c642e6f7267000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a095670771293fddb9fe07a4e288b7588bef6cd9aa06e5312962348604976999a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000685050887700900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a043990e4a3f5c679055fff27167ca098d0cc48e5f0b32fb16adbb66d5d93f0d3100000000000000000000000000000000000000000000000000000000000000 -------------------------------------------------------------------------------- /test/payload/l1_block_0x4df537.hex: -------------------------------------------------------------------------------- 1 | 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000604d00f3add4dfa340f263a29c611ca08de042d195efd8fab8ec18a102d5c6f63c00000000000000000000000000000000000000000000000000000000004df53700000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000007e00000000000000000000000000000000000000000000000000000000000000021a07b7f1cd3400204ab518b143360ac525502f4bb581d427429f196fe1918ac6024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015946a7aa9b882d50bb7bc5da1a244719c99f12f06a300000000000000000000000000000000000000000000000000000000000000000000000000000000000021a04d00f3add4dfa340f263a29c611ca08de042d195efd8fab8ec18a102d5c6f63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0055b2c032685cb2a61d961c90c07959746fed2d28645ffa111a4ff89ad43ccd7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0a04f47b60169ec4e651dd932ffe8d66b33de8c8548af12657eefda8895217593000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103b901000d70308f1a5cf2a1a33174d28ca50572c6f8000c4294a84d0ad019e92109402ac20a588289bf2aabd8298a2ac42486b3500d0e12318b57484511d09a12365a4d3209ad824fe56a70e1102a8b886da09486115c8409a9023583000a26406060203ff681dc075dc25d84a040774c32a995da8c2ec045248421aa9a8c1e308b0f148230ca093578200027fb11318ad072101be1b44b184780e579a7580f264018510e8ca70a1af0104b426901d26850b059d49230104a1a8c85604027a0256200b20381bb23122cd2504309e809691612d90475162002740a2640a6aa331207212918f809b700f112c12b8a3650a3a4211ea66964300a410301e70b1e8502d27a9d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004834df5370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058401c9c380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000584013577f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058465a94dd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a99d883010d08846765746888676f312e32302e31856c696e75780000000000000000000000000000000000000000000000000000000000000000000000000021a021c3d44a811c2c122d61ca3aef8847c6d6f6defe5f02ff74a96b41ee38c8497500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000988000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006850cbade162400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0ab226847f9728d2c1b0fd37a4431c938a65a196304dddc4e84615862767aa2ca00000000000000000000000000000000000000000000000000000000000000 -------------------------------------------------------------------------------- /test/payload/l1_block_0x61a80.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "id": "1", 4 | "result": { 5 | "difficulty": "0x57a418a7c3e", 6 | "extraData": "0xd583010202844765746885676f312e35856c696e7578", 7 | "gasLimit": "0x2fefd8", 8 | "gasUsed": "0x0", 9 | "hash": "0x5d15649e25d8f3e2c0374946078539d200710afc977cdfc6a977bd23f20fa8e8", 10 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226", 12 | "mixHash": "0x3fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38", 13 | "nonce": "0x6af23caae95692ef", 14 | "number": "0x61a80", 15 | "parentHash": "0x1e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14", 16 | "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 17 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 18 | "size": "0x21b", 19 | "stateRoot": "0x0b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89", 20 | "timestamp": "0x5622efdc", 21 | "totalDifficulty": "0x1bf3a15db92c52a5", 22 | "transactions": [], 23 | "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 24 | "uncles": [] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/payload/l1_block_ancestor.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "id": "1", 4 | "result":{ 5 | "hash": "0x911faba7371dde57a5650b304ef8fd70f0059f5e23cc8ca305a54ae101329c5e", 6 | "parentHash": "0xfd0cf75c3395e8e31a8a66484d1fdc337ae06548e0389576ce1342c24cfc4c0c", 7 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 8 | "miner": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97", 9 | "stateRoot": "0x5b5aa7b9d9b48ecc9e1a5acd52c99a4b7368392bc88719e8adcd002666b4ff00", 10 | "transactionsRoot": "0x1ca62b0feca24a6363ed60d8a22ce5dff0360345f784193eadaa71b0bbad02ba", 11 | "receiptsRoot": "0x5dc05f27d790f10ed41b577adf202bb8784a0cef4bc25d2a510d766f6cbeef55", 12 | "logsBloom": "0x392f5158c9016dcd1d268bd496a8a81c0b294a01fc014002269940b626722d800012159ac608800ac08a7316023c8114df2da7459ed32f3b1984286aa9aab90b09321ea8750b19980c6ee3a89186cee4980112b102c24c956011767090660224042531a50ef205ab6a5e43a3e3a54ee1f47b18af8e1a266ec2032b91300a22428b62875852a1a6f820cb2965a3728c65ae01287b8b30d4ad5c3200f00733d94b2bcc35400a83f2024e8e11e608a79e6e24008da9160172a214e904f258f80d75a856845fe10a8f1347428673438e8bdbabd4059274a02a90c48745f2652baa4a2c79e2c010820f8089049da704e559862a1d1bc163802cd86009b31242019e0b", 13 | "difficulty": "0x0", 14 | "number": "0x1403e78", 15 | "gasLimit": "0x1c9c380", 16 | "gasUsed": "0x119c8ab", 17 | "timestamp": "0x67116bcf", 18 | "totalDifficulty": "0xc70d815d562d3cfa955", 19 | "extraData": "0x546974616e2028746974616e6275696c6465722e78797a29", 20 | "mixHash": "0x99de1f7d00918a47a9909bd6bb8ea30c47f0c823e6ed014b70049576a0df6de7", 21 | "nonce": "0x0000000000000000", 22 | "baseFeePerGas": "0x47fe26383", 23 | "withdrawalsRoot": "0x52b2e029af917cce9f73904f2046b8e9e33749bcdc23885d89daaf33af9f7fe0", 24 | "blobGasUsed": "0x0", 25 | "excessBlobGas": "0xa0000", 26 | "parentBeaconBlockRoot": "0x61c7198bbdc252979973e408dedbcddc7aacdf78459d69a3e95aa35e3249ed50", 27 | "uncles": [] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/payload/l1_block_child.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "id": "1", 4 | "result":{ 5 | "hash": "0xb4ebc467e644b0d3b855a71305bdeb1fef88e761976f2a6ea2f75b4c82b0acd5", 6 | "parentHash": "0xabbe2e82802e00a1caa3996470825a4ce61d7137d359cb5e8f36261b8f2fdd38", 7 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 8 | "miner": "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5", 9 | "stateRoot": "0x4e96a5c64d8866f8dab8ec7acca71ca9594336670573d1de1cd7a9c419272f15", 10 | "transactionsRoot": "0x53cbb89954c8ee1623d139c5848cfab350ca5c5687497f7d3b9e813634bc20fb", 11 | "receiptsRoot": "0xa072f434d74313444e47d269c1530cdda9694178cc4281d9e683efd7d9b730d4", 12 | "logsBloom": "0x3dabf73f7f454f7655aeec519bbf9a83fdeb1b70d67b76c2726b414d77a6288ff3f37f2fbf71a597ff1d7fd9feb7df91d73fcb6dfff37db557ed7fdef4bf4bf3bcf69cafd10caa7beaf77fbef96eebefdd1646f9bff54f9df7bf0debf2f5bef74eaef7f7aa7afae784dfd7f77949ff672f6d6b5b1f7ff67b90cef0b6591f9df3aef7fb587b33f6699a4d256aefee9ee7e653ff77fdb1d87dcc3c2d57ef785dfcebb7f772ed9bfadf1f0671d6c987bfcafdf756d9dffdf6ef4d6fcbf3ecbfe9f5cd977a7ab55ffffb7dcb8935c6eaff454fdff77245ff2df744dbf5efe94af49729fbbd51f99c347757fdf78eb2f917479f7fd94d6d3ab6fb3d0ca1787ff8fccf", 13 | "difficulty": "0x0", 14 | "number": "0x1403e80", 15 | "gasLimit": "0x1c9c380", 16 | "gasUsed": "0x1842e76", 17 | "timestamp": "0x67116c2f", 18 | "totalDifficulty": "0xc70d815d562d3cfa955", 19 | "extraData": "0x6265617665726275696c642e6f7267", 20 | "mixHash": "0x1dd88cf475dd30d18ca90277a91458626d76f2debcb280e6fd13eaf923d12437", 21 | "nonce": "0x0000000000000000", 22 | "baseFeePerGas": "0x3a5b90cbb", 23 | "withdrawalsRoot": "0xf2fa7fc5d5c8045c5174e53c774e0a4e5b78019670d069f715b75caada2e7a34", 24 | "blobGasUsed": "0x80000", 25 | "excessBlobGas": "0x40000", 26 | "parentBeaconBlockRoot": "0x0a1ccf62294537ea20b53fe2a3df1b5a11fc29d8105d42b9517d0857cdafbc58", 27 | "uncles": [] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/payload/l2_block_0x4b0.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "id": "1", 4 | "result": { 5 | "baseFeePerGas": "0x0", 6 | "difficulty": "0x0", 7 | "extraData": "", 8 | "gasLimit": "0x1c9c380", 9 | "gasUsed": "0x0", 10 | "hash": "0xf51d6bb7b2578b14d98849963eab7433110e4ad7fdac1636d03bcc9f9a852721", 11 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 12 | "mixHash": "0x13368de1a039f35df18b6363569284e944460f3e408fabe7ce998c2c5d8f46d0", 13 | "number": "0x4b0", 14 | "parentHash": "0xb1c7530229b8155c745feaa0da2d9018f3c873fd8e4a8f226a08b8b1c8bc894e", 15 | "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", 16 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 17 | "stateRoot": "0x6cc4ecfce2112e8b6100e023edac48d81f554c1d3869bac8140b0609959586af", 18 | "timestamp": "0x657ba6ba", 19 | "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/upgradeableProxy/DispatcherUUPS.accessControl.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "../../contracts/libs/Ibc.sol"; 5 | import {Dispatcher} from "../../contracts/core/Dispatcher.sol"; 6 | import {IbcEventsEmitter} from "../../contracts/interfaces/IbcDispatcher.sol"; 7 | import {IbcReceiver} from "../../contracts/interfaces/IbcReceiver.sol"; 8 | import {IFeeVault} from "../../contracts/interfaces/IFeeVault.sol"; 9 | import {Mars} from "../../contracts/examples/Mars.sol"; 10 | import "../../contracts/core/OptimisticLightClient.sol"; 11 | import "../utils/Dispatcher.base.t.sol"; 12 | import {DispatcherV2} from "./upgrades/DispatcherV2.sol"; 13 | import {DispatcherV2Initializable} from "./upgrades/DispatcherV2Initializable.sol"; 14 | import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; 15 | import {DummyLightClient} from "../../contracts/utils/DummyLightClient.sol"; 16 | 17 | contract DispatcherUUPSAccessControl is Base { 18 | string public portPrefix2 = "IIpolyibc.eth."; 19 | ILightClient lightClient2 = new DummyLightClient(); 20 | address public notOwner = vm.addr(1); 21 | DispatcherV2 dispatcherImplementation2; 22 | DispatcherV2Initializable dispatcherImplementation3; 23 | 24 | function setUp() public override { 25 | (dispatcherProxy, dispatcherImplementation) = deployDispatcherProxyAndImpl(portPrefix, feeVault); 26 | dispatcherProxy.setClientForConnection(connectionHops[0], dummyLightClient); 27 | dispatcherImplementation2 = new DispatcherV2(); 28 | dispatcherImplementation3 = new DispatcherV2Initializable(); 29 | } 30 | 31 | function test_Dispatcher_Allows_Upgrade_123() public { 32 | assertEq(address(dispatcherImplementation), getProxyImplementation(address(dispatcherProxy), vm)); 33 | UUPSUpgradeable(address(dispatcherProxy)).upgradeTo(address(dispatcherImplementation2)); 34 | assertEq(address(dispatcherImplementation2), getProxyImplementation(address(dispatcherProxy), vm)); 35 | assertEq(dispatcherProxy.portPrefix(), portPrefix); 36 | } 37 | 38 | function test_Dispatcher_Allows_Upgrade_To_And_Call() public { 39 | IFeeVault newFeeVault = new FeeVault(); 40 | assertEq(address(dispatcherImplementation), getProxyImplementation(address(dispatcherProxy), vm)); 41 | bytes memory initData = abi.encodeWithSignature("initialize(string,address)", portPrefix2, newFeeVault); 42 | UUPSUpgradeable(address(dispatcherProxy)).upgradeToAndCall(address(dispatcherImplementation3), initData); 43 | assertEq(address(dispatcherImplementation3), getProxyImplementation(address(dispatcherProxy), vm)); 44 | assertEq(dispatcherProxy.portPrefix(), portPrefix2); 45 | assertEq(address(dispatcherProxy.feeVault()), address(newFeeVault)); 46 | } 47 | 48 | function test_Dispatcher_Prevents_Non_Owner_Updgrade() public { 49 | vm.startPrank(notOwner); 50 | vm.expectRevert("Ownable: caller is not the owner"); 51 | UUPSUpgradeable(address(dispatcherProxy)).upgradeTo(address(dispatcherImplementation2)); 52 | } 53 | 54 | function test_Dispatcher_Prevents_Non_Owner_UpdgradeToAndCall() public { 55 | vm.expectRevert("Function must be called through delegatecall"); 56 | UUPSUpgradeable(address(dispatcherImplementation)).upgradeTo(address(dispatcherImplementation2)); 57 | } 58 | 59 | function test_Dispatcher_Prevents_Reinit_Attacks() public { 60 | vm.expectRevert("Initializable: contract is already initialized"); 61 | dispatcherImplementation.initialize("IIpolyibc.eth.", IFeeVault(vm.addr(1))); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/upgradeableProxy/upgrades/DispatcherV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | pragma solidity ^0.8.9; 18 | 19 | import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; 20 | import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; 21 | import {OwnableUpgradeable} from "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; 22 | import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 23 | import {IbcChannelReceiver, IbcPacketReceiver} from "../../../contracts/interfaces/IbcReceiver.sol"; 24 | import {L1Header, OpL2StateProof, Ics23Proof} from "../../../contracts/interfaces/IProofVerifier.sol"; 25 | import {ILightClient} from "../../../contracts/interfaces/ILightClient.sol"; 26 | import {IDispatcher} from "../../../contracts/interfaces/IDispatcher.sol"; 27 | import {Dispatcher} from "../../../contracts/core/Dispatcher.sol"; 28 | import { 29 | Channel, ChannelEnd, ChannelOrder, IbcPacket, ChannelState, AckPacket, Ibc 30 | } from "../../../contracts/libs/Ibc.sol"; 31 | import {IbcUtils} from "../../../contracts/libs/IbcUtils.sol"; 32 | import {IBCErrors} from "../../../contracts/libs/IbcErrors.sol"; 33 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 34 | 35 | /** 36 | * @title Dispatcher 37 | * @author Polymer Labs 38 | * @notice 39 | * Contract callers call this contract to send IBC-like msg, 40 | * which can be relayed to a rollup module on the Polymerase chain 41 | */ 42 | contract DispatcherV2 is Dispatcher {} 43 | -------------------------------------------------------------------------------- /test/upgradeableProxy/upgrades/DispatcherV2Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | * Copyright 2024, Polymer Labs 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | pragma solidity ^0.8.9; 19 | 20 | import {DispatcherV2} from "./DispatcherV2.sol"; 21 | import {ILightClient} from "../../../contracts/interfaces/ILightClient.sol"; 22 | import {IFeeVault} from "../../../contracts/interfaces/IFeeVault.sol"; 23 | import {IBCErrors} from "../../../contracts/libs/IbcErrors.sol"; 24 | 25 | /** 26 | * @title Dispatcher 27 | * @author Polymer Labs 28 | * @notice 29 | * Contract callers call this contract to send IBC-like msg, 30 | * which can be relayed to a rollup module on the Polymerase chain 31 | */ 32 | contract DispatcherV2Initializable is DispatcherV2 { 33 | function initialize(string memory initPortPrefix, IFeeVault _feeVault) public override reinitializer(3) { 34 | __Ownable_init(); 35 | portPrefix = initPortPrefix; 36 | portPrefixLen = uint32(bytes(initPortPrefix).length); 37 | feeVault = _feeVault; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/utils/Proof.base.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../../contracts/libs/Ibc.sol"; 6 | import "../../contracts/core/OptimisticProofVerifier.sol"; 7 | import "../../contracts/interfaces/IProofVerifier.sol"; 8 | import {L1Block} from "optimism/L2/L1Block.sol"; 9 | 10 | contract ProofBase is Test { 11 | using stdJson for string; 12 | 13 | string rootDir = vm.projectRoot(); 14 | 15 | OpL2StateProof validStateProof; 16 | OpL2StateProof invalidStateProof; 17 | 18 | Ics23Proof validProof; 19 | Ics23Proof invalidProof; 20 | 21 | L1Header emptyl1header; 22 | 23 | L1Header l1header; 24 | bytes32 l2BlockHash; 25 | 26 | bytes32 apphash; 27 | 28 | L1Block l1BlockProvider = new L1Block(); 29 | address public sequencer; 30 | uint256 public sequencerPkey; 31 | 32 | OptimisticProofVerifier opProofVerifier = 33 | new OptimisticProofVerifier(address(0x5cA3f8919DF7d82Bf51a672B7D73Ec13a2705dDb)); 34 | 35 | constructor() { 36 | // generate the channel_proof.hex file with the following command: 37 | // cd test-data-generator && go run ./cmd/ --type l1 > ../test/payload/l1_block_0x4df537.hex 38 | // this is the "rlp" half-encoded header that would be sent by the relayer. this was produced 39 | // by the test-data-generator tool. 40 | l1header = abi.decode( 41 | vm.parseBytes(vm.readFile(string.concat(rootDir, "/test/payload/l1_block_0x4df537.hex"))), (L1Header) 42 | ); 43 | 44 | // this calculates the key for the given output proposal in the list of proposals that live on the 45 | // L2OO contract. The list lives on slot 3, the item we are looking for is the one with index 9 46 | // and the 2 means that the output proposal itself is 2 32bytes words in size. 47 | bytes32 l2OutputProposalKey = bytes32(uint256(keccak256(abi.encode(uint256(0x3)))) + uint256(0x9) * 2); 48 | 49 | // this happens to be the polymer height when the L2OO was updated with the output proposal 50 | // we are using in the test 51 | string memory l2BlockJson = vm.readFile(string.concat(rootDir, "/test/payload/l2_block_0x4b0.json")); 52 | l2BlockHash = abi.decode(l2BlockJson.parseRaw(".result.hash"), (bytes32)); 53 | apphash = abi.decode(l2BlockJson.parseRaw(".result.stateRoot"), (bytes32)); 54 | 55 | // the output proof height must match that of the l1 header 56 | string memory outputProposalJson = 57 | vm.readFile(string.concat(rootDir, "/test/payload/output_at_block_l1_0x4df537_with_proof.json")); 58 | 59 | validStateProof = OpL2StateProof( 60 | abi.decode(outputProposalJson.parseRaw(".result.accountProof"), (bytes[])), 61 | abi.decode(outputProposalJson.parseRaw(".result.storageProof[0].proof"), (bytes[])), 62 | l2OutputProposalKey, 63 | l2BlockHash 64 | ); 65 | 66 | validProof.height = 10; 67 | } 68 | 69 | function setL1BlockAttributes(bytes32 hash, uint64 number) public { 70 | vm.prank(l1BlockProvider.DEPOSITOR_ACCOUNT()); 71 | l1BlockProvider.setL1BlockValues( 72 | number, 73 | 0, // timestamp 74 | 0, // basefee 75 | hash, 76 | 0, // sequenceNumber 77 | bytes32(0), // batcherHash 78 | 0, // l1FeeOverhead 79 | 0 // l1FeeScalar 80 | ); 81 | } 82 | 83 | function setUp() public virtual { 84 | delete emptyl1header; 85 | delete invalidStateProof; 86 | 87 | (sequencer, sequencerPkey) = makeAddrAndKey("sequencer"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/utils/Signing.base.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../../contracts/libs/Ibc.sol"; 6 | import "../../contracts/interfaces/IProofVerifier.sol"; 7 | import "../../contracts/core/SequencerSignatureVerifier.sol"; 8 | import {RLPWriter} from "optimism/libraries/rlp/RLPWriter.sol"; 9 | import {L1Block} from "optimism/L2/L1Block.sol"; 10 | 11 | contract SigningBase is Test { 12 | using stdJson for string; 13 | 14 | string rootDir = vm.projectRoot(); 15 | 16 | bytes32 l1BlockHash; 17 | bytes32 l1AncestorBlockHash; 18 | bytes32 peptideAppHash; 19 | uint256 public peptideBlockNumber; 20 | uint256 public sequencerPkey; 21 | uint256 public notSequencerPkey; 22 | address public sequencer; 23 | bytes32 hashToSign; 24 | bytes32 domain; // Domain will be empty so we can leave it as initialized to default 0x 32 bytes 25 | 26 | SequencerSignatureVerifier public sigVerifier; 27 | bytes32 PEPTIDE_CHAIN_ID = bytes32(uint256(444)); 28 | 29 | L1Header childl1Block; // Child block, represents the l1 origin of dest chain when peptide catches up to ancestor L1 30 | // block 31 | L1Header ancestorL1Block; // Ancestor block, represents the l1 origin of dest chain when peptide wants to submit a 32 | 33 | // client update but is behind, so we need to checkpoint this block 34 | L1Block l1BlockProvider = new L1Block(); 35 | 36 | constructor() { 37 | (sequencer, sequencerPkey) = makeAddrAndKey("alice"); 38 | (, notSequencerPkey) = makeAddrAndKey(unicode"bob😈"); 39 | sigVerifier = new SequencerSignatureVerifier(sequencer, PEPTIDE_CHAIN_ID); 40 | 41 | // generate the channel_proof.hex file with the following command: 42 | // cd test-data-generator && go run ./cmd/ --type l1 > ../test/payload/l1_block_0x4df537.hex 43 | // this is the "rlp" half-encoded header that would be sent by the relayer. this was produced 44 | // by the test-data-generator tool. 45 | L1Header memory l1header = abi.decode( 46 | vm.parseBytes(vm.readFile(string.concat(rootDir, "/test/payload/l1_block_0x4df537.hex"))), (L1Header) 47 | ); 48 | 49 | l1BlockHash = keccak256(RLPWriter.writeList(l1header.header)); // Blockhash that will be signed by sequencer 50 | 51 | // Set l1 child block hash which is used to verify signature against 52 | l1AncestorBlockHash = readBytes32FromJson("/test/payload/l1_block_ancestor.json", ".result.hash"); 53 | 54 | peptideBlockNumber = 101; 55 | 56 | // this happens to be the polymer height when the L2OO was updated with the output proposal 57 | // we are using in the test 58 | string memory l2BlockJson = vm.readFile(string.concat(rootDir, "/test/payload/l2_block_0x4b0.json")); 59 | peptideAppHash = abi.decode(l2BlockJson.parseRaw(".result.stateRoot"), (bytes32)); 60 | 61 | bytes32 payloadHash = keccak256(abi.encodePacked(peptideBlockNumber, peptideAppHash, l1BlockHash)); 62 | hashToSign = keccak256(bytes.concat(domain, PEPTIDE_CHAIN_ID, payloadHash)); 63 | } 64 | 65 | // Read a specific bytes32 json property from a json file 66 | function readBytes32FromJson(string memory fileName, string memory property) public view returns (bytes32) { 67 | string memory blockJson = vm.readFile(string.concat(rootDir, fileName)); 68 | return abi.decode(blockJson.parseRaw(property), (bytes32)); 69 | } 70 | 71 | // Read a specific uint64 json property from a json file 72 | function readUint64FromJson(string memory fileName, string memory property) public view returns (uint64) { 73 | string memory blockJson = vm.readFile(string.concat(rootDir, fileName)); 74 | return abi.decode(blockJson.parseRaw(property), (uint64)); 75 | } 76 | 77 | // Set the block attributes on the l1 block info to that from a json file 78 | function setBlockAttributesFromJson(string memory fileName) public { 79 | bytes32 hash = readBytes32FromJson(fileName, ".result.hash"); 80 | uint64 number = readUint64FromJson(fileName, ".result.number"); 81 | setL1BlockAttributes(hash, number); 82 | } 83 | 84 | function setL1BlockAttributes(bytes32 hash, uint64 number) public { 85 | vm.prank(l1BlockProvider.DEPOSITOR_ACCOUNT()); 86 | l1BlockProvider.setL1BlockValues( 87 | number, 88 | 0, // timestamp 89 | 0, // basefee 90 | hash, 91 | 0, // sequenceNumber 92 | bytes32(0), // batcherHash 93 | 0, // l1FeeOverhead 94 | 0 // l1FeeScalar 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/utils/TestUtils.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | import {IDispatcher} from "../../contracts/interfaces/IDispatcher.sol"; 3 | import {IUniversalChannelHandler} from "../../contracts/interfaces/IUniversalChannelHandler.sol"; 4 | import {UniversalChannelHandler} from "../../contracts/core/UniversalChannelHandler.sol"; 5 | import {ILightClient} from "../../contracts/interfaces/ILightClient.sol"; 6 | import {IFeeVault} from "../../contracts/interfaces/IFeeVault.sol"; 7 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 8 | import {Dispatcher} from "../../contracts/core/Dispatcher.sol"; 9 | import {Test} from "forge-std/Test.sol"; 10 | import {Vm} from "forge-std/Vm.sol"; 11 | 12 | pragma solidity ^0.8.0; 13 | 14 | abstract contract TestUtilsTest { 15 | bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 16 | bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; 17 | bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 18 | 19 | function deployDispatcherProxyAndImpl(string memory initPortPrefix, IFeeVault feeVault) 20 | public 21 | returns (IDispatcher proxy, Dispatcher dispatcherImplementation) 22 | { 23 | dispatcherImplementation = new Dispatcher(); 24 | proxy = IDispatcher( 25 | address( 26 | new ERC1967Proxy( 27 | address(dispatcherImplementation), 28 | abi.encodeWithSelector(Dispatcher.initialize.selector, initPortPrefix, feeVault) 29 | ) 30 | ) 31 | ); 32 | } 33 | 34 | function deployUCHProxyAndImpl(address dispatcherProxy) 35 | public 36 | returns (IUniversalChannelHandler proxy, UniversalChannelHandler uchImplementation) 37 | { 38 | uchImplementation = new UniversalChannelHandler(); 39 | proxy = IUniversalChannelHandler( 40 | address( 41 | new ERC1967Proxy( 42 | address(uchImplementation), 43 | abi.encodeWithSelector(UniversalChannelHandler.initialize.selector, dispatcherProxy) 44 | ) 45 | ) 46 | ); 47 | } 48 | 49 | function getProxyImplementation(address proxy, Vm vm) public view returns (address dispatcherImplementation) { 50 | dispatcherImplementation = address(uint160(uint256(vm.load(address(proxy), _IMPLEMENTATION_SLOT)))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/utils/extract.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from 'mocha' 2 | import { assert } from 'chai' 3 | import os from 'os' 4 | import path from 'path' 5 | import fs from 'fs' 6 | import { extractVibcCoreSmartContracts } from '../src/index' 7 | 8 | function readdir(dir: string) { 9 | return fs 10 | .readdirSync(dir, { withFileTypes: true }) 11 | .map((d) => { 12 | if (d.isDirectory()) return readdir(path.join(dir, d.name)) 13 | return path.join(dir, d.name) 14 | }) 15 | .flat() 16 | } 17 | 18 | describe('Extract API', function () { 19 | const tmpdir = fs.mkdtempSync(os.tmpdir() + path.sep) 20 | 21 | after(() => { 22 | fs.rmSync(tmpdir, { recursive: true, force: true }) 23 | }) 24 | 25 | it('extracted contracts match with the built artifacts', async function () { 26 | await extractVibcCoreSmartContracts(tmpdir) 27 | const contractsDir = path.join(__dirname, '..', 'artifacts', 'contracts') 28 | const expected = readdir(contractsDir).map((d: string) => d.replace(contractsDir, '')) 29 | const actual = readdir(tmpdir).map((d: string) => d.replace(tmpdir, '')) 30 | assert.deepEqual(actual, expected) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/utils/goHelpers/cmd/receiptMMPTProof/EthProof.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type EthProof map[string][]byte 6 | 7 | // Has returns if the key exists in db. 8 | // Error is always nil. 9 | func (p EthProof) Has(key []byte) (bool, error) { 10 | _, ok := p[string(key)] 11 | return ok, nil 12 | } 13 | 14 | // Get retrieves the value for the given key. 15 | // If key is not found, return invalid key error. 16 | func (p EthProof) Get(key []byte) ([]byte, error) { 17 | if entry, ok := p[string(key)]; ok { 18 | return CopyBytes(entry), nil 19 | } 20 | return nil, fmt.Errorf("invalid key: %v", key) 21 | } 22 | 23 | // Put inserts the given key-value pair. 24 | // Existing key/value is overwritten. 25 | func (p EthProof) Put(key, value []byte) error { 26 | p[string(key)] = CopyBytes(value) 27 | return nil 28 | } 29 | 30 | // Delete removes the key from the key-value store. 31 | // If the key doesn't exist, it's a no-op without error. 32 | func (p EthProof) Delete(key []byte) error { 33 | delete(p, string(key)) 34 | return nil 35 | } 36 | 37 | // CopyBytes returns an exact copy of the provided bytes. 38 | func CopyBytes(b []byte) []byte { 39 | if b == nil { 40 | return nil 41 | } 42 | copiedBytes := make([]byte, len(b)) 43 | copy(copiedBytes, b) 44 | return copiedBytes 45 | } 46 | -------------------------------------------------------------------------------- /test/utils/goHelpers/cmd/receiptMMPTProof/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/hex" 7 | "fmt" 8 | "math/big" 9 | "os" 10 | "time" 11 | 12 | "github.com/ethereum/go-ethereum/core/rawdb" 13 | "github.com/ethereum/go-ethereum/core/types" 14 | "github.com/ethereum/go-ethereum/ethclient" 15 | "github.com/ethereum/go-ethereum/rlp" 16 | "github.com/ethereum/go-ethereum/rpc" 17 | "github.com/ethereum/go-ethereum/trie" 18 | "github.com/ethereum/go-ethereum/triedb" 19 | ) 20 | 21 | func main() { 22 | 23 | blockNumber := int64(20221924) 24 | logIdx := uint64(1) 25 | fmt.Println("blockNumber: ", blockNumber) 26 | 27 | rpcUrl := os.Getenv("OP_SEPOLIA_RPC_URL") 28 | alchemyRpc, err := ethclient.Dial(rpcUrl) 29 | if err != nil { 30 | fmt.Println("error dialing alchemyRpc: ", err) 31 | return 32 | } 33 | 34 | client := ethclient.NewClient(alchemyRpc.Client()) 35 | 36 | ctxWithTimeout, _ := context.WithTimeout(context.Background(), 10000*time.Millisecond) 37 | 38 | receipts, err := client.BlockReceipts(ctxWithTimeout, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(blockNumber))) 39 | 40 | for i, receipt := range receipts { 41 | fmt.Println("receipt: ", i, receipt.TxHash) 42 | } 43 | 44 | if err != nil { 45 | fmt.Println("error fetching receipts: ", err) 46 | return 47 | } 48 | 49 | ctxWithTimeout, _ = context.WithTimeout(context.Background(), 1000*time.Millisecond) 50 | 51 | block, err := client.HeaderByNumber(ctxWithTimeout, big.NewInt(blockNumber)) 52 | if err != nil { 53 | fmt.Println("error fetching block: ", err) 54 | return 55 | } 56 | 57 | trieFun := trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)) 58 | derivableReceipts := types.Receipts(receipts) 59 | receiptTrieRoot := types.DeriveSha(derivableReceipts, trieFun) 60 | 61 | fmt.Println("receiptTrieRoot: from deriveSha ", receiptTrieRoot.Hex()) 62 | fmt.Println("receiptTrieRoot from block ", block.ReceiptHash) 63 | 64 | if !bytes.Equal(receiptTrieRoot.Bytes(), block.ReceiptHash.Bytes()) { 65 | fmt.Println("derived receipt trie root does not match receipt trie root in eth header") 66 | return 67 | } 68 | 69 | // check that the receiptTrieRoot matches the one in the ethHeader 70 | 71 | targetReceiptProof := EthProof{} 72 | 73 | if err := trieFun.Prove(rlp.AppendUint64(nil, logIdx), &targetReceiptProof); err != nil { 74 | fmt.Println("error proving receipt: ", err) 75 | return 76 | } 77 | 78 | for key, value := range targetReceiptProof { 79 | fmt.Println("key:", hex.EncodeToString([]byte(key))) 80 | fmt.Println("value:", hex.EncodeToString(value)) 81 | } 82 | 83 | var buf bytes.Buffer 84 | receipts[0].EncodeRLP(&buf) 85 | hexStr := hex.EncodeToString(buf.Bytes()) 86 | fmt.Println("RLP-encoded receipt (hex):", hexStr) 87 | } 88 | -------------------------------------------------------------------------------- /test/utils/goHelpers/cmd/receiptMMPTProof/receiptMMPTProof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-ibc/vibc-core-smart-contracts/e463191080973d50bf48c2bddcb34be310228445/test/utils/goHelpers/cmd/receiptMMPTProof/receiptMMPTProof -------------------------------------------------------------------------------- /test/utils/goHelpers/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/open-ibc/vibc-core-smart-contracts/test/utils/goHelpers 2 | 3 | go 1.22.7 4 | 5 | toolchain go1.22.8 6 | 7 | require ( 8 | github.com/ethereum/go-ethereum v1.14.12 9 | github.com/open-ibc/vibc-core-smart-contracts/v4 v4.0.15 10 | ) 11 | 12 | require ( 13 | github.com/BurntSushi/toml v1.4.0 // indirect 14 | github.com/DataDog/zstd v1.5.6 // indirect 15 | github.com/Microsoft/go-winio v0.6.2 // indirect 16 | github.com/VictoriaMetrics/fastcache v1.12.2 // indirect 17 | github.com/allegro/bigcache v1.2.1 // indirect 18 | github.com/beorn7/perks v1.0.1 // indirect 19 | github.com/bits-and-blooms/bitset v1.16.0 // indirect 20 | github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect 21 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect 22 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 23 | github.com/cockroachdb/errors v1.11.3 // indirect 24 | github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect 25 | github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect 26 | github.com/cockroachdb/pebble v1.1.2 // indirect 27 | github.com/cockroachdb/redact v1.1.5 // indirect 28 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect 29 | github.com/consensys/bavard v0.1.22 // indirect 30 | github.com/consensys/gnark-crypto v0.14.0 // indirect 31 | github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect 32 | github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect 33 | github.com/deckarep/golang-set/v2 v2.6.0 // indirect 34 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 35 | github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241121214935-eeb0f8563c07 // indirect 36 | github.com/ethereum/c-kzg-4844 v1.0.3 // indirect 37 | github.com/ethereum/go-verkle v0.2.2 // indirect 38 | github.com/fsnotify/fsnotify v1.7.0 // indirect 39 | github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect 40 | github.com/getsentry/sentry-go v0.29.1 // indirect 41 | github.com/go-ole/go-ole v1.3.0 // indirect 42 | github.com/gofrs/flock v0.12.1 // indirect 43 | github.com/gogo/protobuf v1.3.2 // indirect 44 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect 45 | github.com/google/uuid v1.6.0 // indirect 46 | github.com/gorilla/websocket v1.5.3 // indirect 47 | github.com/hashicorp/go-bexpr v0.1.11 // indirect 48 | github.com/holiman/uint256 v1.3.1 // indirect 49 | github.com/klauspost/compress v1.17.11 // indirect 50 | github.com/kr/pretty v0.3.1 // indirect 51 | github.com/kr/text v0.2.0 // indirect 52 | github.com/mattn/go-runewidth v0.0.16 // indirect 53 | github.com/mitchellh/mapstructure v1.5.0 // indirect 54 | github.com/mmcloughlin/addchain v0.4.0 // indirect 55 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 56 | github.com/olekukonko/tablewriter v0.0.5 // indirect 57 | github.com/onsi/gomega v1.34.1 // indirect 58 | github.com/pkg/errors v0.9.1 // indirect 59 | github.com/prometheus/client_golang v1.20.5 // indirect 60 | github.com/prometheus/client_model v0.6.1 // indirect 61 | github.com/prometheus/common v0.60.1 // indirect 62 | github.com/prometheus/procfs v0.15.1 // indirect 63 | github.com/rivo/uniseg v0.4.7 // indirect 64 | github.com/rogpeppe/go-internal v1.13.1 // indirect 65 | github.com/rs/cors v1.11.1 // indirect 66 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect 67 | github.com/supranational/blst v0.3.13 // indirect 68 | github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect 69 | github.com/tklauser/go-sysconf v0.3.14 // indirect 70 | github.com/tklauser/numcpus v0.9.0 // indirect 71 | github.com/urfave/cli/v2 v2.27.4 // indirect 72 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 73 | golang.org/x/crypto v0.31.0 // indirect 74 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 75 | golang.org/x/mod v0.22.0 // indirect 76 | golang.org/x/sync v0.10.0 // indirect 77 | golang.org/x/sys v0.28.0 // indirect 78 | golang.org/x/text v0.21.0 // indirect 79 | golang.org/x/time v0.7.0 // indirect 80 | google.golang.org/protobuf v1.35.2 // indirect 81 | rsc.io/tmplfunc v0.0.3 // indirect 82 | ) 83 | 84 | replace github.com/ethereum/go-ethereum v1.14.12 => github.com/ethereum-optimism/op-geth v1.101408.1-0.20241002211323-d5a96613c22b 85 | -------------------------------------------------------------------------------- /test/utils/query-contract.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | L1RPC='https://eth-sepolia.g.alchemy.com/v2/nQBLX8FnrRZoqSQeySHwlDbmPnOmCSVe' 4 | L2OOADDR='0x5cA3f8919DF7d82Bf51a672B7D73Ec13a2705dDb' 5 | SLOT_INDEX='0x3' 6 | 7 | # Slot index of the output proposal within the output proposal list is calculated as follows: 8 | # OUTPUT_PROPOSAL_SLOT_INDEX = keccak( bytes32( SLOT_INDEX ) ) + ( OUTPUT_PROPOSAL_INDEX * OUTPUT_PROPOSAL_SIZE) 9 | # 10 | # - SLOT_INDEX is 0x3 11 | # - OUTPUT_PROPOSAL_INDEX is the element index within the list of proposals. Something the user needs to provide 12 | # - OUTPUT_PROPOSAL_SIZE is the size of the OutputProposal struct in bytes32 words. Looking at the smart contract that 13 | # defines (https://github.com/polymerdao/optimism/blob/develop/packages/contracts-bedrock/src/libraries/Types.sol#L13) 14 | # we can see this is 2 bytes32 words 15 | # 16 | # Putting it together we have: 17 | # 18 | # OUTPUT_PROPOSAL_SLOT_INDEX = \ 19 | # keccak( 0x0000000000000000000000000000000000000000000000000000000000000003 ) + \ 20 | # ( OUTPUT_PROPOSAL_INDEX * 2 ) 21 | # 22 | OUTPUT_PROPOSAL_INDEX='0x9' 23 | L1_BLOCK_HEIGHT='0x4df537' #'5109047' 24 | BASE_SLOT_HASH="$( cast keccak "$( printf '0x%064x' "$SLOT_INDEX" )" )" 25 | OUTPUT_PROPOSAL_SLOT_INDEX="$( python3 -c "print(hex($BASE_SLOT_HASH + ($OUTPUT_PROPOSAL_INDEX * 2)))" )" 26 | 27 | curl "$L1RPC" -s -X POST -H 'Content-Type: application/json' --data "$( 28 | jq -n --arg address "$L2OOADDR" --arg slot "$OUTPUT_PROPOSAL_SLOT_INDEX" --arg tag "$L1_BLOCK_HEIGHT" '{ 29 | "jsonrpc":"2.0", 30 | "id":"1", 31 | "method":"eth_getProof", 32 | "params":[$address,[$slot],$tag] 33 | }' 34 | )" > "output_at_block_l1_${L1_BLOCK_HEIGHT}_with_proof.json" 35 | 36 | curl "$L1RPC" -s -X POST -H 'Content-Type: application/json' --data "$( 37 | jq -n --arg tag "$L1_BLOCK_HEIGHT" '{ 38 | "jsonrpc":"2.0", 39 | "id":"1", 40 | "method":"eth_getBlockByNumber", 41 | "params":[$tag,false] 42 | }' 43 | )" > "l1_block_${L1_BLOCK_HEIGHT}.json" 44 | 45 | # The storageProof.value value we get there should match the outputRoot being used here 46 | # https://sepolia.etherscan.io/tx/0x8a8c03286c9da4c206386a4a567aceabfba2788419904bcb760ed68e1e48267e 47 | # that is 0x69cf8daa7e45fae8ba4bae698652bdc45cbde62057e25e41addf9000a87a99c8 48 | # 49 | # From the same page we can get the L1 block hash (0x27627c02dd7c760bbec27c2d4087899663d151c1db4940acf9e530cf6fefc83a) 50 | # so we can get the L1 state root. 51 | # 52 | # This is the block https://sepolia.etherscan.io/block/0x27627c02dd7c760bbec27c2d4087899663d151c1db4940acf9e530cf6fefc83a 53 | # and this is the state root 0x86f3b255f8b02accde34a0f7ecf3507bf8c95942e5cf311413811fa31f294886 54 | # 55 | 56 | # need to set up port forwarding to use this. See 'just forward-peptide-ports' from infra repo 57 | L2RPC='localhost:8545' 58 | L2_BLOCK_HEIGHT='0x4b0' 59 | 60 | curl -s "$L2RPC" -X POST -H 'Content-Type: application/json' --data "$( 61 | jq -n --arg tag "$L2_BLOCK_HEIGHT" '{ 62 | "jsonrpc":"2.0", 63 | "id":"1", 64 | "method":"eth_getBlockByNumber", 65 | "params":[$tag,false] 66 | }' 67 | )" > "l2_block_${L2_BLOCK_HEIGHT}.json" 68 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | "src/index.ts", 6 | "src/utils/index.ts", 7 | "src/evm/index.ts", 8 | "src/evm/schemas/index.ts", 9 | "src/utils/cli.ts", 10 | "src/utils/io.ts", 11 | "src/utils/constants.ts", 12 | "src/evm/chain.ts", 13 | "src/evm/contracts/*.ts", 14 | "src/evm/schemas/*.ts", 15 | "src/scripts/update-contracts-script.ts", 16 | "src/scripts/verify-contract-script.ts", 17 | "src/scripts/fork-deployment-test.ts", 18 | "src/scripts/deploy-multisig.ts", 19 | "src/scripts/execute-multisig-tx.ts", 20 | ], 21 | format: ["cjs", "esm"], // Build for commonJS and ESmodules 22 | dts: true, // Generate declaration file (.d.ts) 23 | splitting: false, 24 | sourcemap: true, 25 | clean: true, 26 | outDir: "./dist", 27 | target: "node18", 28 | }); 29 | --------------------------------------------------------------------------------