From 7df5e9cbbc55cf88d333672436a8124c26eaebd6 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:44:05 -0800 Subject: [PATCH] base commit --- .github/CODEOWNERS | 2 + .github/workflows/publish.yaml | 47 + .gitignore | 10 + LICENSE | 7 + README.md | 81 + examples/nextjs/.eslintrc.json | 3 + examples/nextjs/.gitignore | 36 + examples/nextjs/README.md | 36 + examples/nextjs/app/ccip-js/page.tsx | 10 + examples/nextjs/app/ccip-js/providers.tsx | 16 + examples/nextjs/app/drawer/page.tsx | 5 + examples/nextjs/app/favicon.ico | Bin 0 -> 25931 bytes examples/nextjs/app/globals.css | 9 + examples/nextjs/app/layout.tsx | 48 + examples/nextjs/app/page.tsx | 5 + examples/nextjs/components/ccip.tsx | 1258 ++ examples/nextjs/components/client-only.tsx | 20 + examples/nextjs/components/default-widget.tsx | 11 + examples/nextjs/components/drawer-widget.tsx | 31 + examples/nextjs/config/index.ts | 28 + examples/nextjs/config/tokensList.ts | 73 + examples/nextjs/config/wagmiConfig.ts | 33 + examples/nextjs/next.config.mjs | 4 + examples/nextjs/package.json | 35 + examples/nextjs/postcss.config.mjs | 8 + examples/nextjs/public/next.svg | 6 + examples/nextjs/public/vercel.svg | 1 + examples/nextjs/tailwind.config.ts | 20 + examples/nextjs/tsconfig.json | 26 + package.json | 19 + packages/ccip-js/.env.example | 5 + packages/ccip-js/.eslintignore | 2 + packages/ccip-js/.eslintrc.json | 23 + packages/ccip-js/.gitignore | 19 + packages/ccip-js/.mocharc.json | 7 + packages/ccip-js/.prettierignore | 2 + packages/ccip-js/.prettierrc | 7 + packages/ccip-js/LICENSE | 7 + packages/ccip-js/README.md | 614 + .../artifacts-compile/BridgeToken.json | 795 + .../artifacts-compile/CCIPLocalSimulator.json | 151 + .../artifacts-compile/EVM2EVMOnRamp.json | 1869 ++ .../artifacts-compile/PriceRegistry.json | 605 + .../ccip-js/artifacts-compile/Router.json | 710 + packages/ccip-js/hardhat.config.cjs | 20 + packages/ccip-js/ignition/modules/Lock.js | 18 + packages/ccip-js/jest.config.js | 8 + packages/ccip-js/package.json | 57 + packages/ccip-js/src/abi/BridgeToken.json | 787 + .../ccip-js/src/abi/CCIPLocalSimulator.json | 143 + packages/ccip-js/src/abi/IERC20Metadata.json | 224 + packages/ccip-js/src/abi/IPoolV1.json | 83 + packages/ccip-js/src/abi/OffRamp.json | 1142 ++ packages/ccip-js/src/abi/OnRamp.json | 785 + packages/ccip-js/src/abi/PriceRegistry.json | 234 + packages/ccip-js/src/abi/Router.json | 493 + .../ccip-js/src/abi/TokenAdminRegistry.json | 485 + packages/ccip-js/src/abi/TokenPool.json | 655 + packages/ccip-js/src/api.ts | 1212 ++ packages/ccip-js/src/config.ts | 39 + .../ccip-js/src/contracts/BridgeToken.sol | 26 + .../src/contracts/CCIPLocalSimulator.sol | 4 + .../ccip-js/src/contracts/EVM2EVMOffRamp.sol | 4 + .../ccip-js/src/contracts/EVM2EVMOnRamp.sol | 4 + .../ccip-js/src/contracts/PriceRegistry.sol | 285 + packages/ccip-js/src/contracts/Router.sol | 292 + packages/ccip-js/src/contracts/TokenProxy.sol | 4 + packages/ccip-js/tasks/helpers.ts | 0 packages/ccip-js/test/helpers/accounts.ts | 81 + packages/ccip-js/test/helpers/clients.ts | 21 + packages/ccip-js/test/helpers/config.ts | 177 + packages/ccip-js/test/helpers/constants.ts | 122 + packages/ccip-js/test/helpers/contracts.ts | 181 + packages/ccip-js/test/helpers/types.ts | 106 + packages/ccip-js/test/helpers/utils.ts | 8 + packages/ccip-js/test/helpers/write.ts | 45 + packages/ccip-js/test/integration.test.ts | 423 + packages/ccip-js/test/unit.test.ts | 1125 ++ packages/ccip-js/tsconfig.json | 18 + packages/ccip-react-components/.eslintrc.cjs | 18 + packages/ccip-react-components/.gitignore | 26 + packages/ccip-react-components/LICENSE | 7 + packages/ccip-react-components/README.md | 361 + .../ccip-react-components/components.json | 17 + .../ccip-react-components/lib/App.test.tsx | 56 + packages/ccip-react-components/lib/App.tsx | 53 + .../ccip-react-components/lib/AppContext.tsx | 140 + .../lib/AppDefault.test.tsx | 41 + .../ccip-react-components/lib/AppDefault.tsx | 73 + .../lib/AppProviders.tsx | 23 + .../lib/components/ActionButton.test.tsx | 67 + .../lib/components/ActionButton.tsx | 81 + .../lib/components/Balance.test.tsx | 111 + .../lib/components/Balance.tsx | 78 + .../lib/components/ConnectWallet.test.tsx | 60 + .../lib/components/ConnectWallet.tsx | 173 + .../lib/components/Drawer.test.tsx | 23 + .../lib/components/Drawer.tsx | 72 + .../lib/components/Error.tsx | 22 + .../lib/components/Fees.test.tsx | 97 + .../lib/components/Fees.tsx | 160 + .../lib/components/Logos.tsx | 39 + .../lib/components/RateLimit.test.tsx | 123 + .../lib/components/RateLimit.tsx | 88 + .../lib/components/SendButton.test.tsx | 229 + .../lib/components/SendButton.tsx | 249 + .../lib/components/svg/arbitrum.tsx | 35 + .../lib/components/svg/avalanche.tsx | 30 + .../lib/components/svg/base.tsx | 24 + .../lib/components/svg/bnb.tsx | 34 + .../lib/components/svg/browser.tsx | 60 + .../lib/components/svg/chainlink.tsx | 30 + .../lib/components/svg/chevron.tsx | 14 + .../lib/components/svg/circle.tsx | 30 + .../lib/components/svg/coinbase.tsx | 17 + .../lib/components/svg/ethereum.tsx | 43 + .../lib/components/svg/info-tooltip.tsx | 15 + .../lib/components/svg/info.tsx | 14 + .../lib/components/svg/metamask.tsx | 243 + .../lib/components/svg/optimism.tsx | 26 + .../lib/components/svg/polygon.tsx | 22 + .../lib/components/svg/square.tsx | 24 + .../lib/components/svg/swap.tsx | 39 + .../lib/components/svg/walletconnect.tsx | 22 + .../lib/components/svg/warning.tsx | 14 + .../lib/components/ui/alert.tsx | 56 + .../lib/components/ui/badge.tsx | 36 + .../lib/components/ui/button.tsx | 57 + .../lib/components/ui/card.tsx | 83 + .../lib/components/ui/form.tsx | 177 + .../lib/components/ui/input.tsx | 25 + .../lib/components/ui/label.tsx | 26 + .../lib/components/ui/popover.tsx | 29 + .../lib/components/ui/select.tsx | 159 + .../lib/components/ui/sheet.tsx | 138 + .../lib/components/ui/slider.tsx | 26 + .../lib/components/ui/tooltip.tsx | 28 + .../lib/hooks/useAppContext.ts | 4 + .../lib/hooks/useChains.ts | 79 + .../lib/hooks/useTheme.ts | 26 + .../lib/hooks/useTokens.ts | 28 + .../lib/hooks/useWallets.ts | 7 + packages/ccip-react-components/lib/index.css | 76 + packages/ccip-react-components/lib/index.ts | 4 + .../lib/pages/BridgeForm.tsx | 382 + .../lib/pages/TxProgress.test.tsx | 57 + .../lib/pages/TxProgress.tsx | 161 + .../ccip-react-components/lib/tests/setup.ts | 9 + packages/ccip-react-components/lib/types.ts | 179 + .../lib/utils/ccip-client.ts | 3 + .../ccip-react-components/lib/utils/config.ts | 78 + .../ccip-react-components/lib/utils/index.ts | 56 + packages/ccip-react-components/package.json | 77 + .../ccip-react-components/postcss.config.js | 6 + .../ccip-react-components/tailwind.config.js | 83 + packages/ccip-react-components/tsconfig.json | 32 + .../ccip-react-components/tsconfig.node.json | 10 + .../ccip-react-components/vite.config.d.ts | 2 + packages/ccip-react-components/vite.config.js | 53 + packages/ccip-react-components/vite.config.ts | 54 + pnpm-lock.yaml | 14159 ++++++++++++++++ pnpm-workspace.yaml | 3 + 162 files changed, 35706 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/publish.yaml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 examples/nextjs/.eslintrc.json create mode 100644 examples/nextjs/.gitignore create mode 100644 examples/nextjs/README.md create mode 100644 examples/nextjs/app/ccip-js/page.tsx create mode 100644 examples/nextjs/app/ccip-js/providers.tsx create mode 100644 examples/nextjs/app/drawer/page.tsx create mode 100644 examples/nextjs/app/favicon.ico create mode 100644 examples/nextjs/app/globals.css create mode 100644 examples/nextjs/app/layout.tsx create mode 100644 examples/nextjs/app/page.tsx create mode 100644 examples/nextjs/components/ccip.tsx create mode 100644 examples/nextjs/components/client-only.tsx create mode 100644 examples/nextjs/components/default-widget.tsx create mode 100644 examples/nextjs/components/drawer-widget.tsx create mode 100644 examples/nextjs/config/index.ts create mode 100644 examples/nextjs/config/tokensList.ts create mode 100644 examples/nextjs/config/wagmiConfig.ts create mode 100644 examples/nextjs/next.config.mjs create mode 100644 examples/nextjs/package.json create mode 100644 examples/nextjs/postcss.config.mjs create mode 100644 examples/nextjs/public/next.svg create mode 100644 examples/nextjs/public/vercel.svg create mode 100644 examples/nextjs/tailwind.config.ts create mode 100644 examples/nextjs/tsconfig.json create mode 100644 package.json create mode 100644 packages/ccip-js/.env.example create mode 100644 packages/ccip-js/.eslintignore create mode 100644 packages/ccip-js/.eslintrc.json create mode 100644 packages/ccip-js/.gitignore create mode 100644 packages/ccip-js/.mocharc.json create mode 100644 packages/ccip-js/.prettierignore create mode 100644 packages/ccip-js/.prettierrc create mode 100644 packages/ccip-js/LICENSE create mode 100644 packages/ccip-js/README.md create mode 100644 packages/ccip-js/artifacts-compile/BridgeToken.json create mode 100644 packages/ccip-js/artifacts-compile/CCIPLocalSimulator.json create mode 100644 packages/ccip-js/artifacts-compile/EVM2EVMOnRamp.json create mode 100644 packages/ccip-js/artifacts-compile/PriceRegistry.json create mode 100644 packages/ccip-js/artifacts-compile/Router.json create mode 100644 packages/ccip-js/hardhat.config.cjs create mode 100644 packages/ccip-js/ignition/modules/Lock.js create mode 100644 packages/ccip-js/jest.config.js create mode 100644 packages/ccip-js/package.json create mode 100644 packages/ccip-js/src/abi/BridgeToken.json create mode 100644 packages/ccip-js/src/abi/CCIPLocalSimulator.json create mode 100644 packages/ccip-js/src/abi/IERC20Metadata.json create mode 100644 packages/ccip-js/src/abi/IPoolV1.json create mode 100644 packages/ccip-js/src/abi/OffRamp.json create mode 100644 packages/ccip-js/src/abi/OnRamp.json create mode 100644 packages/ccip-js/src/abi/PriceRegistry.json create mode 100644 packages/ccip-js/src/abi/Router.json create mode 100644 packages/ccip-js/src/abi/TokenAdminRegistry.json create mode 100644 packages/ccip-js/src/abi/TokenPool.json create mode 100644 packages/ccip-js/src/api.ts create mode 100644 packages/ccip-js/src/config.ts create mode 100644 packages/ccip-js/src/contracts/BridgeToken.sol create mode 100644 packages/ccip-js/src/contracts/CCIPLocalSimulator.sol create mode 100644 packages/ccip-js/src/contracts/EVM2EVMOffRamp.sol create mode 100644 packages/ccip-js/src/contracts/EVM2EVMOnRamp.sol create mode 100644 packages/ccip-js/src/contracts/PriceRegistry.sol create mode 100644 packages/ccip-js/src/contracts/Router.sol create mode 100644 packages/ccip-js/src/contracts/TokenProxy.sol create mode 100644 packages/ccip-js/tasks/helpers.ts create mode 100644 packages/ccip-js/test/helpers/accounts.ts create mode 100644 packages/ccip-js/test/helpers/clients.ts create mode 100644 packages/ccip-js/test/helpers/config.ts create mode 100644 packages/ccip-js/test/helpers/constants.ts create mode 100644 packages/ccip-js/test/helpers/contracts.ts create mode 100644 packages/ccip-js/test/helpers/types.ts create mode 100644 packages/ccip-js/test/helpers/utils.ts create mode 100644 packages/ccip-js/test/helpers/write.ts create mode 100644 packages/ccip-js/test/integration.test.ts create mode 100644 packages/ccip-js/test/unit.test.ts create mode 100644 packages/ccip-js/tsconfig.json create mode 100644 packages/ccip-react-components/.eslintrc.cjs create mode 100644 packages/ccip-react-components/.gitignore create mode 100644 packages/ccip-react-components/LICENSE create mode 100644 packages/ccip-react-components/README.md create mode 100644 packages/ccip-react-components/components.json create mode 100644 packages/ccip-react-components/lib/App.test.tsx create mode 100644 packages/ccip-react-components/lib/App.tsx create mode 100644 packages/ccip-react-components/lib/AppContext.tsx create mode 100644 packages/ccip-react-components/lib/AppDefault.test.tsx create mode 100644 packages/ccip-react-components/lib/AppDefault.tsx create mode 100644 packages/ccip-react-components/lib/AppProviders.tsx create mode 100644 packages/ccip-react-components/lib/components/ActionButton.test.tsx create mode 100644 packages/ccip-react-components/lib/components/ActionButton.tsx create mode 100644 packages/ccip-react-components/lib/components/Balance.test.tsx create mode 100644 packages/ccip-react-components/lib/components/Balance.tsx create mode 100644 packages/ccip-react-components/lib/components/ConnectWallet.test.tsx create mode 100644 packages/ccip-react-components/lib/components/ConnectWallet.tsx create mode 100644 packages/ccip-react-components/lib/components/Drawer.test.tsx create mode 100644 packages/ccip-react-components/lib/components/Drawer.tsx create mode 100644 packages/ccip-react-components/lib/components/Error.tsx create mode 100644 packages/ccip-react-components/lib/components/Fees.test.tsx create mode 100644 packages/ccip-react-components/lib/components/Fees.tsx create mode 100644 packages/ccip-react-components/lib/components/Logos.tsx create mode 100644 packages/ccip-react-components/lib/components/RateLimit.test.tsx create mode 100644 packages/ccip-react-components/lib/components/RateLimit.tsx create mode 100644 packages/ccip-react-components/lib/components/SendButton.test.tsx create mode 100644 packages/ccip-react-components/lib/components/SendButton.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/arbitrum.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/avalanche.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/base.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/bnb.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/browser.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/chainlink.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/chevron.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/circle.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/coinbase.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/ethereum.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/info-tooltip.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/info.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/metamask.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/optimism.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/polygon.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/square.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/swap.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/walletconnect.tsx create mode 100644 packages/ccip-react-components/lib/components/svg/warning.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/alert.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/badge.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/button.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/card.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/form.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/input.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/label.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/popover.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/select.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/sheet.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/slider.tsx create mode 100644 packages/ccip-react-components/lib/components/ui/tooltip.tsx create mode 100644 packages/ccip-react-components/lib/hooks/useAppContext.ts create mode 100644 packages/ccip-react-components/lib/hooks/useChains.ts create mode 100644 packages/ccip-react-components/lib/hooks/useTheme.ts create mode 100644 packages/ccip-react-components/lib/hooks/useTokens.ts create mode 100644 packages/ccip-react-components/lib/hooks/useWallets.ts create mode 100644 packages/ccip-react-components/lib/index.css create mode 100644 packages/ccip-react-components/lib/index.ts create mode 100644 packages/ccip-react-components/lib/pages/BridgeForm.tsx create mode 100644 packages/ccip-react-components/lib/pages/TxProgress.test.tsx create mode 100644 packages/ccip-react-components/lib/pages/TxProgress.tsx create mode 100644 packages/ccip-react-components/lib/tests/setup.ts create mode 100644 packages/ccip-react-components/lib/types.ts create mode 100644 packages/ccip-react-components/lib/utils/ccip-client.ts create mode 100644 packages/ccip-react-components/lib/utils/config.ts create mode 100644 packages/ccip-react-components/lib/utils/index.ts create mode 100644 packages/ccip-react-components/package.json create mode 100644 packages/ccip-react-components/postcss.config.js create mode 100644 packages/ccip-react-components/tailwind.config.js create mode 100644 packages/ccip-react-components/tsconfig.json create mode 100644 packages/ccip-react-components/tsconfig.node.json create mode 100644 packages/ccip-react-components/vite.config.d.ts create mode 100644 packages/ccip-react-components/vite.config.js create mode 100644 packages/ccip-react-components/vite.config.ts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..cd58e8b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Root +* @smartcontractkit/prodsec-public diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..0fec7e1 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,47 @@ +name: Publish to NPM + +on: + push: + branches: + - "main" + +jobs: + build-and-publish: + runs-on: ubuntu-latest + environment: publish + steps: + - name: Checkout the repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + fetch-depth: 0 + + - name: Setup Node 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.12 + always-auth: true + + - name: Install PNPM + run: npm install -g pnpm@9.4.0 + shell: bash + + - name: Install dependencies + run: + pnpm install --frozen-lockfile --strict-peer-dependencies --filter=ccip-js --filter=ccip-react-components + shell: bash + + - name: Publish ccip-js to NPM + run: | + pnpm build-ccip-js + cd packages/ccip-js + pnpm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_CCIP_JAVASCRIPT_SDK }} + pnpm publish --no-git-checks --access public + shell: bash + + - name: Publish ccip-react-components to NPM + run: | + pnpm build-components + cd packages/ccip-react-components + pnpm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_CCIP_JAVASCRIPT_SDK }} + pnpm publish --no-git-checks --access public + shell: bash \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c4bf65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# dependencies +node_modules/ + +# build +dist/ + +# misc +.env +*.tsbuildinfo +bin diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d68762 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2024 SmartContract ChainLink Limited SEZC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0177a2c --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +
+ + Chainlink logo + + +[![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/smartcontractkit/ccip-javascript-sdk/blob/main/LICENSE) +[![SDK Documentation](https://img.shields.io/static/v1?label=sdk-docs&message=latest&color=blue)](https://docs.chain.link/ccip/ccip-javascript-sdk/) +
+ +# CCIP JavaScript SDK + +The CCIP JavaScript SDK includes two packages: + - [`ccip-js`](/packages/ccip-js/README.md): A TypeScript library that provides a client for managing cross-chain token transfers that use Chainlink's [Cross-Chain Interoperability Protocol (CCIP)](https://docs.chain.link/ccip) routers. + - [`ccip-react-components`](/packages/ccip-react-components/README.md): A set of prebuilt ready-to-use UI components built on top of `ccip-js`. + + Using both packages, you can add a fully featured CCIP bridge to your app that can be styled to match your app design. + + To view more detailed documentation and more examples, visit the [Chainlink Javascript SDK Documentation](https://docs.chain.link/ccip/ccip-javascript-sdk/). + +### Prerequisites + +1. Clone the `ccip-javascript-sdk` repo: + +```sh +git clone https://github.com/smartcontractkit/ccip-javascript-sdk.git +``` + +2. [Install `pnpm`](https://pnpm.io/installation). + +3. Run `pnpm install` + + +### Run the example app + +```sh +pnpm build +``` + +```sh +pnpm dev-example +``` + +### Build packages + +If you want to make changes to the package code, you need to rebuild the packages and make sure package.json file to point to the updated local versions. + +Make sure to build the `ccip-js` package before you build the `ccip-react-components` package. The React components depend on the JS package. + +Follow these steps: + +1. Build the `ccip-js` package: + +```sh +pnpm build-ccip-js +``` + +2. Build the `ccip-react-components` package: + +```sh +pnpm build-components +``` + +3. Update the `ccip-react-components` package to use the local `ccip-js` version by modifying `packages/ccip-react-components/package.json` file. Replace the `@chainlink/ccip-js` dependency with the workspace reference: + +```sh +"@chainlink/ccip-js": "workspace:*" +``` + +4. Update the `examples/nextjs` app to use both local `ccip-js` and `ccip-react-components` version by modifying `examples/nextjs/package.json` file. Replace the `@chainlink/ccip-js` and `@chainlink/ccip-react-components` dependency with relative path: + +```sh +"@chainlink/ccip-js": "link:../../packages/ccip-js", +"@chainlink/ccip-react-components": "link:../../packages/ccip-react-components", +``` + +## Resources + +- [Chainlink CCIP Javascript SDK Documentation](https://docs.chain.link/ccip/ccip-javascript-sdk/) +- [Chainlink CCIP Documentation](https://docs.chain.link/ccip) +- [Chainlink CCIP Directory](https://docs.chain.link/ccip/directory) +- [Chainlink Documentation](https://docs.chain.link/) \ No newline at end of file diff --git a/examples/nextjs/.eslintrc.json b/examples/nextjs/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/examples/nextjs/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/examples/nextjs/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md new file mode 100644 index 0000000..c403366 --- /dev/null +++ b/examples/nextjs/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/nextjs/app/ccip-js/page.tsx b/examples/nextjs/app/ccip-js/page.tsx new file mode 100644 index 0000000..0b00b06 --- /dev/null +++ b/examples/nextjs/app/ccip-js/page.tsx @@ -0,0 +1,10 @@ +import { CCIP } from "@/components/ccip"; +import { Providers } from "./providers"; + +export default function CCIPJsPage() { + return ( + + + + ); +} diff --git a/examples/nextjs/app/ccip-js/providers.tsx b/examples/nextjs/app/ccip-js/providers.tsx new file mode 100644 index 0000000..91787dc --- /dev/null +++ b/examples/nextjs/app/ccip-js/providers.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { WagmiProvider } from 'wagmi'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactNode } from 'react'; +import { wagmiConfig } from '@/config/wagmiConfig'; + +const queryClient = new QueryClient(); + +export function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/examples/nextjs/app/drawer/page.tsx b/examples/nextjs/app/drawer/page.tsx new file mode 100644 index 0000000..13e5403 --- /dev/null +++ b/examples/nextjs/app/drawer/page.tsx @@ -0,0 +1,5 @@ +import { DrawerWidget } from '@/components/drawer-widget'; + +export default function DrawerPage() { + return ; +} diff --git a/examples/nextjs/app/favicon.ico b/examples/nextjs/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/examples/nextjs/app/globals.css b/examples/nextjs/app/globals.css new file mode 100644 index 0000000..39a9271 --- /dev/null +++ b/examples/nextjs/app/globals.css @@ -0,0 +1,9 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/examples/nextjs/app/layout.tsx b/examples/nextjs/app/layout.tsx new file mode 100644 index 0000000..bf73ccd --- /dev/null +++ b/examples/nextjs/app/layout.tsx @@ -0,0 +1,48 @@ +import type { Metadata } from 'next'; +import { Inter } from 'next/font/google'; +import './globals.css'; +import { ClientOnly } from '@/components/client-only'; +import Link from 'next/link'; + +const inter = Inter({ subsets: ['latin'] }); + +export const metadata: Metadata = { + title: 'CCIP Widget Examples', + description: 'View CCIP Widgets', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + +
+ {children} +
+ + + ); +} diff --git a/examples/nextjs/app/page.tsx b/examples/nextjs/app/page.tsx new file mode 100644 index 0000000..ad02a66 --- /dev/null +++ b/examples/nextjs/app/page.tsx @@ -0,0 +1,5 @@ +import { DefaultWidget } from '@/components/default-widget'; + +export default function Home() { + return ; +} diff --git a/examples/nextjs/components/ccip.tsx b/examples/nextjs/components/ccip.tsx new file mode 100644 index 0000000..a2dff92 --- /dev/null +++ b/examples/nextjs/components/ccip.tsx @@ -0,0 +1,1258 @@ +'use client'; + +import { + createClient, + IERC20ABI, + RateLimiterState, + TransferStatus, +} from '@chainlink/ccip-js'; +import { + useConnect, + useAccount, + useSwitchChain, + usePublicClient, + useWalletClient, +} from 'wagmi'; +import { + Address, + encodeAbiParameters, + encodeFunctionData, + Hash, + Hex, + parseEther, + PublicClient, + TransactionReceipt, + WalletClient, +} from 'viem'; +import { useState } from 'react'; + +const ccipClient = createClient(); + +export function CCIP() { + const publicClient = usePublicClient(); + const { data: walletClient } = useWalletClient(); + + return ( +
+ + {publicClient && ( + <> + + + + + + + + + + + + )} + {walletClient && ( + <> + + + + + + )} +
+ ); +} + +function ConnectWallet() { + const { chain, address } = useAccount(); + const { + connectors, + connect, + isError: isConnectError, + error: connectError, + } = useConnect(); + const { + chains, + switchChain, + error: switchError, + isError: isSwitchError, + } = useSwitchChain(); + + const [chainId, setChainId] = useState(`${chain?.id}`); + + return ( +
+

Connect Wallet:

+
+ {connectors.map((connector) => ( + + ))} +
+ {isConnectError &&

{connectError.message}

} + {address &&

{`Address: ${address}`}

} + {chain && ( + <> +

{`Connected to ${chain.name} (chainId: ${chain.id})`}

+
+ + +
+ + {isSwitchError && ( +

{switchError.message}

+ )} + + )} +
+ ); +} + +function ApproveRouter({ walletClient }: { walletClient: WalletClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [amount, setAmount] = useState(); + const [txHash, setTxHash] = useState(); + + return ( +
+

Approve Transfer

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+
+ + setAmount(target.value)} + /> +
+ + {txHash && ( +
+ + {txHash} +
+ )} +
+ ); +} + +function TransferTokensAndMessage({ + walletClient, +}: { + walletClient: WalletClient; +}) { + const [routerAddress, setRouterAddress] = useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [amount, setAmount] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [destinationAccount, setDestinationAccount] = useState(); + const [data, setData] = useState(); + const [messageId, setMessageId] = useState(); + const [txHash, setTxHash] = useState(); + + return ( +
+

Transfer Tokens

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setDestinationAccount(target.value)} + /> +
+
+ + + setData( + encodeAbiParameters( + [{ type: 'string', name: 'data' }], + [target.value] + ) + ) + } + /> +
+
+ + setAmount(target.value)} + /> +
+ + {txHash && ( +
+ + {txHash} +
+ )} + {messageId && ( +
+ + + {messageId} + +
+ )} +
+ ); +} + +function SendCCIPMessage({ walletClient }: { walletClient: WalletClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [destinationAccount, setDestinationAccount] = useState(); + const [data, setData] = useState(); + const [messageId, setMessageId] = useState(); + const [txHash, setTxHash] = useState(); + + return ( +
+

Send Message

+
+ + setRouterAddress(target.value)} + /> +
+ +
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setDestinationAccount(target.value)} + /> +
+
+ + + setData( + encodeAbiParameters( + [{ type: 'string', name: 'data' }], + [target.value] + ) + ) + } + /> +
+ + {txHash && ( +
+ + {txHash} +
+ )} + {messageId && ( +
+ + + {messageId} + +
+ )} +
+ ); +} + +function SendFunctionData({ walletClient }: { walletClient: WalletClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [destinationAccount, setDestinationAccount] = useState(); + const [amount, setAmount] = useState(); + const [data, setData] = useState(); + const [messageId, setMessageId] = useState(); + const [txHash, setTxHash] = useState(); + + return ( +
+

Send Function Data

+

Using ERC20 transfer function

+
+ + setRouterAddress(target.value)} + /> +
+ +
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setDestinationAccount(target.value)} + /> +
+
+ + setAmount(target.value)} + /> +
+ + {txHash && ( +
+ + {txHash} +
+ )} + {messageId && ( +
+ + + {messageId} + +
+ )} +
+ ); +} + +function GetAllowance({ publicClient }: { publicClient: PublicClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [account, setAccount] = useState(); + const [allowance, setAllowance] = useState(); + return ( +
+

Get allowance:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+ +
+ + setAccount(target.value)} + /> +
+ + + {allowance && ( +
+ + + {allowance} + +
+ )} +
+ ); +} + +function GetOnRampAddress({ publicClient }: { publicClient: PublicClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [onRamp, setOnRamp] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + return ( +
+

Get On-ramp address:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+ + + {onRamp && ( +
+ + {onRamp} +
+ )} +
+ ); +} + +function GetSupportedFeeTokens({ + publicClient, +}: { + publicClient: PublicClient; +}) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [supportedFeeTokens, setSupportedFeeTokens] = useState(); + + return ( +
+

Get supported fee tokens:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+ + {supportedFeeTokens && supportedFeeTokens.length > 0 && ( +
+ + + {supportedFeeTokens.map((address) => ( +
+                {address}
+              
+ ))} +
+
+ )} +
+ ); +} + +function GetLaneRateRefillLimits({ + publicClient, +}: { + publicClient: PublicClient; +}) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [rateLimits, setRateLimits] = useState(); + + return ( +
+

Get lane rate refil limits:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+ + {rateLimits && ( +
+ + +
+              {`Tokens: ${rateLimits.tokens.toLocaleString()}`}
+            
+
+              {`Last updated: ${new Date(rateLimits.lastUpdated * 1000).toLocaleString()}`}
+            
+
{`Is enabled: ${rateLimits.isEnabled.toString()}`}
+
{`Capacity: ${rateLimits.capacity.toLocaleString()}`}
+
{`Rate: ${rateLimits.rate.toLocaleString()}`}
+
+
+ )} +
+ ); +} + +function GetTokenRateLimitByLane({ + publicClient, +}: { + publicClient: PublicClient; +}) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [tokenRateLimits, setTokenRateLimits] = useState(); + + return ( +
+

Get token rate limit by lane:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+ + {tokenRateLimits && ( + <> +
+ + +
+                {`Tokens: ${tokenRateLimits.tokens.toLocaleString()}`}
+              
+
+                {`Last updated: ${new Date(tokenRateLimits.lastUpdated * 1000).toLocaleString()}`}
+              
+
{`Is enabled: ${tokenRateLimits.isEnabled.toString()}`}
+
{`Capacity: ${tokenRateLimits.capacity.toLocaleString()}`}
+
{`Rate: ${tokenRateLimits.rate.toLocaleString()}`}
+
+
+ + )} +
+ ); +} + +function IsTokenSupported({ publicClient }: { publicClient: PublicClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [isTokenSupported, setIsTokenSupported] = useState(); + + return ( +
+

Is token supported:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+ + {isTokenSupported && ( +
+ + + {isTokenSupported.toLocaleString()} + +
+ )} +
+ ); +} + +function GetTokenAdminRegistry({ + publicClient, +}: { + publicClient: PublicClient; +}) { + const [routerAddress, setRouterAddress] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [tokenAdminRegistry, setTokenAdminRegistry] = useState(); + return ( +
+

Token admin registry:

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+ + {tokenAdminRegistry && ( +
+ + + {tokenAdminRegistry.toLocaleString()} + +
+ )} +
+ ); +} + +function GetTransactionReceipt({ + publicClient, +}: { + publicClient: PublicClient; +}) { + const [hash, setHash] = useState(); + const [transactionReceipt, setTransactionReceipt] = + useState(); + + return ( +
+

Get transaction receipt:

+ +
+ + setHash(target.value)} + /> +
+ + + {transactionReceipt && ( + <> +

{`Block Number: ${transactionReceipt.blockNumber.toString()}`}

+

{`From: ${transactionReceipt.from}`}

+

{`To: ${transactionReceipt.to}`}

+

{`Status: ${transactionReceipt.status}`}

+
+ + +
+                {`Block Number: ${transactionReceipt.blockNumber.toString()}`}
+              
+
{`From: ${transactionReceipt.from}`}
+
{`To: ${transactionReceipt.to}`}
+
{`Status: ${transactionReceipt.status}`}
+
+
+ + )} +
+ ); +} + +function GetTransferStatus() { + const { chains } = useSwitchChain(); + const [destinationRouterAddress, setDestinationRouterAddress] = + useState(); + const [destinationChainId, setDestinationChainId] = useState(); + const [sourceChainSelector, setSourceChainSelector] = useState(); + const [messageId, setMessageId] = useState(); + const [transferStatus, setTransferStatus] = useState(); + + const destinationChainPublicClient = usePublicClient({ + chainId: destinationChainId, + }); + + return ( +
+

Get transfer status:

+
+
+ + setDestinationRouterAddress(target.value)} + /> +
+
+ + +
+
+ + setSourceChainSelector(target.value)} + /> +
+
+ + setMessageId(target.value)} + /> +
+
+
+ + {transferStatus &&

{transferStatus}

} +
+
+ ); +} + +function GetFee({ publicClient }: { publicClient: PublicClient }) { + const [routerAddress, setRouterAddress] = useState(); + const [tokenAddress, setTokenAddress] = useState(); + const [amount, setAmount] = useState(); + const [destinationChainSelector, setDestinationChainSelector] = + useState(); + const [destinationAccount, setDestinationAccount] = useState(); + const [data, setData] = useState(); + const [fee, setFee] = useState(); + + return ( +
+

Get fee

+
+ + setRouterAddress(target.value)} + /> +
+
+ + setTokenAddress(target.value)} + /> +
+
+ + setDestinationChainSelector(target.value)} + /> +
+
+ + setDestinationAccount(target.value)} + /> +
+
+ + + setData( + encodeAbiParameters( + [{ type: 'string', name: 'data' }], + [target.value] + ) + ) + } + /> +
+
+ + setAmount(target.value)} + /> +
+ + {fee && ( +
+ + {fee} +
+ )} +
+ ); +} diff --git a/examples/nextjs/components/client-only.tsx b/examples/nextjs/components/client-only.tsx new file mode 100644 index 0000000..ae38ba4 --- /dev/null +++ b/examples/nextjs/components/client-only.tsx @@ -0,0 +1,20 @@ +"use client"; + +import React, { useState, useEffect } from "react"; + +type ClientOnlyProps = { + children: React.ReactNode; + fallback?: React.ReactNode; +}; + +export function ClientOnly({ children, fallback }: ClientOnlyProps) { + const [hasMounted, setHasMounted] = useState(false); + + useEffect(() => { + setHasMounted(true); + }, []); + + if (!hasMounted) return <>{fallback} ?? null; + + return <>{children}; +} diff --git a/examples/nextjs/components/default-widget.tsx b/examples/nextjs/components/default-widget.tsx new file mode 100644 index 0000000..a3d923e --- /dev/null +++ b/examples/nextjs/components/default-widget.tsx @@ -0,0 +1,11 @@ +'use client'; + +import '@chainlink/ccip-react-components/dist/style.css'; +import { CCIPWidget } from '@chainlink/ccip-react-components'; + +import { tokensList } from '@/config/tokensList'; +import { config } from '@/config'; + +export function DefaultWidget() { + return ; +} diff --git a/examples/nextjs/components/drawer-widget.tsx b/examples/nextjs/components/drawer-widget.tsx new file mode 100644 index 0000000..a91fd72 --- /dev/null +++ b/examples/nextjs/components/drawer-widget.tsx @@ -0,0 +1,31 @@ +'use client'; + +import '@chainlink/ccip-react-components/dist/style.css'; +import { TDrawer, CCIPWidget } from '@chainlink/ccip-react-components'; + +import { useRef } from 'react'; +import { tokensList } from '@/config/tokensList'; +import { config } from '@/config'; + +export function DrawerWidget() { + const drawerRef = useRef(null); + + const toggleWidget = () => { + drawerRef.current?.toggleDrawer(); + }; + return ( + <> + + + + ); +} diff --git a/examples/nextjs/config/index.ts b/examples/nextjs/config/index.ts new file mode 100644 index 0000000..366a426 --- /dev/null +++ b/examples/nextjs/config/index.ts @@ -0,0 +1,28 @@ +import { Config } from '@chainlink/ccip-react-components'; + +/** Uncomment any of the properties to see it's effect. Refer to the documentation for more information */ +export const config: Config = { + // variant: 'drawer', + // fromChain: avalancheFuji.id, + // toChain: sepolia.id, + token: 'CCIP-BnM', + // chains: { + // deny: [arbitrumSepolia.id], + // from: { + // deny: [sepolia.id], + // }, + // to: { deny: [bscTestnet.id] }, + // }, + theme: { + palette: { + // background: '#ffaaaa', + // border: '#ff00ee', + // text: '#00ffc3fd', + // muted: '#004cfffc', + // input: '#ddff00', + // popover: '#97a82b', + // selected: '#aa2492', + }, + shape: { radius: 4 }, + }, +}; diff --git a/examples/nextjs/config/tokensList.ts b/examples/nextjs/config/tokensList.ts new file mode 100644 index 0000000..5641109 --- /dev/null +++ b/examples/nextjs/config/tokensList.ts @@ -0,0 +1,73 @@ +import { Token } from '@chainlink/ccip-react-components'; +import { + arbitrumSepolia, + avalancheFuji, + baseSepolia, + bscTestnet, + optimismSepolia, + polygonAmoy, + sepolia, +} from 'viem/chains'; + +export const tokensList: Token[] = [ + { + symbol: 'CCIP-BnM', + address: { + [arbitrumSepolia.id]:'0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D', + [avalancheFuji.id]: '0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4', + [baseSepolia.id]: '0x88A2d74F47a237a62e7A51cdDa67270CE381555e', + [bscTestnet.id]: '0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9', + [optimismSepolia.id]: '0x8aF4204e30565DF93352fE8E1De78925F6664dA7', + [polygonAmoy.id]: '0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4', + [sepolia.id]: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05' + }, + logoURL: + 'https://smartcontract.imgix.net/tokens/ccip-bnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + }, + { + symbol: 'CCIP-LnM', + address: { + [arbitrumSepolia.id]:'0x139E99f0ab4084E14e6bb7DacA289a91a2d92927', + [avalancheFuji.id]: '0x70F5c5C40b873EA597776DA2C21929A8282A3b35', + [baseSepolia.id]: '0xA98FA8A008371b9408195e52734b1768c0d1Cb5c', + [bscTestnet.id]: '0x79a4Fc27f69323660f5Bfc12dEe21c3cC14f5901', + [optimismSepolia.id]: '0x044a6B4b561af69D2319A2f4be5Ec327a6975D0a', + [polygonAmoy.id]: '0x3d357fb52253e86c8Ee0f80F5FfE438fD9503FF2', + [sepolia.id]: '0x466D489b6d36E7E3b824ef491C225F5830E81cC1' + }, + logoURL: + 'https://smartcontract.imgix.net/tokens/ccip-lnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + }, + { + symbol: 'GHO', + address: { + [arbitrumSepolia.id]: '0xb13Cfa6f8B2Eed2C37fB00fF0c1A59807C585810', + [avalancheFuji.id]: '0x9c04928Cc678776eC1C1C0E46ecC03a5F47A7723', + [baseSepolia.id]: '0x7CFa3f3d1cded0Da930881c609D4Dbf0012c14Bb', + [bscTestnet.id]: undefined, + [optimismSepolia.id]: undefined, + [polygonAmoy.id]: undefined, + [sepolia.id]: '0xc4bF5CbDaBE595361438F8c6a187bDc330539c60' + }, + logoURL: + 'https://smartcontract.imgix.net/tokens/gho.webp?auto=compress%2Cformat', + tags: ['stablecoin', 'default'] + }, + { + symbol: 'USDC', + address: { + [arbitrumSepolia.id]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d', + [avalancheFuji.id]: '0x5425890298aed601595a70AB815c96711a31Bc65', + [baseSepolia.id]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', + [bscTestnet.id]: undefined, + [optimismSepolia.id]: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7', + [polygonAmoy.id]: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582', + [sepolia.id]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' + }, + logoURL: + 'https://smartcontract.imgix.net/tokens/usdc.webp?auto=compress%2Cformat', + tags: ['stablecoin', 'default'] + } +]; diff --git a/examples/nextjs/config/wagmiConfig.ts b/examples/nextjs/config/wagmiConfig.ts new file mode 100644 index 0000000..a6eb864 --- /dev/null +++ b/examples/nextjs/config/wagmiConfig.ts @@ -0,0 +1,33 @@ +import { http, createConfig, Config } from 'wagmi'; +import { + arbitrumSepolia, + avalancheFuji, + baseSepolia, + bscTestnet, + optimismSepolia, + polygonAmoy, + sepolia, +} from 'viem/chains'; +import { injected } from 'wagmi/connectors'; + +export const wagmiConfig: Config = createConfig({ + chains: [ + arbitrumSepolia, + avalancheFuji, + baseSepolia, + bscTestnet, + sepolia, + optimismSepolia, + polygonAmoy, + ], + connectors: [injected()], + transports: { + [arbitrumSepolia.id]: http(), + [avalancheFuji.id]: http(), + [baseSepolia.id]: http(), + [bscTestnet.id]: http(), + [sepolia.id]: http(), + [optimismSepolia.id]: http(), + [polygonAmoy.id]: http(), + }, +}); diff --git a/examples/nextjs/next.config.mjs b/examples/nextjs/next.config.mjs new file mode 100644 index 0000000..4678774 --- /dev/null +++ b/examples/nextjs/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json new file mode 100644 index 0000000..e44d4b5 --- /dev/null +++ b/examples/nextjs/package.json @@ -0,0 +1,35 @@ +{ + "name": "example-nextjs", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "bdev": "pnpm run build-components && next dev", + "dev": "next dev", + "build-ccip-js": "pnpm --filter ccip-js run build", + "build-components": "pnpm --filter ccip-react-components run build", + "build": "pnpm build-ccip-js && pnpm build-components && next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@chainlink/ccip-js": "0.2.1", + "@chainlink/ccip-react-components": "0.2.1", + "@tanstack/react-query": "^5.37.1", + "next": "14.2.3", + "react": "18", + "react-dom": "18", + "typescript": "^5", + "viem": "2.21.25", + "wagmi": "^2.12.7" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18.3.3", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "postcss": "^8", + "tailwindcss": "^3.4.1" + } +} diff --git a/examples/nextjs/postcss.config.mjs b/examples/nextjs/postcss.config.mjs new file mode 100644 index 0000000..1a69fd2 --- /dev/null +++ b/examples/nextjs/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/examples/nextjs/public/next.svg b/examples/nextjs/public/next.svg new file mode 100644 index 0000000..4f5db82 --- /dev/null +++ b/examples/nextjs/public/next.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/examples/nextjs/public/vercel.svg b/examples/nextjs/public/vercel.svg new file mode 100644 index 0000000..d2f8422 --- /dev/null +++ b/examples/nextjs/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nextjs/tailwind.config.ts b/examples/nextjs/tailwind.config.ts new file mode 100644 index 0000000..7e4bd91 --- /dev/null +++ b/examples/nextjs/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/examples/nextjs/tsconfig.json b/examples/nextjs/tsconfig.json new file mode 100644 index 0000000..e7ff90f --- /dev/null +++ b/examples/nextjs/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..78f6594 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "scripts": { + "build": "pnpm run build-ccip-js && pnpm run build-components", + "build-ccip-js": "pnpm --filter ccip-js run build", + "build-components": "pnpm --filter ccip-react-components run build", + "dev-example": "pnpm --filter example-nextjs run dev", + "clean": "rm -rf node_modules packages/*/node_modules packages/*/dist examples/nextjs/node_modules examples/nextjs/.next", + "test-ccip-js": "pnpm --filter ccip-js run test", + "test-components": "pnpm --filter ccip-react-components run test" + }, + "devDependencies": { + "c8": "^10.1.2", + "tsx": "^4.17.0" + }, + "dependencies": { + "typescript": "^5.6.3" + } +} diff --git a/packages/ccip-js/.env.example b/packages/ccip-js/.env.example new file mode 100644 index 0000000..ea868a5 --- /dev/null +++ b/packages/ccip-js/.env.example @@ -0,0 +1,5 @@ +RUST_BACKTRACE=1 + +# default Anvil private key and wallet address +PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +WALLET_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ No newline at end of file diff --git a/packages/ccip-js/.eslintignore b/packages/ccip-js/.eslintignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/packages/ccip-js/.eslintignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/packages/ccip-js/.eslintrc.json b/packages/ccip-js/.eslintrc.json new file mode 100644 index 0000000..2f91c85 --- /dev/null +++ b/packages/ccip-js/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "plugins": ["@typescript-eslint", "prettier"], + "env": { + "browser": true, + "es6": true, + "node": true + }, + "rules": { + "prettier/prettier": ["error", { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + }], + "@typescript-eslint/explicit-module-boundary-types": "off" + } +} diff --git a/packages/ccip-js/.gitignore b/packages/ccip-js/.gitignore new file mode 100644 index 0000000..4509db5 --- /dev/null +++ b/packages/ccip-js/.gitignore @@ -0,0 +1,19 @@ +coverage + +node_modules +.env + +# Hardhat files +/cache +/artifacts + +# TypeChain files +/typechain +/typechain-types + +# solidity-coverage files +/coverage +/coverage.json + +# Hardhat Ignition default folder for deployments against a local node +ignition/deployments/chain-31337 diff --git a/packages/ccip-js/.mocharc.json b/packages/ccip-js/.mocharc.json new file mode 100644 index 0000000..96ec559 --- /dev/null +++ b/packages/ccip-js/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extension": ["ts"], + "spec": "test/*.ts", + "require": "hardhat/register", + "timeout": 40000, + "_": ["test/**/*.ts"] +} diff --git a/packages/ccip-js/.prettierignore b/packages/ccip-js/.prettierignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/packages/ccip-js/.prettierignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/packages/ccip-js/.prettierrc b/packages/ccip-js/.prettierrc new file mode 100644 index 0000000..c3ed579 --- /dev/null +++ b/packages/ccip-js/.prettierrc @@ -0,0 +1,7 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "tabWidth": 2, + "semi": false, + "printWidth": 120 +} diff --git a/packages/ccip-js/LICENSE b/packages/ccip-js/LICENSE new file mode 100644 index 0000000..0d68762 --- /dev/null +++ b/packages/ccip-js/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2024 SmartContract ChainLink Limited SEZC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/ccip-js/README.md b/packages/ccip-js/README.md new file mode 100644 index 0000000..3cc7892 --- /dev/null +++ b/packages/ccip-js/README.md @@ -0,0 +1,614 @@ +# CCIP-JS + + +CCIP-JS is a TypeScript library that provides a client for managing cross-chain token transfers that use Chainlink's [Cross-Chain Interoperability Protocol (CCIP)](https://docs.chain.link/ccip) routers. The library utilizes types and helper functions from [Viem](https://viem.sh/). + +To learn more about CCIP, refer to the [CCIP documentation](https://docs.chain.link/ccip). + +## Table of Contents + +- [CCIP-JS](#ccip-js) + - [Table of Contents](#table-of-contents) + - [What is CCIP-JS?](#what-is-ccip-js) + - [Why CCIP-JS?](#why-ccip-js) + - [Features](#features) + - [Installation](#installation) + - [Usage](#usage) + - [API Reference](#api-reference) + - [Client](#client) + - [createClient](#createclient) + - [RateLimiterState](#ratelimiterstate) + - [DynamicConfig](#dynamicconfig) + - [OffRamp](#offramp) + - [TransferStatus](#transferstatus) + - [Client Methods](#client-methods) + - [approveRouter](#approverouter) + - [getAllowance](#getallowance) + - [getOnRampAddress](#getonrampaddress) + - [getSupportedFeeTokens](#getsupportedfeetokens) + - [getLaneRateRefillLimits](#getlaneraterefilllimits) + - [getTokenRateLimitByLane](#gettokenratelimitbylane) + - [getFee](#getfee) + - [getTokenAdminRegistry](#gettokenadminregistry) + - [isTokenSupported](#istokensupported) + - [transferTokens](#transfertokens) + - [sendCCIPMessage](#sendccipmessage) + - [getTransferStatus](#gettransferstatus) + - [getTransactionReceipt](#gettransactionreceipt) + - [Development](#development) + - [Build](#build) + - [Running tests](#running-tests) + - [Contributing](#contributing) + - [License](#license) + + +## Why CCIP-JS? + +CCIP-JS provides ready-to-use typesafe methods for every step of the token transfer process. +Although you can do a CCIP token transfer simply by calling the [`ccipSend` function](https://docs.chain.link/ccip/api-reference/i-router-client#ccipsend), there are multiple steps that need to be done beforehand, such as: + +- checking allowances +- approving transfers +- retrieving supported lanes +- retrieving lane limits +- retrieving fee amounts and fee tokens + +Additionally, after the transfer, you may need to check the transfer status. + +## Features + +- _Token Approvals_: Approve tokens for cross-chain transfers. +- _Allowance Checks_: Retrieve the allowance for token transfers. +- _Rate Limits_: Get rate refill limits for lanes. +- _Fee Calculation_: Calculate the fee required for transfers. +- _Token Transfers_: Transfer tokens across chains. +- _Transfer Status_: Retrieve the status of a transfer by transaction hash. + +## Installation + +To install the package, use the following command: + +```sh +npm install @chainlink/ccip-js viem +``` + +Or with Yarn: + +```sh +yarn add @chainlink/ccip-js viem +``` + +Or with PNPM: + +```sh +pnpm add @chainlink/ccip-js viem +``` + +## Usage + +This example code covers the following steps: +- Initialize CCIP-JS Client for mainnet +- Approve tokens for transfer +- Get fee for the transfer +- Send the transfer through CCIP using one of the following options for fee payment: + - Using the native token fee + - Using the provided supported token for fee payment + +```typescript +import * as CCIP from '@chainlink/ccip-js' +import { createWalletClient, custom } from 'viem' +import { mainnet } from 'viem/chains' + +// Initialize CCIP-JS Client for mainnet +const ccipClient = CCIP.createClient() +const publicClient = createPublicClient({ + chain: mainnet, + transport: http(), +}) +const walletClient = createWalletClient({ + chain: mainnet, + transport: custom(window.ethereum!), +}) + +// Approve Router to transfer tokens on user's behalf +const { txHash, txReceipt } = await ccipClient.approveRouter({ + client: walletClient, + routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + amount: 1000000000000000000n, + waitForReceipt: true, +}) + +console.log(`Transfer approved. Transaction hash: ${txHash}. Transaction receipt: ${txReceipt}`) + +// Get fee for the transfer +const fee = await ccipClient.getFee({ + client: publicClient, + routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + amount: 1000000000000000000n, + destinationAccount: '0x1234567890abcdef1234567890abcdef12345678', + destinationChainSelector: '1234', +}) + +console.log(`Fee: ${fee.toLocaleString()}`) + +// Variant 1: Transfer via CCIP using native token fee +const { txHash, messageId } = await client.transferTokens({ + client: walletClient, + routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + amount: 1000000000000000000n, + destinationAccount: '0x1234567890abcdef1234567890abcdef12345678', + destinationChainSelector: '1234', +}) + +console.log(`Transfer success. Transaction hash: ${txHash}. Message ID: ${messageId}`) + +// Variant 2: Transfer via CCIP using the provided supported token for fee payment +const { txHash, messageId } = await client.transferTokens({ + client: walletClient, + routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', + amount: 1000000000000000000n, + destinationAccount: '0x1234567890abcdef1234567890abcdef12345678', + destinationChainSelector: '1234', + feeTokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', +}) +``` + +## API Reference + +### Client + +An object containing methods for cross-chain transfer management. Refer to [Client methods](#client-methods) for more information about each method. + +```typescript +export interface Client { + approveRouter(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + tokenAddress: Viem.Address + amount: bigint + waitForReceipt?: boolean + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; txReceipt?: Viem.TransactionReceipt }> + + getAllowance(options: { + client: Viem.Client + routerAddress: Viem.Address + tokenAddress: Viem.Address + account: Viem.Address + }): Promise + + getOnRampAddress(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + getSupportedFeeTokens(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + getLaneRateRefillLimits(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + getTokenRateLimitByLane(options: { + client: Viem.Client + routerAddress: Viem.Address + supportedTokenAddress: Viem.Address + destinationChainSelector: string + }): Promise + + getFee(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationAccount: Viem.Address + destinationChainSelector: string + amount: bigint + tokenAddress: Viem.Address + feeTokenAddress?: Viem.Address + message?: string + }): Promise + + getTokenAdminRegistry(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + tokenAddress: Viem.Address + }): Promise + + isTokenSupported(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + tokenAddress: Viem.Address + }): Promise + + transferTokens(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + amount: bigint + destinationAccount: Viem.Address + tokenAddress: Viem.Address + feeTokenAddress?: Viem.Address + message?: string + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> + + sendCCIPMessage(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + destinationAccount: Viem.Address + feeTokenAddress?: Viem.Address + message: string + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> + + getTransferStatus(options: { + client: Viem.Client + destinationRouterAddress: Viem.Address + sourceChainSelector: string + messageId: Viem.Hash + fromBlockNumber?: bigint + }): Promise + + getTransactionReceipt(options: { client: Viem.Client; hash: Viem.Hash }): Promise +} +``` + +### createClient + +```typescript +createClient(): Client +``` + +Creates a [Client](#client) object. + +### RateLimiterState + +Represents the state of a rate limiter using a token bucket algorithm. + +```typescript +interface RateLimiterState { + // Current number of tokens that are in the bucket. This represents the available capacity for requests. + tokens: bigint + // Timestamp in seconds of the last token refill, allows tracking token consumption over time. This is designed to be accurate for over 100 years. + lastUpdated: number + // Indicates whether the rate limiting feature is enabled or disabled. + isEnabled: boolean + // Maximum number of tokens that can be in the bucket, representing the total capacity of the limiter. + capacity: bigint + // The rate at which tokens are refilled in the bucket, measuer in tokes per second. + rate: bigint +} +``` + +### DynamicConfig + +Configuration settings for dynamic aspects of cross-chain transfers. + +The `DynamicConfig` type defines the structure of an object that holds various dynamic configuration parameters for cross-chain transactions. These settings are used to control the behavior and limits of transfers, such as gas calculations, data availability, and message size constraints. + +```typescript +type DynamicConfig = { + // The address of the router responsible for handling the cross-chain transfers. This address is used to route the transaction through the correct path. + router: Viem.Address + // The maximum number of tokens that can be included in a single message. This parameter limits the token batch size in cross-chain transfers to prevent overly large transactions. + maxNumberOfTokensPerMsg: number + // The amount of gas required per byte of payload on the destination chain. This parameter is used to calculate the total gas needed based on the size of the payload being transferred. + destGasPerPayloadByte: number + // The additional gas required on the destination chain to account for data availability overhead. This value is used to ensure that enough gas is allocated for the availability of the data being transferred. + destDataAvailabilityOverheadGas: number + // The overhead in gas that is added to the destination chain to account for base transaction costs. This value helps ensure that the transaction has enough gas to cover additional overhead on the destination chain. + destGasOverhead: number + // The gas cost per byte of data availability on the destination chain. This parameter contributes to the overall gas calculation for data availability during the transfer. + destGasPerDataAvailabilityByte: number + // The multiplier in basis points (bps) applied to the data availability gas cost. This value is used to adjust the cost of data availability by applying a scaling factor. + destDataAvailabilityMultiplierBps: number + // The address of the price registry used to obtain pricing information for gas and other costs during the transfer. This registry helps ensure that the correct prices are applied to the transaction. + priceRegistry: Viem.Address + // The maximum number of data bytes that can be included in a single message. This parameter limits the size of the data payload to prevent excessive data in one transfer. + maxDataBytes: number + // The maximum gas limit that can be applied to a single message. This parameter ensures that the transaction does not exceed a certain gas threshold, preventing overly costly operations. + maxPerMsgGasLimit: number +} +``` + +### OffRamp + +Represents the off-ramp configuration for a cross-chain transfer. + +```typescript +type OffRamp = { + // The address of the off-ramp contract on the destination blockchain. + offRamp: Viem.Address + // The selector for the source chain. + sourceChainSelector: bigint +} +``` + +### TransferStatus + +Represents the transaction status of a cross-chain transfer. + +```typescript +enum TransferStatus { + Untouched = 0, + InProgress = 1, + Success = 2, + Failure = 3, +} +``` + +### Client Methods + +#### approveRouter + +Approve the CCIP router to spend tokens for transfers and fees on behalf of the user. Returns the transaction hash and optionally the transaction receipt. + +Q: Why an approval is needed? +A: For a cross-chain transfer the CCIP router contract needs to withdraw the tokens amount and fee from the sender's wallet. The user should review and approve the parameters of the transfer before executing it. + +```typescript +approveRouter(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + tokenAddress: Viem.Address + amount: bigint + waitForReceipt?: boolean + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> +}): Promise<{ txHash: Viem.Hash; txReceipt?: Viem.TransactionReceipt }> +``` + +#### getAllowance + +Retrieves the allowance of a specified account for a cross-chain transfer. + +```typescript +getAllowance(options: { + client: Viem.Client + routerAddress: Viem.Address + tokenAddress: Viem.Address + account: Viem.Address +}): Promise +``` + +#### getOnRampAddress + +Retrieves the onRamp contract address from a router contract. + +```typescript +getOnRampAddress(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string +}): Promise +``` + +#### getSupportedFeeTokens + +Gets a list of supported tokens which can be used to pay the fees for the cross-chain transfer. + +```typescript +getSupportedFeeTokens(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string +}): Promise +``` + +#### getLaneRateRefillLimits + +Retrieves the aggregated rate refill limits for the specified lane. Returns a promise that resolves to [RateLimiterState](#ratelimiterstate) object. + +```typescript +getLaneRateRefillLimits(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string +}): Promise +``` + +#### getTokenRateLimitByLane + +Retrieves the rate refill limits for the specified token. Returns a promise that resolves to [RateLimiterState](#ratelimiterstate) object. + +```typescript +getTokenRateLimitByLane(options: { + client: Viem.Client + routerAddress: Viem.Address + supportedTokenAddress: Viem.Address + destinationChainSelector: string +}): Promise +``` + +#### getFee + +Gets the fee required for the cross-chain transfer. + +```typescript +getFee(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationAccount: Viem.Address + destinationChainSelector: string + amount: bigint + tokenAddress: Viem.Address + feeTokenAddress?: Viem.Address + message?: string +}): Promise +``` + +Get the fee required for the cross-chain transfer and/or sending cross-chain message. + +#### getTokenAdminRegistry + +Retrieve the token admin registry contract address from an onRamp contract + +```typescript +getTokenAdminRegistry(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + tokenAddress: Viem.Address +}): Promise +``` + +#### isTokenSupported + +Check if the token is supported on the destination chain. The call, and all configs are made on the source chain. + +```typescript +isTokenSupported(options: { + client: Viem.Client + routerAddress: Viem.Address // router address on source chain. + destinationChainSelector: string + tokenAddress: Viem.Address // token address on source chain. +}): Promise +``` + +#### transferTokens + +Initiates the token transfer and returns the transaction hash, cross-chain transfer message ID and transaction receipt. + +```typescript +transferTokens(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + amount: bigint + destinationAccount: Viem.Address + tokenAddress: Viem.Address + feeTokenAddress?: Viem.Address + message?: string + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> +}): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> +``` + +Initiates the token transfer and returns the transaction hash, cross-chain transfer message ID and transaction receipt. + +#### sendCCIPMessage + +Send arbitrary message through CCIP + +```typescript +sendCCIPMessage(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + destinationAccount: Viem.Address + feeTokenAddress?: Viem.Address + message: string + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number +}> +waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number +}> +}): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> +``` + +#### getTransferStatus + +Retrieves the status of a cross-chain transfer based on the message ID. Returns a promise that resolves to [TransferStatus](#transferstatus) object or `null` + +```typescript +getTransferStatus(options: { + client: Viem.Client + destinationRouterAddress: Viem.Address + sourceChainSelector: string + messageId: Viem.Hash + fromBlockNumber?: bigint +}): Promise +``` + +#### getTransactionReceipt + +Retrieves the transaction receipt based on the transaction hash. Returns a promise that resolves to [`TransactionReceipt`](https://viem.sh/docs/glossary/types.html#transactionreceipt) object. + +```typescript +getTransactionReceipt(options: { client: Viem.Client; hash: Viem.Hash }): Promise +``` + +### Development + +#### Build + +```sh +pnpm i -w +pnpm build-ccip-js +``` + +#### Running tests + +```sh +pnpm i -w +anvil +pnpm test +``` + +### Contributing + +Contributions are welcome! Please open an issue or submit a pull request on GitHub. + +1. Fork the repository. +1. Create your feature branch (git checkout -b feature/my-feature). +1. Commit your changes (git commit -m 'Add some feature'). +1. Push to the branch (git push origin feature/my-feature). +1. Open a pull request. + +## License + +CCIP-JS is available under the MIT license. diff --git a/packages/ccip-js/artifacts-compile/BridgeToken.json b/packages/ccip-js/artifacts-compile/BridgeToken.json new file mode 100644 index 0000000..d84389e --- /dev/null +++ b/packages/ccip-js/artifacts-compile/BridgeToken.json @@ -0,0 +1,795 @@ +{ + "contracts": { + "src/contracts/BridgeToken.sol:BridgeToken": { + "bridgeTokenAbi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "supplyAfterMint", + "type": "uint256" + } + ], + "name": "MaxSupplyExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotBurner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotMinter", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "BurnAccessGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "BurnAccessRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "MintAccessGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "MintAccessRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "drip", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getBurners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinters", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "grantBurnRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burnAndMinter", + "type": "address" + } + ], + "name": "grantMintAndBurnRoles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "grantMintRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "isBurner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "isMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "revokeBurnRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "revokeMintRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bridgeTokenBin": "60c060405234620003805762002083803803806200001d8162000384565b928339810190604081830312620003805780516001600160401b03919082811162000380578362000050918301620003aa565b906020938482015184811162000380576200006c9201620003aa565b9181518181116200028b576003908154906001948583811c9316801562000375575b8884101462000361578190601f938481116200030e575b508890848311600114620002ab575f926200029f575b50505f1982851b1c191690851b1782555b84519283116200028b5760049485548581811c9116801562000280575b888210146200026d5782811162000225575b5086918411600114620001be579383949184925f95620001b2575b50501b925f19911b1c19161781555b33156200017057600580546001600160a01b0319163317905560126080525f60a052604051611c6890816200041b823960805181610d67015260a0518181816103400152610aa20152f35b6064916040519162461bcd60e51b8352820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152fd5b015193505f8062000116565b9190601f19841692865f5284885f20945f5b8a898383106200020d5750505010620001f3575b50505050811b01815562000125565b01519060f8845f19921b161c191690555f808080620001e4565b868601518955909701969485019488935001620001d0565b865f52875f208380870160051c8201928a881062000263575b0160051c019086905b82811062000257575050620000fb565b5f815501869062000247565b925081926200023e565b602287634e487b7160e01b5f525260245ffd5b90607f1690620000e9565b634e487b7160e01b5f52604160045260245ffd5b015190505f80620000bb565b90879350601f19831691865f528a5f20925f5b8c828210620002f75750508411620002df575b505050811b018255620000cc565b01515f1983871b60f8161c191690555f8080620002d1565b8385015186558b97909501949384019301620002be565b909150845f52885f208480850160051c8201928b861062000357575b918991869594930160051c01915b82811062000348575050620000a5565b5f815585945089910162000338565b925081926200032a565b634e487b7160e01b5f52602260045260245ffd5b92607f16926200008e565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176200028b57604052565b919080601f84011215620003805782516001600160401b0381116200028b57602090620003e0601f8201601f1916830162000384565b9281845282828701011162000380575f5b818110620004065750825f9394955001015290565b8581018301518482018401528201620003f156fe608060409080825260049182361015610016575f80fd5b5f92833560e01c92836301ffc9a71461111c5750826306fdde0314611028578263095ea7b314610f4057826318160ddd14610f2157826323b872dd14610d8b578263313ce56714610d4d5782633950935114610d2d5782634000aea014610ba257826340c10f1914610a5657826342966c6814610a065782634334614a146109cb5782634f5632f814610968578263661884631461050a57826367a5cd06146108a05782636b32810b1461082657826370a08231146107ef57826379ba50971461073e57826379cc67901461050f57826386fe8b43146106ba5782638da5cb5b1461069157826395d89b41146105775782639dc29fac1461050f578263a457c2d71461050a578263a9059cbb146104e1578263aa271e1a1461049d578263c2e3273d1461043a578263c630948d146103c6578263c64d0ebc14610363578263d5abeb0114610328578263d73dd623146102ff578263dd62ed3e146102b2578263f2fde38b146101f457505063f81094f31461018f575f80fd5b346101f15760203660031901126101f1576101a86111dd565b6101b06113df565b6001600160a01b03166101c281611b0b565b6101ca575080f35b7fed998b960f6340d045f620c119730f7aa7995e7425c2401d3a5b64ff998a59e98280a280f35b80fd5b909150346102ae5760203660031901126102ae576102106111dd565b906102196113df565b6001600160a01b039182169233841461026b575050600680546001600160a01b03191683179055600554167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788380a380f35b906020606492519162461bcd60e51b8352820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b8280fd5b8382346102fb57806003193601126102fb57806020926102d06111dd565b6102d86111f7565b6001600160a01b0391821683526001865283832091168252845220549051908152f35b5080fd5b8382346102fb573660031901126101f15761032461031b6111dd565b602435906115b4565b5080f35b8382346102fb57816003193601126102fb57602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b83346101f15760203660031901126101f15761037d6111dd565b6103856113df565b6001600160a01b0316610397816119d7565b61039f575080f35b7f92308bb7573b2a3d17ddb868b39d8ebec433f3194421abc22d084f89658c9bad8280a280f35b83346101f15760203660031901126101f1576103e06111dd565b6103e86113df565b6001600160a01b03166103fa8161195c565b610410575b6104076113df565b610397816119d7565b807fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea8380a26103ff565b83346101f15760203660031901126101f1576104546111dd565b61045c6113df565b6001600160a01b031661046e8161195c565b610476575080f35b7fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea8280a280f35b8382346102fb5760203660031901126102fb576020906104d86001600160a01b036104c66111dd565b165f52600860205260405f2054151590565b90519015158152f35b8382346102fb57806003193601126102fb576020906104d86105016111dd565b60243590611452565b611243565b8390346102fb57826003193601126102fb576105296111dd565b60243591610542335f52600a60205260405f2054151590565b1561056057509061055d9161055882338361169f565b6117eb565b80f35b60249085519063c820b10b60e01b82523390820152fd5b9083346101f157806003193601126101f15781519181845492600184811c91818616958615610687575b6020968785108114610674579087899a92868b999a9b5291825f1461064a5750506001146105ef575b85886105eb896105dc848a038561120d565b5192828493845283019061119f565b0390f35b815286935091907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b82841061063257505050820101816105dc6105eb886105ca565b8054848a018601528895508794909301928101610618565b60ff19168882015294151560051b870190940194508593506105dc92506105eb91508990506105ca565b634e487b7160e01b835260228a52602483fd5b92607f16926105a1565b8382346102fb57816003193601126102fb5760055490516001600160a01b039091168152602090f35b5082346101f157806003193601126101f1578151918291600954808552602080950194600983527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af92905b828210610727576105eb868661071d828b038361120d565b519182918261139c565b835487529586019560019384019390910190610705565b9150346102ae57826003193601126102ae57600654916001600160a01b039182841633036107b3575050600554916bffffffffffffffffffffffff60a01b903382851617600555166006553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020606492519162461bcd60e51b8352820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152fd5b8382346102fb5760203660031901126102fb5760209181906001600160a01b036108176111dd565b16815280845220549051908152f35b5082346101f157806003193601126101f1578151918291600754808552602080950194600783527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892905b828210610889576105eb868661071d828b038361120d565b835487529586019560019384019390910190610871565b909150346102ae5760203660031901126102ae576001600160a01b036108c46111dd565b1691821561092557600254670de0b6b3a764000092838201809211610912575084925f80516020611bf38339815191529260209260025585855284835280852082815401905551908152a380f35b634e487b7160e01b865260119052602485fd5b906020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b83346101f15760203660031901126101f1576109826111dd565b61098a6113df565b6001600160a01b031661099c81611a23565b6109a4575080f35b7f0a675452746933cefe3d74182e78db7afe57ba60eaa4234b5d85e9aa41b0610c8280a280f35b8382346102fb5760203660031901126102fb576020906104d86001600160a01b036109f46111dd565b165f52600a60205260405f2054151590565b9150346102ae5760203660031901126102ae57610a2e335f52600a60205260405f2054151590565b15610a3f575061055d9035336117eb565b60249250519063c820b10b60e01b82523390820152fd5b9150346102ae57806003193601126102ae57610a706111dd565b9060243591610a8a335f52600860205260405f2054151590565b15610b8c576001600160a01b031692308414610b88577f00000000000000000000000000000000000000000000000000000000000000008015159081610b73575b50610b52578315610b1057506020825f80516020611bf383398151915292610af68795600254611431565b60025585855284835280852082815401905551908152a380f35b6020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b90610b61602493600254611431565b905163cbbf111360e01b815291820152fd5b9050610b8184600254611431565b115f610acb565b8480fd5b815163e2c8c9d560e01b81523381860152602490fd5b909150346102ae5760603660031901126102ae57610bbe6111dd565b9260249182356044359567ffffffffffffffff91828811610b885736602389011215610b885787840135838111610d1b57875198610c06601f8301601f19166020018b61120d565b818a5236888383010111610d17578187928960209301838d01378a010152610c2e8183611452565b508651818152602081018890526001600160a01b0383169290839033907fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c169080610c7a818e018f61119f565b0390a33b610c8d575b6020875160018152f35b819695963b15610b88579684918798838899610cce9951998a9586948593635260769b60e11b8552338c86015284015260606044840152606483019061119f565b03925af18015610d0d57610ce4575b8080610c83565b8311610cfb5750506020925081525f808080610cdd565b634e487b7160e01b8252604190528390fd5b85513d85823e3d90fd5b8680fd5b634e487b7160e01b8652604185528686fd5b8382346102fb57806003193601126102fb576020906104d861031b6111dd565b8382346102fb57816003193601126102fb576020905160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b9083346101f15760603660031901126101f157610da66111dd565b90610daf6111f7565b60443590610dbe82338661169f565b6001600160a01b0390811693308514610f1d5716918215610ecc578315610e7d578281528060205284812054828110610e2b5760209650918582825f80516020611bf383398151915295878b96528286520382822055868152208181540190558551908152a35160018152f35b855162461bcd60e51b8152602081890152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b845162461bcd60e51b8152602081880152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b845162461bcd60e51b8152602081880152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b8380fd5b8382346102fb57816003193601126102fb576020906002549051908152f35b9083346101f157816003193601126101f157610f5a6111dd565b6001600160a01b031690602435903083146101f1573315610fec578215610fb15760209450838291338152600187528181208582528752205582519081525f80516020611c13833981519152843392a35160018152f35b835162461bcd60e51b8152602081870152602260248201525f80516020611bd3833981519152604482015261737360f01b6064820152608490fd5b835162461bcd60e51b81526020818701526024808201525f80516020611bb38339815191526044820152637265737360e01b6064820152608490fd5b9083346101f157806003193601126101f1578151918160035492600184811c91818616958615611112575b6020968785108114610674578899509688969785829a5291825f146110eb57505060011461108f575b5050506105eb92916105dc91038561120d565b9190869350600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8284106110d357505050820101816105dc6105eb61107c565b8054848a0186015288955087949093019281016110ba565b60ff19168782015293151560051b860190930193508492506105dc91506105eb905061107c565b92607f1692611053565b8491346102ae5760203660031901126102ae573563ffffffff60e01b81168091036102ae57602092506336372b0760e01b811490811561118e575b811561117d575b811561116c575b5015158152f35b6301ffc9a760e01b14905083611165565b63e6599b4d60e01b8114915061115e565b630200057560e51b81149150611157565b91908251928382525f5b8481106111c9575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016111a9565b600435906001600160a01b03821682036111f357565b5f80fd5b602435906001600160a01b03821682036111f357565b90601f8019910116810190811067ffffffffffffffff82111761122f57604052565b634e487b7160e01b5f52604160045260245ffd5b346111f3576040806003193601126111f35761125d6111dd565b90602435915f338152602093600185528382209260018060a01b03169283835285528382205481811061134a5703903083146101f157331561130d5782156112d157838291338152600187528181208582528752205582519081525f80516020611c13833981519152843392a35160018152f35b835162461bcd60e51b815260048101869052602260248201525f80516020611bd3833981519152604482015261737360f01b6064820152608490fd5b835162461bcd60e51b8152600481018690526024808201525f80516020611bb38339815191526044820152637265737360e01b6064820152608490fd5b845162461bcd60e51b815260048101879052602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b602090816040818301928281528551809452019301915f5b8281106113c2575050505090565b83516001600160a01b0316855293810193928101926001016113b4565b6005546001600160a01b031633036113f357565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b9190820180921161143e57565b634e487b7160e01b5f52601160045260245ffd5b6001600160a01b0316903082146111f3573315611561578115611510575f338152806020526040812054908282106114bc5782604092338352826020520382822055838152208181540190556040519081525f80516020611bf383398151915260203392a3600190565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b905f90338252602090600182526115e26040918285209560018060a01b031695868652845282852054611431565b923085146101f1573315611662578415611626579083815f80516020611c1383398151915294933381526001855281812088825285522055519283523392a3600190565b815162461bcd60e51b815260048101849052602260248201525f80516020611bd3833981519152604482015261737360f01b6064820152608490fd5b815162461bcd60e51b8152600481018490526024808201525f80516020611bb38339815191526044820152637265737360e01b6064820152608490fd5b909160018060a01b03809216915f90838252602092600184526040918284209616958684528452818320545f1981036116dc575b50505050505050565b8181106117a75703913086146101f157841561176a57851561172e5790828183875f80516020611c1383398151915297969552600186528181208982528652205551908152a35f8080808080806116d3565b815162461bcd60e51b815260048101859052602260248201525f80516020611bd3833981519152604482015261737360f01b6064820152608490fd5b815162461bcd60e51b8152600481018590526024808201525f80516020611bb38339815191526044820152637265737360e01b6064820152608490fd5b825162461bcd60e51b815260048101869052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b6001600160a01b0316801561188f575f9181835282602052604083205481811061183f57815f80516020611bf3833981519152926020928587528684520360408620558060025403600255604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b6007548110156119135760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b634e487b7160e01b5f52603260045260245ffd5b6009548110156119135760095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01905f90565b5f818152600860205260408120546119d257600754600160401b8110156119be5790826119aa611994846001604096016007556118de565b819391549060031b91821b915f19901b19161790565b905560075492815260086020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b5f818152600a60205260408120546119d257600954600160401b8110156119be579082611a0f61199484600160409601600955611927565b9055600954928152600a6020522055600190565b5f818152600a60205260408120549091908015611b06575f1990808201818111611af25760095490838201918211611ade57808203611aaa575b5050506009548015611a9657810190611a7582611927565b909182549160031b1b191690556009558152600a6020526040812055600190565b634e487b7160e01b84526031600452602484fd5b611ac8611ab961199493611927565b90549060031b1c928392611927565b90558452600a60205260408420555f8080611a5d565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b5f818152600860205260408120549091908015611b06575f1990808201818111611af25760075490838201918211611ade57808203611b7e575b5050506007548015611a9657810190611b5d826118de565b909182549160031b1b19169055600755815260086020526040812055600190565b611b9c611b8d611994936118de565b90549060031b1c9283926118de565b90558452600860205260408420555f8080611b4556fe45524332303a20617070726f76652066726f6d20746865207a65726f2061646445524332303a20617070726f766520746f20746865207a65726f206164647265ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a2646970667358221220254750990ce4b5eee18ef8f8cbdc60d4c32649dc23a1edf180e107837e12171b64736f6c63430008140033" + } + }, + "version": "0.8.20+commit.a1b79de6" +} \ No newline at end of file diff --git a/packages/ccip-js/artifacts-compile/CCIPLocalSimulator.json b/packages/ccip-js/artifacts-compile/CCIPLocalSimulator.json new file mode 100644 index 0000000..124ea00 --- /dev/null +++ b/packages/ccip-js/artifacts-compile/CCIPLocalSimulator.json @@ -0,0 +1,151 @@ +{ + "contracts": { + "src/contracts/CCIPLocalSimulator.sol:CCIPLocalSimulator": { + "simulatorAbi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CCIPLocalSimulator__MsgSenderIsNotTokenOwner", + "type": "error" + }, + { + "inputs": [], + "name": "configuration", + "outputs": [ + { + "internalType": "uint64", + "name": "chainSelector_", + "type": "uint64" + }, + { + "internalType": "contract IRouterClient", + "name": "sourceRouter_", + "type": "address" + }, + { + "internalType": "contract IRouterClient", + "name": "destinationRouter_", + "type": "address" + }, + { + "internalType": "contract WETH9", + "name": "wrappedNative_", + "type": "address" + }, + { + "internalType": "contract LinkToken", + "name": "linkToken_", + "type": "address" + }, + { + "internalType": "contract BurnMintERC677Helper", + "name": "ccipBnM_", + "type": "address" + }, + { + "internalType": "contract BurnMintERC677Helper", + "name": "ccipLnM_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "getSupportedTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "isChainSupported", + "outputs": [ + { + "internalType": "bool", + "name": "supported", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "requestLinkFromFaucet", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "supportNewTokenViaGetCCIPAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "supportNewTokenViaOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "simulatorBin": "610120604090808252346101fe576001600160401b0390610876818101838111838210176101d6578291614932833903905ff080156101ea576080528151610e2e808201828110848211176101d6578291613b04833903905ff080156101ea5760a0528151611fdc80820191808310848411176101d657806100c6611b289484868439878152600881890190815267434349502d426e4d60c01b60208201526040019060208183039101526040906008815267434349502d426e4d60c01b60208201520190565b03905ff080156101f45760c052835191818301838110858211176101d6578392610130928439858152600881870190815267434349502d4c6e4d60c01b60208201526040019060208183039101526040906008815267434349502d4c6e4d60c01b60208201520190565b03905ff080156101ea5760e05281519061130b90818301908111838210176101d657829161081d833903905ff09081156101cd5761010091825260c05161018e906001600160a01b0390610185908216610202565b60e05116610202565b516105a1918261027c833960805182610211015260a05182818161023901526102fc015260c05182610261015260e05182610288015251816101d70152f35b513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b82513d5f823e3d90fd5b84513d5f823e3d90fd5b5f80fd5b5f54680100000000000000008110156101d65760018101805f55811015610267575f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630180546001600160a01b0319166001600160a01b03909216919091179055565b634e487b7160e01b5f52603260045260245ffdfe6080604081815260049182361015610015575f80fd5b5f3560e01c9081633eaa88591461038057508063657b9a4e146102af5780636c70bee9146101b9578063a48a905814610180578063c250b5f3146100db5763fbca3b7414610061575f80fd5b346100d757602091826003193601126100d7573567ffffffffffffffff811681036100d75761008f906104bc565b81519283928180850191818652845180935285019301915f5b8281106100b757505050500390f35b83516001600160a01b0316855286955093810193928101926001016100a8565b5f80fd5b50346100d75760203660031901126100d7576100f56103d8565b81516323f5a9ab60e21b81529091906001600160a01b039060208186818588165afa908115610176575f91610147575b501633036101385761013682610443565b005b51634962332f60e11b81529050fd5b610169915060203d60201161016f575b61016181836103ee565b810190610424565b5f610125565b503d610157565b83513d5f823e3d90fd5b5090346100d75760203660031901126100d7573567ffffffffffffffff81168091036100d75767de41ba4fc9d91ad96020925191148152f35b50346100d7575f3660031901126100d75760e09060018060a01b03807f00000000000000000000000000000000000000000000000000000000000000001682519267de41ba4fc9d91ad98452816020850152830152807f0000000000000000000000000000000000000000000000000000000000000000166060830152807f0000000000000000000000000000000000000000000000000000000000000000166080830152807f00000000000000000000000000000000000000000000000000000000000000001660a08301527f00000000000000000000000000000000000000000000000000000000000000001660c0820152f35b5090346100d757816003193601126100d75760206102cb6103d8565b835163a9059cbb60e01b81526001600160a01b03918216938101939093526024803590840152829060449082905f907f0000000000000000000000000000000000000000000000000000000000000000165af1908115610376575f91610338575b50602091519015158152f35b90506020813d60201161036e575b81610353602093836103ee565b810103126100d757519081151582036100d75790602061032c565b3d9150610346565b82513d5f823e3d90fd5b919050346100d75760203660031901126100d75761039c6103d8565b638da5cb5b60e01b8352916001600160a01b039060208186818588165afa908115610176575f9161014757501633036101385761013682610443565b600435906001600160a01b03821682036100d757565b90601f8019910116810190811067ffffffffffffffff82111761041057604052565b634e487b7160e01b5f52604160045260245ffd5b908160209103126100d757516001600160a01b03811681036100d75790565b5f54680100000000000000008110156104105760018101805f558110156104a8575f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630180546001600160a01b0319166001600160a01b03909216919091179055565b634e487b7160e01b5f52603260045260245ffd5b67de41ba4fc9d91ad967ffffffffffffffff8092160361054b575060405180815f548083526020809301905f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563935f905b82821061052857505050610525925003826103ee565b90565b85546001600160a01b03168452600195860195879550938101939091019061050f565b604051906020820190811182821017610410576040525f81525f3681379056fea264697066735822122045901315f1908d63aa4b64452017269750138fa2a1bcd5efa958e30bafe5ddab64736f6c6343000818003360808060405234610016576112f0908161001b8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806320487ded146100b45780633cf97983146100af57806369fe0e2d146100aa57806383826b2b146100a557806396f4e9f9146100a0578063a48a90581461009b578063a8d87a3b14610096578063d6be695a14610091578063ee18e0d31461008c5763fbca3b7414610087575f80fd5b610888565b61086c565b61084f565b610827565b610802565b610579565b610549565b610531565b610432565b346100fa5760403660031901126100fa576100cd6100fe565b506024356001600160401b0381116100fa576100ed903690600401610325565b5060205f54604051908152f35b5f80fd5b600435906001600160401b03821682036100fa57565b35906001600160401b03821682036100fa57565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b0382111761015757604052565b610128565b602081019081106001600160401b0382111761015757604052565b60a081019081106001600160401b0382111761015757604052565b606081019081106001600160401b0382111761015757604052565b60c081019081106001600160401b0382111761015757604052565b90601f801991011681019081106001600160401b0382111761015757604052565b604051906101f682610177565b565b6001600160401b03811161015757601f01601f191660200190565b92919261021f826101f8565b9161022d60405193846101c8565b8294818452818301116100fa578281602093845f960137010152565b9080601f830112156100fa5781602061026493359101610213565b90565b6001600160a01b038116036100fa57565b35906101f682610267565b9291926001600160401b038211610157576020604092604051956102ad60208360051b01886101c8565b602087838152019160061b8401938185116100fa57915b8483106102d357505050505050565b85838303126100fa5783869182516102ea8161013c565b85356102f581610267565b815282860135838201528152019201916102c4565b9080601f830112156100fa5781602061026493359101610283565b91909160a0818403126100fa5761033a6101e9565b926001600160401b0382358181116100fa5782610358918501610249565b855260208301358181116100fa5782610372918501610249565b602086015260408301358181116100fa578261038f91850161030a565b60408601526103a060608401610278565b606086015260808301359081116100fa576103bb9201610249565b6080830152565b908160a09103126100fa5790565b91908251928382525f5b8481106103fa575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016103da565b93929161042d90604092151586526060602087015260608601906103d0565b930152565b346100fa5760803660031901126100fa576001600160401b036004358181116100fa576104639036906004016103c2565b60243561ffff811681036100fa576064359061047e82610267565b60a0833603126100fa576104906101e9565b90833582526104a160208501610114565b602083015260408401358581116100fa576104bf9036908601610249565b604083015260608401358581116100fa576104dd9036908601610249565b606083015260808401359485116100fa5761050161050f9461051e9636910161030a565b608083015260443591610d35565b6040939193519384938461040e565b0390f35b908160209103126100fa573590565b346100fa5760203660031901126100fa576004355f55005b346100fa5760403660031901126100fa576105626100fe565b5061056e602435610267565b602060405160018152f35b6040806003193601126100fa5761058e6100fe565b50602435906001600160401b0382116100fa576105b0600492369084016103c2565b916105bb83806108fd565b60209150601f19016107f4576105dc6105d485806108fd565b810190610522565b6001600160a01b039081811180156107ea575b6107c3576105fd3687610325565b505f54606087018361060e82610960565b1661079057503410610780575b169161062a60808601866108fd565b61063391610ec8565b51908451948386018661064689836109ed565b0396601f1997888101825261065b90826101c8565b519020815133868201908152909791929190889060200103908101885261068290886101c8565b61068e888601896108fd565b898301959161069d878c610ac2565b9190926106a86101e9565b87815267de41ba4fc9d91ad98b8201529b868d015236906106c892610213565b60608b015236906106d892610283565b60808901525f5b6106e9868b610ac2565b9050811015610744578061073e8b8a8a610734856107178d61072e61072261071d60019c610717858c610ac2565b90610af7565b610960565b6001600160a01b031690565b96610ac2565b0135913390610ded565b016106df565b50866107509189610bec565b50939015610762578151838152602090f35b61077c8492519283926302a35ba360e21b84528301610b1b565b0390fd5b84516303ed377360e11b81528490fd5b346107b357906107a56107226107ae93610960565b30903390610ded565b61061b565b8651631841b4e160e01b81528690fd5b50505061077c6107d384806108fd565b90935193849363370d875f60e01b8552840161094f565b50600a81106105ef565b5061077c6107d384806108fd565b346100fa5760203660031901126100fa5761081b6100fe565b50602060405160018152f35b346100fa5760203660031901126100fa576108406100fe565b50602060405163499602d28152f35b346100fa575f3660031901126100fa57602060405162030d408152f35b346100fa575f3660031901126100fa5760206040516113888152f35b346100fa576020806003193601126100fa576108a26100fe565b50604051906108b08261015c565b5f8252604051918183928301818452825180915281604085019301915f5b8281106108dd57505050500390f35b83516001600160a01b0316855286955093810193928101926001016108ce565b903590601e19813603018212156100fa57018035906001600160401b0382116100fa576020019181360383136100fa57565b908060209392818452848401375f828201840152601f01601f1916010190565b91602061026493818152019161092f565b3561026481610267565b9035601e19823603018112156100fa5701602081359101916001600160401b0382116100fa5781360383136100fa57565b9190808252602080920192915f905b8282106109b8575050505090565b9091929360019085356109ca81610267565b828060a01b031681528286013583820152604080910195019201909291926109aa565b9060208252610a106109ff828061096a565b60a0602086015260c085019161092f565b610a34610a20602084018461096a565b601f1986850381016040880152939161092f565b906040830135601e19843603018112156100fa57830193602085359501946001600160401b0381116100fa578060061b360386136100fa57610a8984610ab39261026498868660a0990301606087015261099b565b94610aa9610a9960608301610278565b6001600160a01b03166080850152565b608081019061096a565b9390928286030191015261092f565b903590601e19813603018212156100fa57018035906001600160401b0382116100fa57602001918160061b360383136100fa57565b9190811015610b075760061b0190565b634e487b7160e01b5f52603260045260245ffd5b9060206102649281815201906103d0565b60405190610b398261015c565b5f8252565b9060209081835280518284015281810151916001600160401b03604093166040850152610b7a604083015160a0606087015260c08601906103d0565b9360a06080610b9a606086015197601f19988986830301848701526103d0565b94015195828503019101528080855193848152019401925f905b838210610bc357505050505090565b845180516001600160a01b03168752830151868401529485019493820193600190910190610bb4565b929091926060810151511580610d2d575b8015610d24575b8015610d0e575b610cfa577f9b877de93ea9895756e337442c657f95a34fc68e7eb988bdfa693d5be83016b6610cf291610c6f6040519560208701976385572ffb60e01b8952610c6988610c5b8660248301610b3e565b03601f1981018a52896101c8565b87611048565b96919490977fa8b0355886b5b7a28bb97e4f0a24feb172618407402721c4012d8b7c6433102f60405180610ca58b8d8b8461040e565b0390a1610cbe602085519501516001600160401b031690565b92519020604080519485526001600160401b0390931660208501523392840192909252606083019190915281906080820190565b0390a1929190565b50915050600190610d09610b2c565b905f90565b50610d1f610d1b85610f71565b1590565b610c0b565b50833b15610c04565b508215610bfd565b90939192936060820151511580610de5575b8015610ddc575b8015610dca575b610dba57610cf291610c6f7f9b877de93ea9895756e337442c657f95a34fc68e7eb988bdfa693d5be83016b6926040519660208801986385572ffb60e01b8a52610db489610da68760248301610b3e565b03601f1981018b528a6101c8565b886110eb565b5050915050600190610d09610b2c565b50610dd7610d1b86610f71565b610d55565b50843b15610d4e565b508315610d47565b905f80610e9b9460405194602097888701956323b872dd60e01b875260018060a01b03938480921660248a0152166044880152606487015260648652610e3286610177565b169260405194610e418661013c565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af13d15610ec0573d91610e80836101f8565b92610e8e60405194856101c8565b83523d5f8785013e611221565b80519081610ea857505050565b826101f693610ebb93830101910161114f565b611167565b606091611221565b905f604051610ed68161015c565b528015610f5a5781356001600160e01b03198181169160048410610f45575b5016636859a83760e01b01610f3357806004116100fa57602090820182900360031901126100fa57600460405191610f2c8361015c565b0135815290565b604051632923fee760e11b8152600490fd5b818093508460040360031b1b1616905f610ef5565b5050604051610f688161015c565b62030d40815290565b604051906020808301815f6301ffc9a760e01b9586845286602482015260248152610f9b81610192565b51617530938685fa933d5f51908661103d575b5085611033575b5084610fd2575b50505081610fc8575090565b61026491506111c6565b839450905f91839460405185810192835263ffffffff60e01b602482015260248152610ffd81610192565b5192fa5f5190913d83611028575b50508161101e575b5015905f8080610fbc565b905015155f611013565b101591505f8061100b565b151594505f610fb5565b84111595505f610fae565b929060405193611057856101ad565b60848552602085019360a0368637823b156110dc575a61138881106110cd576113871980820160061c909103018110156110be575f918291825a9560208451940192f1905a9003923d90608482116110b5575b5f908287523e929190565b608491506110aa565b6337c3be2960e01b5f5260045ffd5b632be8ca8b60e21b5f5260045ffd5b63030ed58f60e21b5f5260045ffd5b60405194926110f9866101ad565b60848652602086019460a0368737833b156110dc575a908082106110cd578291038060061c900311156110be575f918291825a9560208451940192f1905a9003923d90608482116110b5575f908287523e929190565b908160209103126100fa575180151581036100fa5790565b1561116e57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b5f602091604051838101906301ffc9a760e01b82526385572ffb60e01b6024820152602481526111f581610192565b5191617530fa5f513d82611215575b508161120e575090565b9050151590565b6020111591505f611204565b919290156112835750815115611235575090565b3b1561123e5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156112965750805190602001fd5b60405162461bcd60e51b81526020600482015290819061077c9060248301906103d056fea2646970667358221220db802c1955e384cb2a0aad222838636bd7adad5e436c3da255986d40ad72668864736f6c6343000818003360c060405234620003805762001fdc803803806200001d8162000384565b928339810190604081830312620003805780516001600160401b03919082811162000380578362000050918301620003aa565b906020938482015184811162000380576200006c9201620003aa565b9181518181116200028b576003908154906001948583811c9316801562000375575b8884101462000361578190601f938481116200030e575b508890848311600114620002ab575f926200029f575b50505f1982851b1c191690851b1782555b84519283116200028b5760049485548581811c9116801562000280575b888210146200026d5782811162000225575b5086918411600114620001be579383949184925f95620001b2575b50501b925f19911b1c19161781555b33156200017057600580546001600160a01b0319163317905560126080525f60a052604051611bc190816200041b823960805181610d0b015260a05181818161032a0152610a640152f35b6064916040519162461bcd60e51b8352820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152fd5b015193505f8062000116565b9190601f19841692865f5284885f20945f5b8a898383106200020d5750505010620001f3575b50505050811b01815562000125565b01519060f8845f19921b161c191690555f808080620001e4565b868601518955909701969485019488935001620001d0565b865f52875f208380870160051c8201928a881062000263575b0160051c019086905b82811062000257575050620000fb565b5f815501869062000247565b925081926200023e565b602287634e487b7160e01b5f525260245ffd5b90607f1690620000e9565b634e487b7160e01b5f52604160045260245ffd5b015190505f80620000bb565b90879350601f19831691865f528a5f20925f5b8c828210620002f75750508411620002df575b505050811b018255620000cc565b01515f1983871b60f8161c191690555f8080620002d1565b8385015186558b97909501949384019301620002be565b909150845f52885f208480850160051c8201928b861062000357575b918991869594930160051c01915b82811062000348575050620000a5565b5f815585945089910162000338565b925081926200032a565b634e487b7160e01b5f52602260045260245ffd5b92607f16926200008e565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176200028b57604052565b919080601f84011215620003805782516001600160401b0381116200028b57602090620003e0601f8201601f1916830162000384565b9281845282828701011162000380575f5b818110620004065750825f9394955001015290565b8581018301518482018401528201620003f156fe60806040818152600480361015610014575f80fd5b5f3560e01c92836301ffc9a7146110c15750826306fdde0314610fc2578263095ea7b314610edd57826318160ddd14610ebf57826323b872dd14610d2f578263313ce56714610cf25782633950935114610cd35782634000aea014610b5f57826340c10f1914610a1957826342966c68146109cd5782634334614a146109935782634f5632f81461093457826366188463146104e957826367a5cd06146108705782636b32810b146107f857826370a08231146107c257826379ba50971461071357826379cc6790146104ee57826386fe8b43146106915782638da5cb5b1461066957826395d89b41146105505782639dc29fac146104ee578263a457c2d7146104e9578263a9059cbb146104c1578263aa271e1a1461047e578263c2e3273d1461041f578263c630948d146103ac578263c64d0ebc1461034d578263d5abeb0114610313578263d73dd623146102ed578263dd62ed3e146102a4578263f2fde38b146101ee57505063f81094f31461018b575f80fd5b346101ea5760203660031901126101ea576101a4611181565b6101ac61172d565b6001600160a01b03166101be816119c5565b6101c457005b7fed998b960f6340d045f620c119730f7aa7995e7425c2401d3a5b64ff998a59e95f80a2005b5f80fd5b346101ea5760203660031901126101ea57610207611181565b9061021061172d565b6001600160a01b0391821692338414610261575050600680546001600160a01b03191683179055600554167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12785f80a3005b906020606492519162461bcd60e51b8352820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b50346101ea57806003193601126101ea576020906102c0611181565b6102c8611197565b9060018060a01b038091165f5260018452825f2091165f528252805f20549051908152f35b50346101ea573660031901126101ea57610311610308611181565b6024359061139f565b005b50346101ea575f3660031901126101ea57602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101ea5760203660031901126101ea57610366611181565b61036e61172d565b6001600160a01b031661038081611abd565b61038657005b7f92308bb7573b2a3d17ddb868b39d8ebec433f3194421abc22d084f89658c9bad5f80a2005b346101ea5760203660031901126101ea576103c5611181565b6103cd61172d565b6001600160a01b03166103df81611a6a565b6103f5575b6103ec61172d565b61038081611abd565b807fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea5f80a26103e4565b346101ea5760203660031901126101ea57610438611181565b61044061172d565b6001600160a01b031661045281611a6a565b61045857005b7fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea5f80a2005b50346101ea5760203660031901126101ea576020906104b86001600160a01b036104a6611181565b165f52600860205260405f2054151590565b90519015158152f35b50346101ea57806003193601126101ea576020906104b86104e0611181565b60243590611485565b6111e3565b346101ea57816003193601126101ea57610506611181565b6024359161051f335f52600a60205260405f2054151590565b156105395761031183836105348233836115e7565b61177f565b60249084519063c820b10b60e01b82523390820152fd5b90346101ea575f3660031901126101ea578051905f9083549160018360011c906001851696871561065f575b602095868410891461064c57868899858a98999a5291825f146106255750506001146105c9575b5050506105c592916105b69103856111ad565b51928284938452830190611143565b0390f35b5f90815286935091907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b82841061060d57505050820101816105b66105c56105a3565b8054848a0186015288955087949093019281016105f4565b60ff19168782015293151560051b860190930193508492506105b691506105c590506105a3565b602282634e487b7160e01b5f525260245ffd5b91607f169161057c565b50346101ea575f3660031901126101ea5760055490516001600160a01b039091168152602090f35b50346101ea575f3660031901126101ea578051600980548083525f918252602080840194927f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af92915b8282106106fc576105c586866106f2828b03836111ad565b519182918261133a565b8354875295860195600193840193909101906106da565b90346101ea575f3660031901126101ea57600654916001600160a01b03918284163303610786575050600554916bffffffffffffffffffffffff60a01b903382851617600555166006553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b906020606492519162461bcd60e51b8352820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152fd5b50346101ea5760203660031901126101ea576020906001600160a01b036107e7611181565b165f525f8252805f20549051908152f35b50346101ea575f3660031901126101ea578051600780548083525f918252602080840194927fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892915b828210610859576105c586866106f2828b03836111ad565b835487529586019560019384019390910190610841565b346101ea5760203660031901126101ea576001600160a01b03610891611181565b169182156108f157600254670de0b6b3a7640000928382018092116108de57505f925f80516020611b4c8339815191529260209260025585855284835280852082815401905551908152a3005b601190634e487b7160e01b5f525260245ffd5b906020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b346101ea5760203660031901126101ea5761094d611181565b61095561172d565b6001600160a01b0316610967816118f0565b61096d57005b7f0a675452746933cefe3d74182e78db7afe57ba60eaa4234b5d85e9aa41b0610c5f80a2005b50346101ea5760203660031901126101ea576020906104b86001600160a01b036109bb611181565b165f52600a60205260405f2054151590565b346101ea5760203660031901126101ea576109f3335f52600a60205260405f2054151590565b15610a035761031190353361177f565b602491519063c820b10b60e01b82523390820152fd5b90346101ea57806003193601126101ea57610a32611181565b9060243591610a4c335f52600860205260405f2054151590565b15610b49576001600160a01b0316923084146101ea577f00000000000000000000000000000000000000000000000000000000000000008015159081610b34575b50610b13578315610ad157506020825f80516020611b4c83398151915292610ab85f9560025461137e565b60025585855284835280852082815401905551908152a3005b6020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b90610b2260249360025461137e565b905163cbbf111360e01b815291820152fd5b9050610b428460025461137e565b1185610a8d565b815163e2c8c9d560e01b81523381860152602490fd5b90346101ea5760603660031901126101ea57610b79611181565b60249267ffffffffffffffff84356044358281116101ea57366023820112156101ea5780840135838111610cc157865191610bbe601f8301601f1916602001846111ad565b818352368983830101116101ea57815f928a602093018386013783010152610be68286611485565b5060018060a01b038516948587518481528860208201527fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16339180610c2d8c820188611143565b0390a33b610c40575b6020865160018152f35b843b156101ea57610c7a945f92838851809881958294635260769b60e11b8452338b8501528d840152606060448401526064830190611143565b03925af18015610cb757610c90575b8080610c36565b8211610ca55750602092508152828080610c89565b60418491634e487b7160e01b5f52525ffd5b84513d5f823e3d90fd5b87604186634e487b7160e01b5f52525ffd5b50346101ea57806003193601126101ea576020906104b8610308611181565b50346101ea575f3660031901126101ea576020905160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b90346101ea5760603660031901126101ea57610d49611181565b610d51611197565b60443590610d608233856115e7565b6001600160a01b03908116923084146101ea5716908115610e6e578215610e1f57815f525f602052835f2054818110610dcd5760209550815f80516020611b4c833981519152928792855f525f845203865f2055845f52855f208181540190558551908152a35160018152f35b845162461bcd60e51b8152602081880152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b835162461bcd60e51b8152602081870152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b835162461bcd60e51b8152602081870152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b50346101ea575f3660031901126101ea576020906002549051908152f35b90346101ea57806003193601126101ea57610ef6611181565b6001600160a01b03166024353082146101ea573315610f86578115610f4b5760209350335f5260018452825f20825f52845280835f205582519081525f80516020611b6c833981519152843392a35160018152f35b825162461bcd60e51b8152602081860152602260248201525f80516020611b2c833981519152604482015261737360f01b6064820152608490fd5b825162461bcd60e51b81526020818601526024808201525f80516020611b0c8339815191526044820152637265737360e01b6064820152608490fd5b90346101ea575f3660031901126101ea578051905f906003549160018360011c90600185169485156110b7575b60209586841081146110a457838852879493929187908215611082575050600114611027575b50506105c592916105b69103856111ad565b9085925060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f925b82841061106a57505050820101816105b6611015565b8054848a018601528895508794909301928101611054565b60ff19168682015292151560051b850190920192508391506105b69050611015565b602289634e487b7160e01b5f525260245ffd5b91607f1691610fef565b90346101ea5760203660031901126101ea57359063ffffffff60e01b82168092036101ea576020916336372b0760e01b8114908115611132575b8115611121575b8115611110575b5015158152f35b6301ffc9a760e01b14905083611109565b63e6599b4d60e01b81149150611102565b630200057560e51b811491506110fb565b91908251928382525f5b84811061116d575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161114d565b600435906001600160a01b03821682036101ea57565b602435906001600160a01b03821682036101ea57565b90601f8019910116810190811067ffffffffffffffff8211176111cf57604052565b634e487b7160e01b5f52604160045260245ffd5b346101ea576040806003193601126101ea576111fd611181565b9060243591335f5260209260018452825f209160018060a01b031691825f528452825f20548181106112e857033082146101ea5733156112ab57811561126f57335f5260018452825f20825f52845280835f205582519081525f80516020611b6c833981519152843392a35160018152f35b825162461bcd60e51b815260048101859052602260248201525f80516020611b2c833981519152604482015261737360f01b6064820152608490fd5b825162461bcd60e51b8152600481018590526024808201525f80516020611b0c8339815191526044820152637265737360e01b6064820152608490fd5b835162461bcd60e51b815260048101869052602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b60209060206040818301928281528551809452019301915f5b828110611361575050505090565b83516001600160a01b031685529381019392810192600101611353565b9190820180921161138b57565b634e487b7160e01b5f52601160045260245ffd5b90335f526020600181526113ca604092835f209460018060a01b031694855f528352835f205461137e565b913084146101ea57331561144957831561140e57905f80516020611b6c83398151915291335f5260018252805f20855f52825283815f2055519283523392a3600190565b60849250519062461bcd60e51b82526004820152602260248201525f80516020611b2c833981519152604482015261737360f01b6064820152fd5b60849250519062461bcd60e51b825260048201526024808201525f80516020611b0c8339815191526044820152637265737360e01b6064820152fd5b6001600160a01b0316903082146101ea57331561159457811561154357335f525f60205260405f20548181106114ef578190335f525f6020520360405f2055815f5260405f208181540190556040519081525f80516020611b4c83398151915260203392a3600190565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b909160018060a01b0380921691825f5260209160018352604091825f20951694855f528352815f20545f198103611621575b505050505050565b8181106116e95703903085146101ea5783156116ad57841561167257905f80516020611b6c8339815191529291845f5260018352805f20865f52835281815f205551908152a35f8080808080611619565b5162461bcd60e51b815260048101839052602260248201525f80516020611b2c833981519152604482015261737360f01b6064820152608490fd5b5162461bcd60e51b8152600481018390526024808201525f80516020611b0c8339815191526044820152637265737360e01b6064820152608490fd5b825162461bcd60e51b815260048101859052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b6005546001600160a01b0316330361174157565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b6001600160a01b0316801561182357805f525f60205260405f2054918083106117d3576020815f80516020611b4c833981519152925f958587528684520360408620558060025403600255604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b6009548110156118a75760095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01905f90565b634e487b7160e01b5f52603260045260245ffd5b6007548110156118a75760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b5f818152600a602052604090205480156119bf575f199080820181811161138b576009549083820191821161138b57808203611975575b50505060095480156119615781019061193f82611872565b909182549160031b1b191690556009555f52600a6020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6119a961198461199393611872565b90549060031b1c928392611872565b819391549060031b91821b915f19901b19161790565b90555f52600a60205260405f20555f8080611927565b50505f90565b5f8181526008602052604090205480156119bf575f199080820181811161138b576007549083820191821161138b57808203611a36575b505050600754801561196157810190611a14826118bb565b909182549160031b1b191690556007555f5260086020525f6040812055600190565b611a54611a45611993936118bb565b90549060031b1c9283926118bb565b90555f52600860205260405f20555f80806119fc565b805f52600860205260405f2054155f14611ab857600754600160401b8110156111cf57611aa16119938260018594016007556118bb565b9055600754905f52600860205260405f2055600190565b505f90565b805f52600a60205260405f2054155f14611ab857600954600160401b8110156111cf57611af4611993826001859401600955611872565b9055600954905f52600a60205260405f205560019056fe45524332303a20617070726f76652066726f6d20746865207a65726f2061646445524332303a20617070726f766520746f20746865207a65726f206164647265ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a2646970667358221220323b21236f528899c1e3ee240515eaf1462f2ae22b12199f1c0c2f77901816dc64736f6c63430008180033608034620003d5576040906001600160401b039080830182811182821017620003c1578352600f81526020906e21b430b4b72634b735902a37b5b2b760891b8282015283519084820182811085821117620003c1578552600493848352634c494e4b60e01b84840152815192818411620003ae576003908154946001948587811c97168015620003a3575b8888101462000390578190601f978881116200033d575b508890888311600114620002da575f92620002ce575b50505f1982851b1c191690851b1782555b8051928311620002bb5786548481811c91168015620002b0575b878210146200029d5785811162000255575b508590858411600114620001ee579383949184925f95620001e2575b50501b925f19911b1c19161783555b3315620001a357506002546b033b2e3c9fd0803ce8000000928382018092116200019057505f917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9160025533835282815284832084815401905584519384523393a351610a549081620003da8239f35b601190634e487b7160e01b5f525260245ffd5b90606493519262461bcd60e51b845283015260248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b015193505f8062000110565b9190601f19841692885f5284885f20945f5b8a898383106200023d575050501062000223575b50505050811b0183556200011f565b01519060f8845f19921b161c191690555f80808062000214565b86860151895590970196948501948893500162000200565b875f52865f208680860160051c82019289871062000293575b0160051c019085905b82811062000287575050620000f4565b5f815501859062000277565b925081926200026e565b602288634e487b7160e01b5f525260245ffd5b90607f1690620000e2565b604187634e487b7160e01b5f525260245ffd5b015190505f80620000b7565b90879350601f19831691865f528a5f20925f5b8c8282106200032657505084116200030e575b505050811b018255620000c8565b01515f1983871b60f8161c191690555f808062000300565b8385015186558b97909501949384019301620002ed565b909150845f52885f208880850160051c8201928b861062000386575b918991869594930160051c01915b82811062000377575050620000a1565b5f815585945089910162000367565b9250819262000359565b602289634e487b7160e01b5f525260245ffd5b96607f16966200008a565b604186634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe6080604090808252600480361015610015575f80fd5b5f3560e01c91826306fdde031461061757508163095ea7b3146105ee57816318160ddd146105d057816323b872dd14610507578163313ce567146104ec57816339509351146104885781634000aea01461031457816370a08231146102de57816395d89b41146101c5578163a457c2d71461012057508063a9059cbb146100f05763dd62ed3e146100a4575f80fd5b346100ec57806003193601126100ec576020906100bf610751565b6100c7610767565b9060018060a01b038091165f5260018452825f2091165f528252805f20549051908152f35b5f80fd5b50346100ec57806003193601126100ec5760209061011961010f610751565b60243590336108b1565b5160018152f35b9050346100ec57816003193601126100ec5761013a610751565b9060243590335f526001602052835f2060018060a01b0384165f52602052835f2054908282106101745760208561011985850387336107b3565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152fd5b82346100ec575f3660031901126100ec578051905f9083549160018360011c90600185169687156102d4575b60209586841089146102c157868899858a98999a5291825f1461029a57505060011461023e575b50505061023a929161022b91038561077d565b51928284938452830190610713565b0390f35b5f90815286935091907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b828410610282575050508201018161022b61023a610218565b8054848a018601528895508794909301928101610269565b60ff19168782015293151560051b8601909301935084925061022b915061023a9050610218565b602282634e487b7160e01b5f525260245ffd5b91607f16916101f1565b82346100ec5760203660031901126100ec576020906001600160a01b03610303610751565b165f525f8252805f20549051908152f35b82346100ec5760603660031901126100ec5761032e610751565b60249267ffffffffffffffff84356044358281116100ec57366023820112156100ec578084013583811161047657865191610373601f8301601f19166020018461077d565b818352368983830101116100ec57815f928a60209301838601378301015261039c8286336108b1565b60018060a01b038516948587518481528860208201527fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c163391806103e28c820188610713565b0390a33b6103f5575b6020865160018152f35b843b156100ec5761042f945f92838851809881958294635260769b60e11b8452338b8501528d840152606060448401526064830190610713565b03925af1801561046c57610445575b80806103eb565b821161045a575060209250815282808061043e565b60418491634e487b7160e01b5f52525ffd5b84513d5f823e3d90fd5b87604186634e487b7160e01b5f52525ffd5b82346100ec57806003193601126100ec576104a1610751565b335f526001602052815f2060018060a01b0382165f52602052815f20549260243584018094116104d9575060209261011991336107b3565b601190634e487b7160e01b5f525260245ffd5b82346100ec575f3660031901126100ec576020905160128152f35b9050346100ec5760603660031901126100ec57610522610751565b61052a610767565b906044359260018060a01b0382165f526001602052845f20335f52602052845f2054905f198203610564575b6020866101198787876108b1565b84821061058d575091839161058260209695610119950333836107b3565b919394819350610556565b606490602087519162461bcd60e51b8352820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b82346100ec575f3660031901126100ec576020906002549051908152f35b82346100ec57806003193601126100ec5760209061011961060d610751565b60243590336107b3565b83346100ec575f3660031901126100ec575f906003549160018360011c9060018516948515610709575b60209586841081146106f6578388528794939291879082156106d4575050600114610679575b505061023a929161022b91038561077d565b9085925060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f925b8284106106bc575050508201018161022b610667565b8054848a0186015288955087949093019281016106a6565b60ff19168682015292151560051b8501909201925083915061022b9050610667565b602289634e487b7160e01b5f525260245ffd5b91607f1691610641565b91908251928382525f5b84811061073d575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161071d565b600435906001600160a01b03821682036100ec57565b602435906001600160a01b03821682036100ec57565b90601f8019910116810190811067ffffffffffffffff82111761079f57604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160a01b0390811691821561086057169182156108105760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591835f526001825260405f20855f5282528060405f2055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b6001600160a01b039081169182156109cb571691821561097a57815f525f60205260405f205481811061092657817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092855f525f84520360405f2055845f5260405f20818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fdfea2646970667358221220a91f0c9fdce06ce8547c25288a5b0b7ef61a5d640c2ede4d8de31a041e791eb764736f6c6343000818003360806040523461010e576100135f54610112565b601f81116100c7575b507f577261707065642045746865720000000000000000000000000000000000001a5f556001805461004d90610112565b601f8111610080575b6008630ae8aa8960e31b016001556002805460ff1916601217905560405161072b908161014b8239f35b60015f52601f0160051c7fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101905b8181106100bd5750610056565b5f815582016100b0565b5f8052601f0160051c7f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563908101905b818110610103575061001c565b5f81556001016100f6565b5f80fd5b90600182811c92168015610140575b602083101461012c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169161012156fe608060409080825260049081361015610063575b5050361561001f575f80fd5b335f526003602052805f206100353482546105cf565b9055513481527fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c60203392a2005b5f3560e01c90816306fdde031461044857508063095ea7b3146103d957806318160ddd146103bf57806323b872dd1461038e5780632e1a7d4d14610305578063313ce567146102e457806370a08231146102ad57806395d89b411461018a578063a9059cbb14610158578063d0e30db0146101365763dd62ed3e146100e85780610013565b9034610132578060031936011261013257602091610104610582565b61010c610598565b6001600160a01b039182165f908152928552838320911682528352819020549051908152f35b5f80fd5b825f36600319011261013257335f526003602052805f206100353482546105cf565b8234610132578060031936011261013257602090610181610177610582565b60243590336105dc565b90519015158152f35b509034610132575f366003190112610132578051905f60018054908160011c90600183169283156102a3575b602093848410811461029057838852908115610274575060011461021e575b505050829003601f01601f191682019267ffffffffffffffff84118385101761020b575082918261020792528261053b565b0390f35b604190634e487b7160e01b5f525260245ffd5b60015f908152929350837fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b83851061026057505050508301015f80806101d5565b80548886018301529301928490820161024a565b60ff1916878501525050151560051b84010190505f80806101d5565b602289634e487b7160e01b5f525260245ffd5b91607f16916101b6565b8234610132576020366003190112610132576020906001600160a01b036102d2610582565b165f5260038252805f20549051908152f35b8234610132575f3660031901126101325760209060ff600254169051908152f35b5034610132576020366003190112610132573590335f526003602052805f2080548381106101325783610337916105ae565b9055815f8115610385575b5f80809381933390f11561037c57519081527f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b6560203392a2005b513d5f823e3d90fd5b506108fc610342565b8234610132576060366003190112610132576020906101816103ae610582565b6103b6610598565b604435916105dc565b8234610132575f3660031901126101325751478152602090f35b5090346101325780600319360112610132576020916103f6610582565b9060243590335f528452825f209160018060a01b031691825f52845280835f205582519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b90508234610132575f366003190112610132575f805460018160011c9060018316928315610531575b60209384841081146102905783885290811561051557506001146104c157505050829003601f01601f191682019267ffffffffffffffff84118385101761020b575082918261020792528261053b565b5f808052929350837f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b83851061050157505050508301018480806101d5565b8054888601830152930192849082016104eb565b60ff1916878501525050151560051b84010190508480806101d5565b91607f1691610471565b602080825282518183018190529093925f5b82811061056e57505060409293505f838284010152601f8019910116010190565b81810186015184820160400152850161054d565b600435906001600160a01b038216820361013257565b602435906001600160a01b038216820361013257565b919082039182116105bb57565b634e487b7160e01b5f52601160045260245ffd5b919082018092116105bb57565b919060018060a01b0380931691825f526020906003825260409481865f2054106101325733851415806106cc575b61066b575b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93855f5260038452865f206106468482546105ae565b90551694855f5260038352805f2061065f8382546105cf565b905551908152a3600190565b845f5260048352855f20335f52835281865f205410610132577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93855f5260048452865f20335f528452865f206106c38482546105ae565b9055935061060f565b5060048352855f20335f5283526fffffffffffffffffffffffffffffffff865f2054141561060a56fea2646970667358221220a6088a34f03560da2147d6ee51e3a49644496c32eca9c2af5f1e233cc62c972e64736f6c63430008180033" + } + }, + "version": "0.8.24+commit.e11b9ed9" +} \ No newline at end of file diff --git a/packages/ccip-js/artifacts-compile/EVM2EVMOnRamp.json b/packages/ccip-js/artifacts-compile/EVM2EVMOnRamp.json new file mode 100644 index 0000000..75ca96f --- /dev/null +++ b/packages/ccip-js/artifacts-compile/EVM2EVMOnRamp.json @@ -0,0 +1,1869 @@ +{ + "contracts": { + "src/contracts/EVM2EVMOnRamp.sol:EVM2EVMOnRamp": { + "onRampAbi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "linkToken", + "type": "address" + }, + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "defaultTxGasLimit", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "maxNopFeesJuels", + "type": "uint96" + }, + { + "internalType": "address", + "name": "prevOnRamp", + "type": "address" + }, + { + "internalType": "address", + "name": "rmnProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenAdminRegistry", + "type": "address" + } + ], + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "staticConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "uint16", + "name": "maxNumberOfTokensPerMsg", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerPayloadByte", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destDataAvailabilityOverheadGas", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerDataAvailabilityByte", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destDataAvailabilityMultiplierBps", + "type": "uint16" + }, + { + "internalType": "address", + "name": "priceRegistry", + "type": "address" + }, + { + "internalType": "uint32", + "name": "maxDataBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPerMsgGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "defaultTokenFeeUSDCents", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "defaultTokenDestGasOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "enforceOutOfOrder", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "capacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.Config", + "name": "rateLimiterConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "networkFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "gasMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "premiumMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeTokenConfigs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "minFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "deciBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "destBytesOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "aggregateRateLimitEnabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "tokenTransferFeeConfigArgs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "nop", + "type": "address" + }, + { + "internalType": "uint16", + "name": "weight", + "type": "uint16" + } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "capacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + } + ], + "name": "AggregateValueMaxCapacityExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minWaitInSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + } + ], + "name": "AggregateValueRateLimitReached", + "type": "error" + }, + { + "inputs": [], + "name": "BucketOverfilled", + "type": "error" + }, + { + "inputs": [], + "name": "CannotSendZeroTokens", + "type": "error" + }, + { + "inputs": [], + "name": "CursedByRMN", + "type": "error" + }, + { + "inputs": [], + "name": "ExtraArgOutOfOrderExecutionMustBeTrue", + "type": "error" + }, + { + "inputs": [], + "name": "GetSupportedTokensFunctionalityRemovedCheckAdminRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "InvalidChainSelector", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidConfig", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "destBytesOverhead", + "type": "uint32" + } + ], + "name": "InvalidDestBytesOverhead", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedAddress", + "type": "bytes" + } + ], + "name": "InvalidEVMAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidExtraArgsTag", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nop", + "type": "address" + } + ], + "name": "InvalidNopAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWithdrawParams", + "type": "error" + }, + { + "inputs": [], + "name": "LinkBalanceNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "MaxFeeBalanceReached", + "type": "error" + }, + { + "inputs": [], + "name": "MessageGasLimitTooHigh", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualSize", + "type": "uint256" + } + ], + "name": "MessageTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "MustBeCalledByRouter", + "type": "error" + }, + { + "inputs": [], + "name": "NoFeesToPay", + "type": "error" + }, + { + "inputs": [], + "name": "NoNopsToPay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "NotAFeeToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdminOrOwner", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdminOrNop", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "PriceNotFoundForToken", + "type": "error" + }, + { + "inputs": [], + "name": "RouterMustSetOriginalSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SourceTokenDataTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "capacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "TokenMaxCapacityExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minWaitInSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "TokenRateLimitReached", + "type": "error" + }, + { + "inputs": [], + "name": "TooManyNops", + "type": "error" + }, + { + "inputs": [], + "name": "UnsupportedNumberOfTokens", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "UnsupportedToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint64", + "name": "sequenceNumber", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "strict", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "nonce", + "type": "uint64" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feeTokenAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { + "internalType": "bytes[]", + "name": "sourceTokenData", + "type": "bytes[]" + }, + { + "internalType": "bytes32", + "name": "messageId", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct Internal.EVM2EVMMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "CCIPSendRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "capacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "indexed": false, + "internalType": "struct RateLimiter.Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "linkToken", + "type": "address" + }, + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "defaultTxGasLimit", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "maxNopFeesJuels", + "type": "uint96" + }, + { + "internalType": "address", + "name": "prevOnRamp", + "type": "address" + }, + { + "internalType": "address", + "name": "rmnProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenAdminRegistry", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "staticConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "uint16", + "name": "maxNumberOfTokensPerMsg", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerPayloadByte", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destDataAvailabilityOverheadGas", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerDataAvailabilityByte", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destDataAvailabilityMultiplierBps", + "type": "uint16" + }, + { + "internalType": "address", + "name": "priceRegistry", + "type": "address" + }, + { + "internalType": "uint32", + "name": "maxDataBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPerMsgGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "defaultTokenFeeUSDCents", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "defaultTokenDestGasOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "enforceOutOfOrder", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "networkFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "gasMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "premiumMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeConfig", + "type": "tuple[]" + } + ], + "name": "FeeConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "nop", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "NopPaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "nopWeightsTotal", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "nop", + "type": "address" + }, + { + "internalType": "uint16", + "name": "weight", + "type": "uint16" + } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "name": "NopsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "TokenTransferFeeConfigDeleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "minFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "deciBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "destBytesOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "aggregateRateLimitEnabled", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "transferFeeConfig", + "type": "tuple[]" + } + ], + "name": "TokenTransferFeeConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "tokens", + "type": "uint256" + } + ], + "name": "TokensConsumed", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentRateLimiterState", + "outputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "tokens", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "lastUpdated", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "capacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.TokenBucket", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "receiver", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "extraArgs", + "type": "bytes" + } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "feeTokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "originalSender", + "type": "address" + } + ], + "name": "forwardFromRouter", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDynamicConfig", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "uint16", + "name": "maxNumberOfTokensPerMsg", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerPayloadByte", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destDataAvailabilityOverheadGas", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerDataAvailabilityByte", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destDataAvailabilityMultiplierBps", + "type": "uint16" + }, + { + "internalType": "address", + "name": "priceRegistry", + "type": "address" + }, + { + "internalType": "uint32", + "name": "maxDataBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPerMsgGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "defaultTokenFeeUSDCents", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "defaultTokenDestGasOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "enforceOutOfOrder", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpectedNextSequenceNumber", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "receiver", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "extraArgs", + "type": "bytes" + } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "getFee", + "outputs": [ + { + "internalType": "uint256", + "name": "feeTokenAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getFeeTokenConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "networkFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "gasMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "premiumMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfig", + "name": "feeTokenConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNopFeesJuels", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNops", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "nop", + "type": "address" + }, + { + "internalType": "uint16", + "name": "weight", + "type": "uint16" + } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "weightsTotal", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + }, + { + "internalType": "contract IERC20", + "name": "sourceToken", + "type": "address" + } + ], + "name": "getPoolBySourceToken", + "outputs": [ + { + "internalType": "contract IPoolV1", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "getSenderNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticConfig", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "linkToken", + "type": "address" + }, + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "defaultTxGasLimit", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "maxNopFeesJuels", + "type": "uint96" + }, + { + "internalType": "address", + "name": "prevOnRamp", + "type": "address" + }, + { + "internalType": "address", + "name": "rmnProxy", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenAdminRegistry", + "type": "address" + } + ], + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "name": "getSupportedTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getTokenLimitAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenTransferFeeConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "minFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "deciBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "destBytesOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "aggregateRateLimitEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfig", + "name": "tokenTransferFeeConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "linkAvailableForPayment", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payNops", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "uint16", + "name": "maxNumberOfTokensPerMsg", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerPayloadByte", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destDataAvailabilityOverheadGas", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "destGasPerDataAvailabilityByte", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destDataAvailabilityMultiplierBps", + "type": "uint16" + }, + { + "internalType": "address", + "name": "priceRegistry", + "type": "address" + }, + { + "internalType": "uint32", + "name": "maxDataBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPerMsgGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "defaultTokenFeeUSDCents", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "defaultTokenDestGasOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "enforceOutOfOrder", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "name": "setDynamicConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "networkFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "gasMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "premiumMultiplierWeiPerEth", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeTokenConfigArgs", + "type": "tuple[]" + } + ], + "name": "setFeeTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "nop", + "type": "address" + }, + { + "internalType": "uint16", + "name": "weight", + "type": "uint16" + } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "name": "setNops", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "capacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setRateLimiterConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "minFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFeeUSDCents", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "deciBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "destGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "destBytesOverhead", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "aggregateRateLimitEnabled", + "type": "bool" + } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "tokenTransferFeeConfigArgs", + "type": "tuple[]" + }, + { + "internalType": "address[]", + "name": "tokensToUseDefaultFeeConfigs", + "type": "address[]" + } + ], + "name": "setTokenTransferFeeConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawNonLinkFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "onRampBin": "6080806040526004361015610012575f80fd5b5f3560e01c90816306285c6914613145575080631772047e14613041578063181f5a7714612fea57806320487ded146128765780634120fccd1461282c5780634816f4f7146123f257806348a98aa4146123b0578063504bffe014611f56578063546719cd14611e70578063549e946f14611d5e57806354b7146814611d38578063599f643114611d10578063704b6c0214611c8b5780637437ff9f14611b6357806376f6ae76146118ae57806379ba509714611809578063856c8247146117d55780638da5cb5b146117ae5780639a113c36146116fa578063b06d41bc14611613578063c92b28321461142b578063d09dc33914611409578063df0aa9e914610495578063eff7cc481461047b578063f25561fd1461022b578063f2fde38b146101755763fbca3b7414610145575f80fd5b346101715760203660031901126101715761015e6134b4565b506040516313ce2ef960e31b8152600490fd5b5f80fd5b346101715760203660031901126101715761018e613364565b610196613e6a565b6001600160a01b03908116903382146101e657816001600160601b0360a01b60015416176001555f54167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12785f80a3005b60405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606490fd5b3461017157602080600319360112610171576004356001600160401b03808211610171573660238301121561017157816004013592610269846134de565b6102766040519182613432565b8481528181019384602460a08098028301019136831161017157602401905b82821061040b575050506102a7613e36565b5f5b815181101561036d57806102bf6001928461386a565b5163ffffffff808683015116918760408201511691606090898284015116906080840151151593604051966102f3886133c5565b87528a87019586526040808801938452848801958652905160a08a901b8a9003165f908152600b8c5220955186549551925194516001600160a81b031990961691161790891b6bffffffffffffffff00000000161791901b67ffffffffffffffff60601b1617901515891b60ff60a01b16179055016102a9565b5084916040519381850192828652518093526040850195935f915b8483106103b7577f067924bf9277d905a9a4631a06d959bc032ace86b3caa835ae7e403d4f39010e87890388a1005b855180516001600160a01b031689528085015163ffffffff16898601526040808201518316908a01526060808201518316908a01526080908101511515908901529681019694830194600190920191610388565b878236031261017157848891604051610423816133fc565b61042c8561337a565b81526104398386016134f5565b83820152610449604086016134ca565b6040820152606061045b8187016134ca565b90820152608061046c818701613515565b90820152815201910190610295565b34610171575f36600319011261017157610493613ae3565b005b3461017157600319608036820112610171576104af6134b4565b906001600160401b03602435116101715760a0906024353603011261017157606435906001600160a01b038216820361017157604051632cbc26bb60e01b8152608082901b67ffffffffffffffff60801b1660048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610ed0575f916113ea575b506113d8576001600160a01b038216156113c6576005546001600160a01b031633036113b4576001600160401b037f0000000000000000000000000000000000000000000000000000000000000000166001600160401b0382160361139257506105c36105bd60846024350160243560040161361a565b90613c54565b906105d860446024350160243560040161364c565b90506106026105f0602480350160243560040161361a565b90508285516020870151151592613d85565b806111bb575b610616606460243501613681565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911681036110fe5750600e546001600160601b036106656044358216838316613995565b16906001600160601b03191617600e555b6001600160601b0380600e5416907f000000000000000000000000000000000000000000000000000000000000000016106110ec57602083015115610fdd575b6106d76106d26106cb6004602435018061361a565b36916139b0565b614021565b92600e54906106f16001600160401b038360801c166139e6565b67ffffffffffffffff60801b19909216608083901b67ffffffffffffffff60801b1617600e5580516020919091015115610f92575f5b610735606460243501613681565b610748602480350160243560040161361a565b909161075e60446024350160243560040161364c565b94909561076a896134de565b97610778604051998a613432565b898952601f196107878b6134de565b01895f5b828110610f80575050506040519b6107a28d6133e0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168d528c600160a01b600190038d169060200152600160a01b600190031660408d01526001600160401b031660608c015260808b015260a08a015f90526001600160401b031660c08a0152600160a01b600190031660e08901526044356101008901523690610839926139b0565b610120870152610848816134de565b91604051926108579084613432565b8183526020830190368360061b8201116101715780915b8360061b82018310610f6657505050506101408501526101608401525f6101808401525f5b818110610b61578360018060a01b0360208201511660018060a01b036040830151166001600160401b03606084015116608084015160a085015115156001600160401b0360c0870151169160018060a01b0360e088015116936101008801519560405197602089015260408801526060870152608086015260a085015260c084015260e08301526101008201526101008152806101208101106001600160401b0361012083011117610b4d5760209181610120610180930160405280518482012090610120830151858151910120906101206101408501519161099f816109876101408201958b8752610160830190613a40565b0361013f1981018484015261011f1901838301613432565b015190206101608401516040516109d2816109c48a8201948b86526040830190613a87565b03601f198101835282613432565b5190209060405192878401945f86527f000000000000000000000000000000000000000000000000000000000000000060408601526060850152608084015260a083015260c082015260c08152610a28816133aa565b519020828201527fd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd6040518481526001600160401b038351168582015260018060a01b038584015116604082015260018060a01b0360408401511660608201526001600160401b036060840151166080820152608083015160a082015260a0830151151560c08201526001600160401b0360c08401511660e082015260018060a01b0360e08401511661010082015261010083015161012082015280610120840151610b37610b21610b096101a093846101408701526101c086019061348f565b610140880151858203601f1901610160870152613a40565b610160870151848203601f190189860152613a87565b9086860151908301520390a10151604051908152f35b634e487b7160e01b5f52604160045260245ffd5b610b8981610b84610b7c60446024350160243560040161364c565b369391613956565b613966565b80516001600160a01b0390610b9f9082166136e7565b169081158015610efe575b610edb57610bbd6004602435018061361a565b91906020820151610bdf60018060a01b0384511692604051956106cb876133fc565b84526001600160401b037f000000000000000000000000000000000000000000000000000000000000000016602085015260018060a01b0388166040850152606084015260808301525f604051808094639a4575b960e01b825260206004830152610c56815160a0602485015260c484019061348f565b60208201516001600160401b0316604484015260408201516001600160a01b0390811660648501526060830151608485015260809092015190911660a4830152038183875af1918215610ed0575f92610e41575b5060208201515160208111610df1575b5092610d80610dbd829484600197610cd5610dd49751614021565b5060405192602084015260208352610cec83613417565b6020825192015190898060a01b038151165f52600c60205260ff60405f205460981c165f14610ddb57898060a01b039051165f52610da763ffffffff60405f205460501c16915b60405194610d40866133c5565b85526020850193845260408501908152606085019363ffffffff8094168552610d966040519889976020808a015251608060408a015260c089019061348f565b915191603f1992838983030160608a015261348f565b91519086830301608087015261348f565b91511660a083015203601f198101835282613432565b61016088015190610dce838361386a565b5261386a565b5001610893565b50610da763ffffffff60075460101c1691610d33565b81516001600160a01b03165f908152600c602052604090205460701c63ffffffff1610610e1e5787610cba565b51604051631b7a9b6560e11b81526001600160a01b039091166004820152602490fd5b9091503d805f833e610e538183613432565b810190602081830312610171578051906001600160401b03821161017157016040818303126101715760405191610e8983613417565b81516001600160401b0381116101715781610ea59184016139fe565b835260208201516001600160401b03811161017157610ec492016139fe565b60208201529087610caa565b6040513d5f823e3d90fd5b51604051635f8b555b60e11b81526001600160a01b039091166004820152602490fd5b506040516301ffc9a760e01b815263aff2afbf60e01b6004820152602081602481865afa908115610ed0575f91610f37575b5015610baa565b610f59915060203d602011610f5f575b610f518183613432565b81019061393e565b87610f30565b503d610f47565b6020604091610f753686613966565b81520192019161086e565b60606020828194010152018a9061078b565b6001600160a01b0384165f908152600d60205260409020805490610fbe6001600160401b0383166139e6565b67ffffffffffffffff199092166001600160401b038316179055610727565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316156106b6576001600160a01b0382165f908152600d60205260409020546001600160401b03166106b65760405163856c824760e01b81526001600160a01b038381166004830152602090829060249082907f0000000000000000000000000000000000000000000000000000000000000000165afa908115610ed0575f916110bd575b5060018060a01b0383165f52600d6020526001600160401b0360405f2091166001600160401b03198254161790556106b6565b6110df915060203d6020116110e5575b6110d78183613432565b8101906137a4565b8461108a565b503d6110cd565b60405163e5c7a49160e01b8152600490fd5b600654602090811c6001600160a01b031691606461111f6024358201613681565b60405194859384926220f2df60e11b845260018060a01b03166004840152604435602484015260448301525afa908115610ed0575f91611189575b506001600160601b0361117481600e549316828416613995565b16906001600160601b03191617600e55610676565b90506020813d6020116111b3575b816111a460209383613432565b8101031261017157518461115a565b3d9150611197565b5f805b8281106111e15750806111d2575b50610608565b6111db9061408c565b836111cc565b6020611201826111fb60446024350160243560040161364c565b90613956565b013515611380578061123761123261122360446024350160243560040161364c565b6001600160a01b039491613956565b613681565b165f52600c60205260ff60405f205460901c16611257575b6001016111be565b90611270826111fb60446024350160243560040161364c565b60065460201c6001600160a01b03169061128b903690613966565b90604060018060a01b038351166024825180948193630681320d60e51b835260048301525afa908115610ed0575f91611321575b50516001600160e01b03169081156112fe5791670de0b6b3a76400006112ef6112f69360206001960151906136a9565b04906136bc565b91905061124f565b51604051639a655f7b60e01b81526001600160a01b039091166004820152602490fd5b90506040813d604011611378575b8161133c60409383613432565b810103126101715760206040519161135383613417565b61135c81613695565b8352015163ffffffff81168103610171576020820152876112bf565b3d915061132f565b604051635cf0444960e01b8152600490fd5b604051631b3539ad60e31b81526001600160401b039091166004820152602490fd5b604051631c0a352960e01b8152600490fd5b60405163a4ec747960e01b8152600490fd5b604051630a75a23b60e31b8152600490fd5b611403915060203d602011610f5f57610f518183613432565b83610546565b34610171575f366003190112610171576020611423613892565b604051908152f35b3461017157606036600319011261017157604051606081018181106001600160401b03821117610b4d5760405260043580151581036101715781526024356001600160801b03918282168203610171576020810191825260443592808416840361017157604082019384525f54336001600160a01b0391821614159081611604575b506115f2577f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1993816060946003948554836114f163ffffffff8360801c1642613ebb565b8061157e575b5090508251169083875416848184105f14611575576115309150835b166001600160801b03166001600160801b03196003541617600355565b51151595805460ff60a01b8860a01b169060ff60a01b19161790556001600160801b0319845160801b16176004556040519485525116602084015251166040820152a1005b61153091611513565b6115a16115c7938361159a600454948286169560801c906136a9565b91166136bc565b808210156115eb5750166001600160801b03166001600160801b03196003541617600355565b855463ffffffff60801b19164260801b63ffffffff60801b161786558883816114f7565b9050611513565b6040516307b66ab160e51b8152600490fd5b905060025416331415856114ad565b34610171575f3660031901126101715760085461162f816134de565b9061163d6040519283613432565b808252601f1961164c826134de565b015f5b8181106116d75750505f5b81811061168f576116858363ffffffff600e5460601c166040519283926040845260408401906135cf565b9060208301520390f35b8061ffff61169e600193613ff3565b60405192916116ac84613417565b858060a01b031683521660208201526116c5828661386a565b526116d0818561386a565b500161165a565b6020906040516116e681613417565b5f8152825f8183015282870101520161164f565b346101715760208060031936011261017157608090611717613364565b5f6060604051611726816133c5565b8281528285820152826040820152015260018060a01b03165f52600b815260405f209060405191611756836133c5565b549063ffffffff8216928381526001600160401b0391828183018186841c16815260ff606060408601958489831c168752019660a01c1615158652604051968752511690850152511660408301525115156060820152f35b34610171575f366003190112610171575f546040516001600160a01b039091168152602090f35b346101715760203660031901126101715760206117f86117f3613364565b6137c3565b6001600160401b0360405191168152f35b34610171575f366003190112610171576001546001600160a01b038082163303611870575f54916001600160601b0360a01b9033828516175f55166001553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606490fd5b3461017157602080600319360112610171576001600160401b039060043582811161017157366023820112156101715780600401359283116101715760249060248460061b82010136811161017157611905613e36565b61190e856134de565b9461191c6040519687613432565b855260248486019201915b818310611b255750505082519260408411611b1357600e549263ffffffff93848160601c169081611aef575b5050600894929454805b611a8057506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116955f9485929091905b828410611a0057600e805463ffffffff60601b1916606089901b63ffffffff60601b1617905560408051888a1681528087018290527f8c337bff38141c507abd25c547606bdde78fe8c12e941ab613f3a565fea6cd249181906119fb9082018a6135cf565b0390a1005b9091929581611a0f888861386a565b5151169061ffff86611a218a8a61386a565b51015116918a81148015611a78575b611a615791611a5791611a51846001955f52600a8a528360405f205561439e565b50613e1e565b9601929190611996565b849060405190634de938d160e01b82526004820152fd5b508015611a30565b9294925f198101818111611adc57611a9a611ab691613ff3565b506001600160a01b03165f818152600a855260408120556142c9565b508015611ac9579294925f19018061195d565b83634e487b7160e01b5f5260116004525ffd5b84634e487b7160e01b5f5260116004525ffd5b6001600160601b03161015611b06575b8580611953565b611b0e613ae3565b611aff565b604051635ad0867d60e11b8152600490fd5b60408336031261017157846040918251611b3e81613417565b611b478661337a565b8152611b54838701613506565b83820152815201920191611927565b34610171575f366003190112610171576101a0604051611b82816133e0565b5f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e0820152610100905f8282015260ff610120925f84840152610140935f85850152610160905f828601525f6101808096015260405195611bea876133e0565b6005549160018060a01b03808416895261ffff95868560a01c1660208b015263ffffffff94858160b01c1660408c0152878160d01c1660608c015260e01c60808b01526006549187831660a08c0152878360101c1660c08c01528260201c1660e08b0152848260c01c16908a015260e01c90880152600754938416908701528260101c169085015260301c16151590820152611c896040518092613522565bf35b3461017157602036600319011261017157611ca4613364565b5f546001600160a01b039190821633141580611d02575b6115f2577f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c9160209116806001600160601b0360a01b6002541617600255604051908152a1005b508160025416331415611cbb565b34610171575f366003190112610171576002546040516001600160a01b039091168152602090f35b34610171575f3660031901126101715760206001600160601b03600e5416604051908152f35b3461017157604036600319011261017157611d77613364565b602435906001600160a01b0380831680840361017157611d95613e36565b15611e5e57611da2613892565b915f8312611e4d578116907f0000000000000000000000000000000000000000000000000000000000000000168103611ddf579161049392613ec8565b6040516370a0823160e01b815230600482015292909150602083602481855afa918215610ed0575f92611e17575b6104939350613ec8565b91506020833d602011611e45575b81611e3260209383613432565b8101031261017157610493925191611e0d565b3d9150611e25565b604051620103af60e91b8152600490fd5b60405163232cb97f60e01b8152600490fd5b34610171575f36600319011261017157611e8861377a565b5060a0604051611e97816133fc565b6003546001600160801b039081811683528163ffffffff916020850190838160801c16825260ff6040870191881c16151581526004549360608701928486168452608088019560801c8652611eea61377a565b508480855116611f17828b5116611f11611f078787511642613ebb565b858c5116906136a9565b906136bc565b80821015611f4f57505b1680985281421681526040519788525116602087015251151560408601525116606084015251166080820152f35b9050611f21565b34610171576101a036600319011261017157604051611f74816133e0565b611f7c613364565b815260243561ffff8116810361017157602082015260443563ffffffff8116810361017157604082015260643561ffff8116810361017157606082015260843563ffffffff8116810361017157608082015260a43561ffff811681036101715760a082015260c43561ffff811681036101715760c082015260e4356001600160a01b03811681036101715760e08201526101043563ffffffff811681036101715761010082015263ffffffff6101243516610124350361017157610124356101208201526101443561ffff81168103610171576101408201526101643563ffffffff811681036101715761016082015261018435801515810361017157610180820152612087613e6a565b60e08101516001600160a01b03161561239e576102a07f45b5ad483aa608464c2c7f278bd413d284d7790cdc836e40652e23a0277082209160018060a01b0381511661ffff60a01b602083015160a01b1663ffffffff60b01b604084015160b01b1661ffff60d01b606085015160d01b169063ffffffff60e01b9384608087015160e01b16931717171760055561ffff60a0830151169060c083015191640100000000600160c01b0360e085015160201b169063ffff000063ffffffff60c01b61010087015160c01b169361012087015160e01b169460101b161717171760065561ffff610140820151166007549065ffffffff000061016084015160101b169066ff000000000000610180850151151560301b169266ffffffffffffff191617171760075561239b604051916121bd8361338e565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811684526001600160401b037f0000000000000000000000000000000000000000000000000000000000000000811660208601527f000000000000000000000000000000000000000000000000000000000000000081166040808701919091527f000000000000000000000000000000000000000000000000000000000000000090911660608601526001600160601b037f00000000000000000000000000000000000000000000000000000000000000001660808601527f0000000000000000000000000000000000000000000000000000000000000000821660a08601527f0000000000000000000000000000000000000000000000000000000000000000821660c08601527f000000000000000000000000000000000000000000000000000000000000000090911660e0850152519261239090849060e0908160018060a01b039182815116855260208101516001600160401b03809116602087015280604083015116604087015260608201511660608601526001600160601b0360808201511660808601528260a08201511660a08601528260c08201511660c0860152015116910152565b610100830190613522565ba1005b6040516306b7c75960e31b8152600490fd5b34610171576040366003190112610171576123c96134b4565b506024356001600160a01b038082168203610171576123e96020926136e7565b60405191168152f35b34610171576040366003190112610171576001600160401b03600435818111610171573660238201121561017157806004013590602492612432836134de565b906124406040519283613432565b838252602092838301908660e083970282010190368211610171578701915b81831061279857505050843590811161017157366023820112156101715780600401359061248c826134de565b9061249a6040519283613432565b82825284820187819460051b830101913683116101715788879101915b83831061278057505050506124ca613e36565b5f5b835181101561261d576124df818561386a565b5160a0908181019063ffffffff808351168981106125f2575090816001959493928a830151169381604084015116936060928c69ffff000000000000000067ffffffff0000000061ffff87890151169360808180828c0151169751169760c091828c015115159a6040519e8f90612555826133aa565b81528881019e8f5260408101998a52019889528d019889528c019889528b01988d8a528d8060a01b039051165f52600c845260405f209a5116928a549951901b16925160401b169263ffffffff60501b905160501b169363ffffffff60701b905160701b169460ff60901b9051151560901b169560ff60981b9051151560981b16966001600160601b0360a01b16171717171717179055016124cc565b82516040516312766e0160e11b81526001600160a01b0390911660048201528c810191909152604490fd5b509190936040519184830190858452518091526040830191905f5b86828210612710575050505090807ff5791bc457b3bb990493cf5f655db46c25ccf5764c9b99b8969b4c72ea7df9d0920390a15f5b815181101561269f576001906001600160a01b0361268b828561386a565b51165f52600c84525f60408120550161266d565b5080516126a857005b906040519181830190828452518091526040830193915f5b8281106126f35750505050807ffb95a0042158e60a33e7b5bec100f3d95407b1a71bee6633bd54b8887449750b920390a1005b83516001600160a01b0316865294810194928101926001016126c0565b835180516001600160a01b031686528181015163ffffffff9081168388015260408083015182169088015260608083015161ffff169088015260808083015182169088015260a0808301519091169087015260c09081015115159086015260e09094019390920191600101612638565b819061278b8461337a565b81520191019086906124b7565b60e083360312610171578560e0916040516127b2816133aa565b6127bb8661337a565b81526127c88387016134f5565b838201526127d8604087016134f5565b604082015260606127ea818801613506565b9082015260806127fb8188016134f5565b9082015260a061280c8188016134f5565b9082015260c061281d818801613515565b9082015281520192019161245f565b34610171575f366003190112610171576001600160401b03600181600e5460801c16018181116128625760209160405191168152f35b634e487b7160e01b5f52601160045260245ffd5b3461017157600319604036820112610171576128906134b4565b906001600160401b03602435116101715760a09060243536030112610171576001600160401b037f0000000000000000000000000000000000000000000000000000000000000000166001600160401b03821603611392576128ff6105bd60846024350160243560040161361a565b9061293e612916602480350160243560040161361a565b9050835161292e60446024350160243560040161364c565b9190506020860151151592613d85565b6001600160a01b03612954602435606401613681565b165f52600b60205260405f2060ff6040519161296f836133c5565b5463ffffffff811683526001600160401b038160201c1660208401526001600160401b038160601c16604084015260a01c161580156060830152612fbc57600654906001600160401b036129c7606460243501613681565b6040805160016224b4c960e01b031981526001600160a01b03928316600482015295909216602486015284906044908290602087901c165afa8015610ed0575f935f91612f75575b505f80612a2660446024350160243560040161364c565b159050612f4b57505093612a3e606460243501613681565b94612a5360446024350160243560040161364c565b9290965f975f945f925f925b828410612bfc57505050509296925b5f9561ffff8160101c16612b2d575b505092612b176114239695936001600160401b036040612b0d612b01612b17976001600160701b03612afa60209f63ffffffff612af2612b1c9f51611f11612ace60055492858460b01c16906136bc565b9161ffff612ae5602480350160243560040161361a565b92905060d01c16906136a9565b9116906136bc565b91166136a9565b838d88015116906136a9565b94015116906136a9565b6136bc565b6001600160e01b03909116906136c9565b909193969550612b46602480350160243560040161361a565b9050612b5c60446024350160243560040161364c565b905061022082810181116128625761018092828481020484148315171561286257612bd29563ffffffff612af2612baa94612ba194612bcd9861ffff980291016136bc565b828516906136a9565b9260101c1691612bcd6001600160701b038660701c169160055460e01c906136bc565b6136a9565b92655af3107a40009384810294818604149015171561286257612b17611423969391949596612a7d565b9091929a612c138c610b8499969936918786613956565b80519091906001600160a01b0390612c2c9082166136e7565b1615612f275760018060a01b038251165f52600c60205260405f209560ff60405197612c57896133aa565b5463ffffffff8116895263ffffffff8160201c1660208a015261ffff8160401c1660408a015263ffffffff8160501c1660608a015263ffffffff8160701c1660808a0152818160901c16151560a08a015260981c1615801560c0890152612ec6575f9261ffff604089015116612d9e575b50612ce1612cf39163ffffffff60608a01511690613e1e565b9963ffffffff60808901511690613e1e565b9563ffffffff81511680662386f26fc10000810204662386f26fc10000148115171561286257662386f26fc1000081028410612d855750602063ffffffff9101511691662386f26fc10000830292808404662386f26fc10000149015171561286257826001938211612d745750612d69916136bc565b9b5b01929190612a5f565b9050612d7f916136bc565b9b612d6b565b60019350612d7f929150662386f26fc1000002906136bc565b8051919350908c906001600160a01b03908116908f9088168214612e9f5750604051634ab35b0b60e01b815260048101919091529060209082906024908290841c6001600160a01b03165afa908115610ed0575f91612e42575b50620186a0612e39670de0b6b3a7640000612e29612cf3956020612ce196915b0151906001600160e01b03166136a9565b0461ffff60408c015116906136a9565b04939150612cc8565b90506020813d602011612e97575b81612e5d60209383613432565b8101031261017157620186a0612e39670de0b6b3a7640000612e29612cf3956020612e8a612ce197613695565b9650509550505050612df8565b3d9150612e50565b612ce19250620186a09150670de0b6b3a7640000612e29612cf3956020612e399491612e18565b91509b94506007549b61ffff8d1690662386f26fc100009282848102048414831517156128625763ffffffff612f05612f1093602096839602906136bc565b9f60101c1690613e1e565b98160163ffffffff81116128625760019094612d6b565b8151604051635f8b555b60e11b81526001600160a01b039091166004820152602490fd5b91909563ffffffff845116662386f26fc10000908181029181830414901517156128625792612a6e565b9350506040833d604011612fb4575b81612f9160409383613432565b8101031261017157612fae6020612fa785613695565b9401613695565b85612a0f565b3d9150612f84565b6024612fcb6064823501613681565b60405163053a4ce960e51b81526001600160a01b039091166004820152fd5b34610171575f3660031901126101715761303d60405161300981613417565b6013815272045564d3245564d4f6e52616d7020312e352e3606c1b602082015260405191829160208352602083019061348f565b0390f35b34610171576020806003193601126101715760e09061305e613364565b5f60c060405161306d816133aa565b82815282858201528260408201528260608201528260808201528260a0820152015260018060a01b03165f52600c815260405f20604051906130ae826133aa565b54809163ffffffff908180841695868352808301908286821c16825261ffff906040850190828860401c168252846060870194818a60501c16865260ff60c060808a0199848d60701c168b528260a082019d60901c1615158d52019b60981c1615158b526040519b8c525116908a015251166040880152511660608601525116608084015251151560a083015251151560c0820152f35b34610171575f3660031901126101715760e0816131625f9361338e565b8281528260208201528260408201528260608201528260808201528260a08201528260c0820152015261010060405161319a8161338e565b60018060a01b03807f00000000000000000000000000000000000000000000000000000000000000001682526001600160401b03807f0000000000000000000000000000000000000000000000000000000000000000166020840152807f00000000000000000000000000000000000000000000000000000000000000001660408401527f00000000000000000000000000000000000000000000000000000000000000001660608301526001600160601b037f0000000000000000000000000000000000000000000000000000000000000000166080830152807f00000000000000000000000000000000000000000000000000000000000000001660a0830152807f00000000000000000000000000000000000000000000000000000000000000001660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e0820152611c89604051809260e0908160018060a01b039182815116855260208101516001600160401b03809116602087015280604083015116604087015260608201511660608601526001600160601b0360808201511660808601528260a08201511660a08601528260c08201511660c0860152015116910152565b600435906001600160a01b038216820361017157565b35906001600160a01b038216820361017157565b61010081019081106001600160401b03821117610b4d57604052565b60e081019081106001600160401b03821117610b4d57604052565b608081019081106001600160401b03821117610b4d57604052565b6101a081019081106001600160401b03821117610b4d57604052565b60a081019081106001600160401b03821117610b4d57604052565b604081019081106001600160401b03821117610b4d57604052565b90601f801991011681019081106001600160401b03821117610b4d57604052565b6001600160401b038111610b4d57601f01601f191660200190565b5f5b83811061347f5750505f910152565b8181015183820152602001613470565b906020916134a88151809281855285808601910161346e565b601f01601f1916010190565b600435906001600160401b038216820361017157565b35906001600160401b038216820361017157565b6001600160401b038111610b4d5760051b60200190565b359063ffffffff8216820361017157565b359061ffff8216820361017157565b3590811515820361017157565b60018060a01b03808251168352602082015161ffff809116602085015260408301519163ffffffff80931660408601528160608501511660608601528260808501511660808601528160a08501511660a08601528160c08501511660c086015260e08401511660e085015261010082818501511690850152610120828185015116908501526101409081840151169084015261016090818301511690830152610180809101511515910152565b9081518082526020808093019301915f5b8281106135ee575050505090565b835180516001600160a01b0316865282015161ffff1685830152604090940193928101926001016135e0565b903590601e198136030182121561017157018035906001600160401b0382116101715760200191813603831361017157565b903590601e198136030182121561017157018035906001600160401b03821161017157602001918160061b3603831361017157565b356001600160a01b03811681036101715790565b51906001600160e01b038216820361017157565b8181029291811591840414171561286257565b9190820180921161286257565b81156136d3570490565b634e487b7160e01b5f52601260045260245ffd5b60405163bbe4f6db60e01b81526001600160a01b0391821660048201526020816024817f000000000000000000000000000000000000000000000000000000000000000086165afa908115610ed0575f9161374157501690565b90506020813d602011613772575b8161375c60209383613432565b8101031261017157518181168103610171571690565b3d915061374f565b60405190613787826133fc565b5f6080838281528260208201528260408201528260608201520152565b9081602091031261017157516001600160401b03811681036101715790565b6001600160a01b039081165f818152600d60205260409020546001600160401b0316917f000000000000000000000000000000000000000000000000000000000000000016908161381357505090565b821561381e57505090565b602091925060246040518094819363856c824760e01b835260048301525afa908115610ed0575f9161384e575090565b613867915060203d6020116110e5576110d78183613432565b90565b805182101561387e5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b6040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610ed0575f9161390c575b506001600160601b03600e5416905f82820392128183128116918313901516176128625790565b90506020813d602011613936575b8161392760209383613432565b8101031261017157515f6138e5565b3d915061391a565b90816020910312610171575180151581036101715790565b919081101561387e5760061b0190565b91908260409103126101715760405161397e81613417565b602080829461398c8161337a565b84520135910152565b9190916001600160601b038080941691160191821161286257565b9291926139bc82613453565b916139ca6040519384613432565b829481845281830111610171578281602093845f960137010152565b6001600160401b038091169081146128625760010190565b81601f82011215610171578051613a1481613453565b92613a226040519485613432565b8184526020828401011161017157613867916020808501910161346e565b9081518082526020808093019301915f5b828110613a5f575050505090565b835180516001600160a01b031686528201518583015260409094019392810192600101613a51565b90808251908181526020809101926020808460051b8301019501935f915b848310613ab55750505050505090565b9091929394958480613ad3600193601f198682030187528a5161348f565b9801930193019194939290613aa5565b5f546001600160a01b039081163303613c25575b600e549063ffffffff8260601c168015613c13576001600160601b0380931690808210613c01575f613b27613892565b12613bef57918192600854915f9186827f000000000000000000000000000000000000000000000000000000000000000016935b858110613b7d5750505050505050166001600160601b0319600e541617600e55565b613b989782613b9d85613b8f85613ff3565b8c9d919d6136a9565b6136c9565b16928391160391898311612862577f55fdec2aab60a41fa5abb106670eb1006f5aeaee1ba7afea2bc89b5b3ec7678f6020866001959b613bde85828c613ec8565b6040519485521692a2018790613b5b565b604051631e9acf1760e31b8152600490fd5b6040516311a1ee3b60e31b8152600490fd5b60405163990e30bf60e01b8152600490fd5b8060025416330315613af757335f52600960205260405f2054613af75760405163032bb72b60e31b8152600490fd5b60405f60208251613c6481613417565b82815201528215613d43576004926001600160e01b03198335818116908290878510613d2f575b505016630181dcf160e41b8103613cd8575080841161017157820182900360031901811361017157613cd0916024915193613cc585613417565b810135845201613515565b602082015290565b6397a657c960e096949395961b14613cfa5750505051632923fee760e11b8152fd5b80859395949294116101715760209084018490036003190112610171575191613d2283613417565b013581525f602082015290565b9080925084880360031b1b1616815f613c8b565b91505051613d5081613417565b6001600160401b037f00000000000000000000000000000000000000000000000000000000000000001681525f602082015290565b6006549063ffffffff8260c01c1690818111613e0057505060e01c10613dee5761ffff60055460a01c1610613ddc5715613dbb57565b60ff60075460301c16613dca57565b60405163ee433e9960e01b8152600490fd5b604051632602b5b560e11b8152600490fd5b604051632627e49d60e11b8152600490fd5b6044925060405191638693378960e01b835260048301526024820152fd5b91909163ffffffff8080941691160191821161286257565b5f546001600160a01b039081163303613e4c5750565b600254163303613e5857565b604051637dedc72b60e11b8152600490fd5b5f546001600160a01b03163303613e7d57565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b9190820391821161286257565b9190613f6f915f80604051936020968786019463a9059cbb60e01b865260018060a01b038093166024880152604487015260448652613f06866133c5565b169260405194613f1586613417565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af13d15613feb573d91613f5483613453565b92613f626040519485613432565b83523d5f8785013e614230565b80519081613f7c57505050565b8280613f8c93830101910161393e565b15613f945750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b606091614230565b613ffc906141fb565b905460039190911b1c5f818152600a60205260409020546001600160a01b0390911691565b6020815103614058576020818051810103126101715760208101516001600160a01b03918282118015614081575b61405857501690565b60405163046b337b60e51b81526020600482015290819061407d90602483019061348f565b0390fd5b50610400821061404f565b60035460ff8160a01c161580156141f3575b6141ef576001600160801b03908181169060045490838216906140ca63ffffffff8260801c1642613ebb565b8061418f575b5050848110614171575083821061412c575091602091614111827f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a95613ebb565b166001600160801b03196003541617600355604051908152a1565b60801c9061413a8185613ebb565b915f1981019080821161286257613b98614156926044956136bc565b90604051916302a4f38160e31b835260048301526024820152fd5b604490856040519163f94ebcd160e01b835260048301526024820152fd5b90938281116141dd57611f116141a9928560801c906136a9565b808210156141d85750805b63ffffffff60801b199093164260801b63ffffffff60801b16176003555f806140d0565b6141b4565b604051634b92ca1560e11b8152600490fd5b5050565b50811561409e565b60085481101561387e5760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee301905f90565b919290156142925750815115614244575090565b3b1561424d5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156142a55750805190602001fd5b60405162461bcd60e51b81526020600482015290819061407d90602483019061348f565b5f818152600960205260409020548015614398575f199080820181811161286257600854908382019182116128625781810361434e575b505050600854801561433a57810190614318826141fb565b909182549160031b1b191690556008555f5260096020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b61438261435d61436c936141fb565b90549060031b1c9283926141fb565b819391549060031b91821b915f19901b19161790565b90555f52600960205260405f20555f8080614300565b50505f90565b805f52600960205260405f2054155f146143f15760085468010000000000000000811015610b4d576143da61436c8260018594016008556141fb565b9055600854905f52600960205260405f2055600190565b505f9056fea2646970667358221220dfda2baf02b16e43c5cb3e636720a48099e3c7fe623e4377be8e74214836d13464736f6c63430008180033" + } + }, + "version": "0.8.24+commit.e11b9ed9" +} \ No newline at end of file diff --git a/packages/ccip-js/artifacts-compile/PriceRegistry.json b/packages/ccip-js/artifacts-compile/PriceRegistry.json new file mode 100644 index 0000000..35d496c --- /dev/null +++ b/packages/ccip-js/artifacts-compile/PriceRegistry.json @@ -0,0 +1,605 @@ +{ + "contracts": { + "src/contracts/PriceRegistry.sol:PriceRegistry": { + "priceRegistryAbi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chain", + "type": "uint64" + } + ], + "name": "ChainNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidStalenessThreshold", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "NotAFeeToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByUpdaterOrOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timePassed", + "type": "uint256" + } + ], + "name": "StaleGasPrice", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timePassed", + "type": "uint256" + } + ], + "name": "StaleTokenPrice", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "TokenNotSupported", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "feeToken", + "type": "address" + } + ], + "name": "FeeTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "feeToken", + "type": "address" + } + ], + "name": "FeeTokenRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "priceUpdater", + "type": "address" + } + ], + "name": "PriceUpdaterRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "priceUpdater", + "type": "address" + } + ], + "name": "PriceUpdaterSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "UsdPerTokenUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "destChain", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "UsdPerUnitGasUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "feeTokensToAdd", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "feeTokensToRemove", + "type": "address[]" + } + ], + "name": "applyFeeTokensUpdates", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "priceUpdatersToAdd", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "priceUpdatersToRemove", + "type": "address[]" + } + ], + "name": "applyPriceUpdatersUpdates", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "fromToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fromTokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "toToken", + "type": "address" + } + ], + "name": "convertTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "fTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "getDestinationChainGasPrice", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "feeTokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPriceUpdaters", + "outputs": [ + { + "internalType": "address[]", + "name": "priceUpdaters", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStalenessThreshold", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "getTokenAndGasPrices", + "outputs": [ + { + "internalType": "uint224", + "name": "feeTokenPrice", + "type": "uint224" + }, + { + "internalType": "uint224", + "name": "gasPriceValue", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenPrice", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "getTokenPrices", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getValidatedTokenPrice", + "outputs": [ + { + "internalType": "uint224", + "name": "", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "uint224", + "name": "usdPerToken", + "type": "uint224" + } + ], + "internalType": "struct Internal.TokenPriceUpdate[]", + "name": "tokenPriceUpdates", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint224", + "name": "usdPerUnitGas", + "type": "uint224" + } + ], + "internalType": "struct Internal.GasPriceUpdate[]", + "name": "gasPriceUpdates", + "type": "tuple[]" + } + ], + "internalType": "struct Internal.PriceUpdates", + "name": "priceUpdates", + "type": "tuple" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "updaters", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "priceRegistryBin": "60a06040908082523462000704573315620006c257505f80546001600160a01b0319163317905580516001600160401b03602080830182811184821017620006ae578452338352600480549360019485835580861062000684575b50815f52825f2090855f5b818110620006685750505050845160608101818110858211176200065557865273779877a7b0d9e8603169ddbd7836e478b4624789815273097d90c9d3e0b50ca60e1ae45f6a81010f9fb5348382015273c4bf5cbdabe595361438f8c6a187bdc330539c608682015260059384549160039260038755806003106200062a575b50855f52845f2090875f5b8581106200060e5750505050906203f480608052865190620001128262000724565b5f8252875191620001238362000724565b5f8352885190620001348262000708565b80825286820193845251934263ffffffff1691895f5b8c8882106200045b57828c8c8c83855191620001668362000724565b5f8352865190818582549182815201915f52855f20905f5b878282106200043e5750505050816200019991038262000740565b5f825b620003c3575b505090815f905b62000348575b505090845192620001c08462000724565b5f8452855191828183549182815201925f52815f20915f905b82821062000326575050505081620001f391038262000740565b5f825b620002ab575b5050905f915b6200022e575b83516113cb908162000a1d82396080518181816101710152818161042301526110b50152f35b8051821015620002a557829182906001600160a01b036200025d8162000255848762000764565b51166200096a565b6200026c575b50019162000202565b62000278828562000764565b51167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f915f80a28662000263565b62000208565b8151811015620003205782906001600160a01b03620002d881620002d0848762000764565b511662000832565b620002e7575b500182620001f6565b620002f3828562000764565b51167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba235f80a287620002de565b620001fc565b83546001600160a01b03168552889694810194938401939190910190620001d9565b8151811015620003bd5782906001600160a01b0362000375816200036d848762000764565b51166200088a565b62000384575b500182620001a9565b62000390828562000764565b51167fff7dbb85c77ca68ca1f894d6498570e3d5095cd19466f07ee8d222b337e4068c5f80a2886200037b565b620001af565b8151811015620004385782906001600160a01b03620003f081620003e8848762000764565b5116620007bf565b620003ff575b5001826200019c565b6200040b828562000764565b51167f34a02290b7920078c19f58e94b78c77eb9cc10195b20676e19bd3b82085893b85f80a289620003f6565b620001a2565b83546001600160a01b031685528a9694019392830192016200017e565b8588916200046b84875162000764565b51908d808301908b60018060e01b03927f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a6200051d858084511694885195620004b48762000708565b86528686018b81528a516001600160a01b039081165f908152928952918a90209651905160e090811b6001600160e01b0319908116928516929092179097559951945189516001600160e01b039190931616825242602083015293909316929081906040820190565b0390a28a6200052e89895162000764565b51511662000547575b50505050505050018a906200014a565b7fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e95620005fe9584846200057d8c8c5162000764565b5101511692865193620005908562000708565b84528484019283528d620005a68c8c5162000764565b5151165f526002855285875f209451169251901b16179055620005dc878b620005d1828a5162000764565b515116975162000764565b510151915191166001600160e01b031681524260208201529081906040820190565b0390a286855f8f818e8162000537565b82516001600160a01b03168482015591870191899101620000f0565b865f52876003875f2092830192015b82811062000649575050620000e5565b5f815501889062000639565b604183634e487b7160e01b5f525260245ffd5b82516001600160a01b0316848201559185019187910162000065565b825f528580855f2092830192015b828110620006a25750506200005a565b5f815501869062000692565b634e487b7160e01b5f52604160045260245ffd5b62461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152606490fd5b5f80fd5b604081019081106001600160401b03821117620006ae57604052565b602081019081106001600160401b03821117620006ae57604052565b601f909101601f19168101906001600160401b03821190821017620006ae57604052565b8051821015620007795760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b600654811015620007795760065f5260205f2001905f90565b600854811015620007795760085f5260205f2001905f90565b805f52600760205260405f2054155f146200082d5760065468010000000000000000811015620006ae5762000816620008008260018594016006556200078d565b819391549060031b91821b915f19901b19161790565b9055600654905f52600760205260405f2055600190565b505f90565b805f52600960205260405f2054155f146200082d5760085468010000000000000000811015620006ae576200087362000800826001859401600855620007a6565b9055600854905f52600960205260405f2055600190565b5f81815260076020526040902054801562000964575f1990808201818111620009505760065490838201918211620009505780820362000916575b50505060065480156200090257810190620008e0826200078d565b909182549160031b1b191690556006555f5260076020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b620009396200092962000800936200078d565b90549060031b1c9283926200078d565b90555f52600760205260405f20555f8080620008c5565b634e487b7160e01b5f52601160045260245ffd5b50505f90565b5f81815260096020526040902054801562000964575f19908082018181116200095057600854908382019182116200095057808203620009e2575b50505060085480156200090257810190620009c082620007a6565b909182549160031b1b191690556008555f5260096020525f6040812055600190565b62000a05620009f56200080093620007a6565b90549060031b1c928392620007a6565b90555f52600960205260405f20555f8080620009a556fe604060808152600480361015610013575f80fd5b60e05f3560e01c90816241e5be14610cfd5781632543b61e14610ca05781633937306f146109655750806345ac924d146108075780634ab35b0b146107d1578063514e8cff1461075857806352877af01461066657806379ba5097146105b85780637afac322146104c457806382cb62291461046e5780638da5cb5b14610447578063a6c94a7314610407578063bfcd45661461038b578063cdc73d511461030b578063d02641a0146102c5578063f2fde38b146102085763ffdb4b37146100d9575f80fd5b346102045781600319360112610204576100f1610d98565b6024359067ffffffffffffffff8216809203610204576001600160a01b0381165f818152600960205285902054156101ee5750815f526002602052835f2092610138610e40565b93546001600160e01b03808216865260e09190911c60208601818152919490156101d85761016e63ffffffff8093511642611032565b917f000000000000000000000000000000000000000000000000000000000000000016908183116101b857505050506101a78291611053565b925116908351921682526020820152f35b8751637845e59f60e11b8152938401526024830152604482015260649150fd5b8260249188519163172ced9d60e11b8352820152fd5b8360249186519163053a4ce960e51b8352820152fd5b5f80fd5b503461020457602036600319011261020457610222610d98565b9061022b611129565b6001600160a01b0391821692338414610282575050816bffffffffffffffffffffffff60a01b60015416176001555f54167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12785f80a3005b906020606492519162461bcd60e51b8352820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b8234610204576020366003190112610204576102e76102e2610d98565b610feb565b815181516001600160e01b0316815260209182015163ffffffff1691810191909152f35b8234610204575f36600319011261020457600880549161032a83610fc3565b915f5b848110610345578251806103418682610f6b565b0390f35b5f8290527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3810154600191906001600160a01b03166103848287610faf565b520161032d565b8234610204575f3660031901126102045760068054916103aa83610fc3565b915f5b8481106103c1578251806103418682610f6b565b5f8290527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f810154600191906001600160a01b03166104008287610faf565b52016103ad565b8234610204575f366003190112610204576020905163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b8234610204575f366003190112610204575f5490516001600160a01b039091168152602090f35b5090346102045760203660031901126102045781358254811015610204576020925f5260018060a01b03907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0154169051908152f35b34610204576104d236610f24565b906104db611129565b5f5b8151811015610548576001906001600160a01b03610506816104ff8487610faf565b51166111e3565b610512575b50016104dd565b61051c8285610faf565b51167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba235f80a28461050b565b825f5b81518110156105b6576001906001600160a01b036105748161056d8487610faf565b51166112f0565b610580575b500161054b565b61058a8285610faf565b51167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f915f80a283610579565b005b509034610204575f36600319011261020457600154916001600160a01b0391828416330361062a5750505f54916bffffffffffffffffffffffff60a01b9033828516175f55166001553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b906020606492519162461bcd60e51b8352820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152fd5b346102045761067436610f24565b9061067d611129565b5f5b81518110156106ea576001906001600160a01b036106a8816106a18487610faf565b511661117a565b6106b4575b500161067f565b6106be8285610faf565b51167f34a02290b7920078c19f58e94b78c77eb9cc10195b20676e19bd3b82085893b85f80a2846106ad565b825f5b81518110156105b6576001906001600160a01b036107168161070f8487610faf565b5116611231565b610722575b50016106ed565b61072c8285610faf565b51167fff7dbb85c77ca68ca1f894d6498570e3d5095cd19466f07ee8d222b337e4068c5f80a28361071b565b5034610204576020366003190112610204573567ffffffffffffffff8116809103610204575f6020610788610e40565b82815201525f526002602052805f2061079f610e40565b90546001600160e01b03811680835260e09190911c602092830190815283519182525163ffffffff1691810191909152f35b8234610204576020366003190112610204576020906107f66107f1610d98565b611053565b90516001600160e01b039091168152f35b503461020457602091826003193601126102045781359167ffffffffffffffff908184116102045736602385011215610204578301359081116102045760246005933660248460051b830101116102045761086d6108688497969597610e9a565b610e74565b9383855261087a84610e9a565b601f19015f5b8181106109405750505f5b8481106108f857875187815286518189018190528190818b0190898b01908b5f8e5b8382106108ba5786860387f35b9184965082866108e8600194969884985163ffffffff6020809260018060e01b038151168552015116910152565b01960192018695949293916108ad565b9596949580821b8301840135906001600160a01b038216820361020457610920600192610feb565b61092a828a610faf565b526109358189610faf565b50019695949661088b565b968098969761094d610e40565b5f81525f8382015282828b0101520197969597610880565b9050346102045760031990602036830181136102045783359467ffffffffffffffff938487116102045781908736030112610204576109a2610e40565b9580860135858111610204578101366023820112156102045786810135906109cc61086883610e9a565b9160248684838152019160061b8301019136831161020457602401905b828210610c6757505050875260248101359085821161020457019336602386011215610204578585013594610a2061086887610e9a565b9560248588838152019160061b8301019136831161020457602401905b828210610c29575050508287019485525f546001600160a01b039690871633141580610c17575b610c095750865151954263ffffffff1693905f5b888110610a8157005b8088868285878f848e610a998f939a60019b51610faf565b517f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a610b29868301928d8060e01b0398899182865116610ad7610e40565b908152838b8201998b8b52848451165f5260038d525f2091511663ffffffff60e01b809a518a1b1617905551169351168a5191829142908360209093929193604081019460018060e01b031681520152565b0390a28c80610b39888b51610faf565b515116610b50575b50505050505050505001610a78565b7fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e976002868893610ba88b868f9b610b8c610bf89f8e51610faf565b510151169a610b99610e40565b9b8c52848c019a8b5251610faf565b5151165f52525f209451169251901b1617905587610bd6868b610bcc828951610faf565b5151169651610faf565b5101518a5191166001600160e01b031681524260208201529081906040820190565b0390a28886825f878b828e8c610b41565b82516311bc205560e21b8152fd5b50335f5260078452825f205415610a64565b848236031261020457610c3a610e40565b90823590858216820361020457828892889452610c58838601610eb2565b83820152815201910190610a3d565b858236031261020457868691610c7b610e40565b610c8485610dae565b8152610c91838601610eb2565b838201528152019101906109e9565b5050346102045760203660031901126102045735906005548210156102045760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db09091015490516001600160a01b03919091168152602090f35b82843461020457606036600319011261020457610d18610d98565b60243591906044356001600160a01b0381168103610204576001600160e01b03918290610d4490611053565b1693848102948186041490151715610d8557610d5f90611053565b16908115610d7257602093505191048152f35b601284634e487b7160e01b5f525260245ffd5b601185634e487b7160e01b5f525260245ffd5b600435906001600160a01b038216820361020457565b35906001600160a01b038216820361020457565b600654811015610df75760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01905f90565b634e487b7160e01b5f52603260045260245ffd5b600854811015610df75760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee301905f90565b604051906040820182811067ffffffffffffffff821117610e6057604052565b634e487b7160e01b5f52604160045260245ffd5b6040519190601f01601f1916820167ffffffffffffffff811183821017610e6057604052565b67ffffffffffffffff8111610e605760051b60200190565b35906001600160e01b038216820361020457565b9080601f83011215610204576020908235610ee361086882610e9a565b9360208086848152019260051b82010192831161020457602001905b828210610f0d575050505090565b838091610f1984610dae565b815201910190610eff565b9060406003198301126102045767ffffffffffffffff6004358181116102045783610f5191600401610ec6565b9260243591821161020457610f6891600401610ec6565b90565b60209060206040818301928281528551809452019301915f5b828110610f92575050505090565b83516001600160a01b031685529381019392810192600101610f84565b8051821015610df75760209160051b010190565b90610fd061086883610e9a565b8281528092610fe1601f1991610e9a565b0190602036910137565b5f6020610ff6610e40565b82815201526001600160a01b03165f908152600360205260409020611019610e40565b90546001600160e01b038116825260e01c602082015290565b9190820391821161103f57565b634e487b7160e01b5f52601160045260245ffd5b6001600160a01b03165f818152600360205260409020611071610e40565b90546001600160e01b0380821680845260e09290921c602084018181529194929015908115611120575b50611107576110b263ffffffff8092511642611032565b907f0000000000000000000000000000000000000000000000000000000000000000168082116110e457505050511690565b606493506040519263632fefe560e11b8452600484015260248301526044820152fd5b6040516306439c6b60e01b815260048101839052602490fd5b9050155f61109b565b5f546001600160a01b0316330361113c57565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b805f52600760205260405f2054155f146111de57600654600160401b811015610e60576111c76111b1826001859401600655610dc2565b819391549060031b91821b915f19901b19161790565b9055600654905f52600760205260405f2055600190565b505f90565b805f52600960205260405f2054155f146111de57600854600160401b811015610e605761121a6111b1826001859401600855610e0b565b9055600854905f52600960205260405f2055600190565b5f8181526007602052604090205480156112ea575f199080820181811161103f576006549083820191821161103f578082036112b6575b50505060065480156112a25781019061128082610dc2565b909182549160031b1b191690556006555f5260076020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6112d46112c56111b193610dc2565b90549060031b1c928392610dc2565b90555f52600760205260405f20555f8080611268565b50505f90565b5f8181526009602052604090205480156112ea575f199080820181811161103f576008549083820191821161103f57808203611361575b50505060085480156112a25781019061133f82610e0b565b909182549160031b1b191690556008555f5260096020525f6040812055600190565b61137f6113706111b193610e0b565b90549060031b1c928392610e0b565b90555f52600960205260405f20555f808061132756fea2646970667358221220dcee150bbaf62bf4038b6c8a7105043b5f3f893c33478b0db781a4da0bbcfffe64736f6c63430008180033" + } + }, + "version": "0.8.24+commit.e11b9ed9" +} \ No newline at end of file diff --git a/packages/ccip-js/artifacts-compile/Router.json b/packages/ccip-js/artifacts-compile/Router.json new file mode 100644 index 0000000..98d1431 --- /dev/null +++ b/packages/ccip-js/artifacts-compile/Router.json @@ -0,0 +1,710 @@ +{ + "contracts": { + "src/contracts/Router.sol:Router": { + "routerAbi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "wrappedNative", + "type": "address" + }, + { + "internalType": "address", + "name": "armProxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "BadARMSignal", + "type": "error" + }, + { + "inputs": [], + "name": "FailedToSendValue", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFeeTokenAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidMsgValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "InvalidRecipientAddress", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "name": "OffRampMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyOffRamp", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "UnsupportedDestinationChain", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "messageId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "offRamp", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "calldataHash", + "type": "bytes32" + } + ], + "name": "MessageExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "name": "OffRampAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "name": "OffRampRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "onRamp", + "type": "address" + } + ], + "name": "OnRampSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_RET_BYTES", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "onRamp", + "type": "address" + } + ], + "internalType": "struct Router.OnRamp[]", + "name": "onRampUpdates", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "internalType": "struct Router.OffRamp[]", + "name": "offRampRemoves", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "internalType": "struct Router.OffRamp[]", + "name": "offRampAdds", + "type": "tuple[]" + } + ], + "name": "applyRampUpdates", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destinationChainSelector", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "receiver", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "extraArgs", + "type": "bytes" + } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "ccipSend", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getArmProxy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destinationChainSelector", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "receiver", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "extraArgs", + "type": "bytes" + } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "getFee", + "outputs": [ + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOffRamps", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "internalType": "struct Router.OffRamp[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "getOnRamp", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "getSupportedTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getWrappedNative", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "isChainSupported", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "address", + "name": "offRamp", + "type": "address" + } + ], + "name": "isOffRamp", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "recoverTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "messageId", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "sourceChainSelector", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "sender", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "destTokenAmounts", + "type": "tuple[]" + } + ], + "internalType": "struct Client.Any2EVMMessage", + "name": "message", + "type": "tuple" + }, + { + "internalType": "uint16", + "name": "gasForCallExactCheck", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "routeMessage", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "retData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "gasUsed", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wrappedNative", + "type": "address" + } + ], + "name": "setWrappedNative", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "routerBin": "60a034620000f357601f62001f7038819003918201601f19168301916001600160401b03831184841017620000f7578084926040948552833981010312620000f3576200005a602062000052836200010b565b92016200010b565b903315620000ae5760018060a01b03199033825f5416175f5560018060a01b0316906002541617600255608052604051611e4f908162000121823960805181818161065a0152818161088701526114ca0152f35b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152606490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620000f35756fe60806040526004361015610011575f80fd5b5f3560e01c8063181f5a771461013457806320487ded1461012f5780633cf979831461012a5780635246492f1461012557806352cb60ca146101205780635f3e849f1461011b578063787350e31461011657806379ba50971461011157806383826b2b1461010c5780638da5cb5b1461010757806396f4e9f914610102578063a40e69c7146100fd578063a48a9058146100f8578063a8d87a3b146100f3578063da5fcac8146100ee578063e861e907146100e9578063f2fde38b146100e45763fbca3b74146100df575f80fd5b6111e2565b61111c565b6110f4565b610e40565b610dcc565b610d85565b610c90565b61085b565b610834565b6107e2565b61072a565b61070f565b6106d0565b610689565b610645565b6105d6565b6104a2565b61026e565b5f91031261014357565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b0382111761017657604052565b610147565b6001600160401b03811161017657604052565b60a081019081106001600160401b0382111761017657604052565b60c081019081106001600160401b0382111761017657604052565b90601f801991011681019081106001600160401b0382111761017657604052565b604051906101f28261018e565b565b604051906101f28261015b565b6001600160401b03811161017657601f01601f191660200190565b91908251928382525f5b848110610246575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610226565b90602061026b92818152019061021c565b90565b34610143575f366003190112610143576102ba60405161028d8161015b565b600c81526b0526f7574657220312e322e360a41b602082015260405191829160208352602083019061021c565b0390f35b6001600160401b0381160361014357565b81601f82011215610143578035906102e682610201565b926102f460405194856101c4565b8284526020838301011161014357815f926020809301838601378301015290565b6001600160401b0381116101765760051b60200190565b6001600160a01b0381160361014357565b35906101f28261032c565b9080601f8301121561014357813591602061036284610315565b9360409361037360405196876101c4565b818652828087019260061b85010193818511610143578301915b84831061039d5750505050505090565b85838303126101435783869182516103b48161015b565b85356103bf8161032c565b8152828601358382015281520192019161038d565b9060031960408184011261014357600480356103ef816102be565b93602435916001600160401b03938484116101435760a0908484030112610143576104186101e5565b9383820135818111610143578383610432928701016102cf565b8552602484013581811161014357838361044e928701016102cf565b6020860152604484013581811161014357838361046d92870101610348565b604086015261047e6064850161033d565b606086015260848401359081116101435761049a9301016102cf565b608082015290565b34610143576104b0366103d4565b6060810180516001600160a01b03919082161561059e575b506104f46104e7846001600160401b03165f52600360205260405f2090565b546001600160a01b031690565b168015610579579060209161051f936040518095819482936320487ded60e01b84526004840161130e565b03915afa8015610574576102ba915f91610545575b506040519081529081906020820190565b610567915060203d60201161056d575b61055f81836101c4565b810190611253565b5f610534565b503d610555565b61132e565b604051632b88db6760e21b81526001600160401b0384166004820152602490fd5b0390fd5b6002546001600160a01b031690525f6104c8565b9392916105d1906040921515865260606020870152606086019061021c565b930152565b346101435760031960803682011261014357600435906001600160401b0382116101435760a0908236030112610143576024359061ffff82168203610143576102ba91610636916064359161062a8361032c565b604435916004016114b0565b604093919351938493846105b2565b34610143575f366003190112610143576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610143576020366003190112610143576004356106a68161032c565b6106ae6119db565b600280546001600160a01b0319166001600160a01b0392909216919091179055005b346101435760603660031901126101435761070d6004356106f08161032c565b6024356106fc8161032c565b6107046119db565b60443591611648565b005b34610143575f36600319011261014357602060405160848152f35b34610143575f366003190112610143576001546001600160a01b0390811633036107a4575f546001600160a01b03165f80546001600160a01b0319163317905590600180546001600160a01b03191690553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606490fd5b3461014357604036600319011261014357602061082a610819600435610807816102be565b602435906108148261032c565b611a8f565b5f52600560205260405f2054151590565b6040519015158152f35b34610143575f366003190112610143575f546040516001600160a01b039091168152602090f35b610864366103d4565b6040805163397796f760e01b81526020906004906001600160a01b0390838184817f000000000000000000000000000000000000000000000000000000000000000086165afa908115610574575f91610c08575b50610bf9576108db6104e7876001600160401b03165f52600360205260405f2090565b938185168015610bd457606087018051909690610908906001600160a01b03165b6001600160a01b031690565b610b47576002546001600160a01b0316875282516320487ded60e01b81528681806109368c8e8b840161130e565b0381865afa908115610574575f91610b2a575b503410610b1a578651349790610969906108fc906001600160a01b031681565b91823b15610143575f8793865194858092630d0e30db60e41b825234905af1928315610574576109b593610b01575b5090513491906109b0906001600160a01b03166108fc565b611a2c565b5f5b82880180518051831015610a9257906109e86108fc6109da85610a2196956116c7565b51516001600160a01b031690565b855163122a62a960e21b81526001600160401b038d168982019081526001600160a01b038316602082015290948a918691829160400190565b0381885afa90811561057457878a85610a57956001985f96610a5d575b5090610a4a91516116c7565b5101519216903390611abf565b016109b7565b610a4a92919650610a8390843d8611610a8b575b610a7b81836101c4565b8101906116e0565b959091610a3e565b503d610a71565b50505091509391945f8497610abc87519889968795869463df0aa9e960e01b8652339386016116f5565b03925af1918215610574576102ba935f93610ae2575b5050519081529081906020820190565b610af9929350803d1061056d5761055f81836101c4565b905f80610ad2565b80610b0e610b149261017b565b80610139565b5f610998565b82516303ed377360e11b81528590fd5b610b419150873d891161056d5761055f81836101c4565b5f610949565b34610bc45782516320487ded60e01b815290868280610b698c8e8b840161130e565b0381865afa90811561057457610ba0925f92610ba5575b50975190978891610b99906001600160a01b03166108fc565b3390611abf565b6109b5565b610bbd919250883d8a1161056d5761055f81836101c4565b905f610b80565b8251631841b4e160e01b81528590fd5b8151632b88db6760e21b81526001600160401b03891681860190815281906020010390fd5b50825163c148371560e01b8152fd5b610c289150843d8611610c2e575b610c2081836101c4565b810190611339565b5f6108b8565b503d610c16565b60208082019080835283518092528060408094019401925f905b838210610c5e57505050505090565b845180516001600160401b031687528301516001600160a01b0316868401529485019493820193600190910190610c4f565b34610143575f366003190112610143576040518060045480835281602080940160045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b925f5b86828210610d6f5784610ced818603826101c4565b610cf78151611732565b915f5b8251811015610d615780610d10600192856116c7565b51610d45610d1c6101f4565b60a083901c6001600160401b031681529160a085901b859003166001600160a01b031685830152565b610d4f82876116c7565b52610d5a81866116c7565b5001610cfa565b604051806102ba8682610c35565b8554845260019586019587955093019201610cd8565b3461014357602036600319011261014357602061082a600435610da7816102be565b6001600160401b03165f908152600360205260409020546001600160a01b0316151590565b34610143576020366003190112610143576001600160401b03600435610df1816102be565b165f526003602052602060018060a01b0360405f205416604051908152f35b9181601f84011215610143578235916001600160401b038311610143576020808501948460061b01011161014357565b346101435760603660031901126101435760046001600160401b03813581811161014357610e72903690600401610e10565b91909260243582811161014357610e8d903690600401610e10565b909160443584811161014357610ea7903690600401610e10565b969095610eb26119db565b5f5b81811061101b575050505f5b828110610f6557505050505f5b838110610ed657005b80610eec610ee76001938787611790565b611351565b610f026020610efc848989611790565b016117d9565b9084610f16610f118484611a8f565b611d12565b610f24575b50505001610ecd565b6040516001600160a01b0393909316835216907fa4bdf64ebdf3316320601a081916a75aa144bcef6c4beeb0e9fb1982cacc6b9490602090a25f8084610f1b565b610f73610ee7828587611790565b610f836020610efc848789611790565b90610f9d610f99610f948484611a8f565b611c67565b1590565b610fe6576040516001600160a01b0392909216825260019291908716907fa823809efda3ba66c873364eec120fa0923d9fabda73bc97dd5663341e2d9bcb90602090a201610ec0565b60408051630496477960e41b81526001600160401b039092168286019081526001600160a01b03841660208201528291010390fd5b8061103161102c6001938587611790565b6117a0565b7f1f7d0ec248b80e5c0dde0ee531c4fc8fdb6ce9a2b3d90f560c74acd6a7202f23896110d66110c860208501946110bb611071875160018060a01b031690565b61109c61108584516001600160401b031690565b6001600160401b03165f52600360205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b516001600160401b031690565b93516001600160a01b031690565b6040516001600160a01b03919091168152921691602090a201610eb4565b34610143575f366003190112610143576002546040516001600160a01b039091168152602090f35b34610143576020366003190112610143576004356111398161032c565b6111416119db565b6001600160a01b031633811461119d57600180546001600160a01b031916821790555f54611177906001600160a01b03166108fc565b7fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12785f80a3005b60405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606490fd5b346101435760208060031936011261014357611208600435611203816102be565b611866565b90604051918183928301818452825180915281604085019301915f5b82811061123357505050500390f35b83516001600160a01b031685528695509381019392810192600101611224565b90816020910312610143575190565b9190611277835160a0835260a083019061021c565b9061128e602092838601518382038585015261021c565b60409060408601519383820360408501528080865193848152019501925f905b8382106112e5575050506060808701516001600160a01b03169084015250929361026b93506080015190608081840391015261021c565b845180516001600160a01b031688528301518784015295860195938201936001909101906112ae565b6040906001600160401b0361026b94931681528160208201520190611262565b6040513d5f823e3d90fd5b90816020910312610143575180151581036101435790565b3561026b816102be565b9035601e19823603018112156101435701602081359101916001600160401b03821161014357813603831361014357565b908060209392818452848401375f828201840152601f01601f1916010190565b9190808252602080920192915f905b8282106113c9575050505090565b9091929360019085356113db8161032c565b828060a01b031681528286013583820152604080910195019201909291926113bb565b906020825280356020830152602081013591611419836102be565b6001600160401b03809316604082015261144a611439604084018461135b565b60a0606085015260c084019161138c565b9261146f61145b606085018561135b565b601f1985880381016080870152969161138c565b926080810135601e1982360301811215610143570191602083359301918311610143578260061b360382136101435760a061026b95828603019101526113ac565b60405163397796f760e01b815290949291906020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610574575f916115fa575b506115e8576020850194611527610f996108198835611520816102be565b3390611a8f565b6115d657611599926115ce61158f7f9b877de93ea9895756e337442c657f95a34fc68e7eb988bdfa693d5be83016b6946040519360208501996385572ffb60e01b8b526115898661157b89602483016113fe565b03601f1981018852876101c4565b85611940565b9891969099611351565b9251902060408051943585526001600160401b0390931660208501523392840192909252606083019190915281906080820190565b0390a1929190565b604051636918b76f60e11b8152600490fd5b60405163c148371560e01b8152600490fd5b611613915060203d602011610c2e57610c2081836101c4565b5f611502565b3d15611643573d9061162a82610201565b9161163860405193846101c4565b82523d5f602084013e565b606090565b9091906001600160a01b0390818416801561169b57501691821561166f576101f292611a2c565b5f809350809281925af1611681611619565b501561168957565b60405163e417b80b60e01b8152600490fd5b602490604051906326a78f8f60e01b82526004820152fd5b634e487b7160e01b5f52603260045260245ffd5b80518210156116db5760209160051b010190565b6116b3565b90816020910312610143575161026b8161032c565b929493906060926001600160401b0361171c92168552608060208601526080850190611262565b60408401959095526001600160a01b0316910152565b9061173c82610315565b60409061174c60405191826101c4565b838152809361175d601f1991610315565b01915f5b83811061176e5750505050565b602090825161177c8161015b565b5f8152825f81830152828601015201611761565b91908110156116db5760061b0190565b604081360312610143576020604051916117b98361015b565b80356117c4816102be565b835201356117d18161032c565b602082015290565b3561026b8161032c565b6020908181840312610143578051906001600160401b03821161014357019180601f8401121561014357825161181881610315565b9361182660405195866101c4565b818552838086019260051b820101928311610143578301905b82821061184d575050505090565b838091835161185b8161032c565b81520191019061183f565b6001600160401b0381165f908152600360205260409020546001600160a01b031615611919576001600160401b0381165f908152600360205260408120546118e892906118bd906108fc906001600160a01b031681565b6040518080958194633ef28edd60e21b8352600483019190916001600160401b036020820193169052565b03915afa908115610574575f916118fd575090565b61026b91503d805f833e61191181836101c4565b8101906117e3565b50604051602081018181106001600160401b03821117610176576040525f81525f36813790565b604051949261194e866101a9565b60848652602086019460a0368737833b156119cc575a908082106119bd578291038060061c900311156119ae575f918291825a9560208451940192f1905a9003923d90608482116119a5575b5f908287523e929190565b6084915061199a565b6337c3be2960e01b5f5260045ffd5b632be8ca8b60e21b5f5260045ffd5b63030ed58f60e21b5f5260045ffd5b5f546001600160a01b031633036119ee57565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815260808101916001600160401b03831182841017610176576101f292604052611b01565b634e487b7160e01b5f52601160045260245ffd5b6001600160a01b0390911660a09190911b67ffffffffffffffff60a01b16908101908110611aba5790565b611a7b565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526101f291611b018261018e565b604051611b5e916001600160a01b0316611b1a8261015b565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1611b58611619565b91611d88565b80519081611b6b57505050565b8280611b7b938301019101611339565b15611b835750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6004548110156116db5760045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905f90565b6004548015611c53575f19810190808210156116db577f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19a5f91600483520155600455565b634e487b7160e01b5f52603160045260245ffd5b5f818152600560205260409020548015611d0c575f199181830191808311611aba57600454938401938411611aba5783835f95611cbd9503611cc3575b505050611caf611c0f565b5f52600560205260405f2090565b55600190565b611caf611ceb91611ce3611cd9611d0395611bda565b90549060031b1c90565b928391611bda565b90919082549060031b91821b915f19901b1916179055565b555f8080611ca4565b50505f90565b805f52600560205260405f2054155f14611d8357600454680100000000000000008110156101765760018101806004558110156116db5781907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0155600454905f52600560205260405f2055600190565b505f90565b91929015611dea5750815115611d9c575090565b3b15611da55790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015611dfd5750805190602001fd5b60405162461bcd60e51b815290819061059a906004830161025a56fea26469706673582212201e0fba5fe8d69c7858fc719ac378071573efd1d64618d46a45e4bd04520d639864736f6c63430008180033" + } + }, + "version": "0.8.24+commit.e11b9ed9" +} \ No newline at end of file diff --git a/packages/ccip-js/hardhat.config.cjs b/packages/ccip-js/hardhat.config.cjs new file mode 100644 index 0000000..6f94d66 --- /dev/null +++ b/packages/ccip-js/hardhat.config.cjs @@ -0,0 +1,20 @@ +require("@nomicfoundation/hardhat-toolbox"); +require("@nomicfoundation/hardhat-ethers"); +require("@nomicfoundation/hardhat-viem"); + +require("chai"); +require("mocha"); +require("ethers"); +/** @type import('hardhat/config').HardhatUserConfig */ +module.exports = { + solidity: "0.8.24", + solc: { + version: "0.8.24", + }, + paths: { + sources: "src/contracts", + tests: "./test", + cache: "./cache", + artifacts: "./artifacts", + }, +}; diff --git a/packages/ccip-js/ignition/modules/Lock.js b/packages/ccip-js/ignition/modules/Lock.js new file mode 100644 index 0000000..a6e8134 --- /dev/null +++ b/packages/ccip-js/ignition/modules/Lock.js @@ -0,0 +1,18 @@ +// This setup uses Hardhat Ignition to manage smart contract deployments. +// Learn more about it at https://hardhat.org/ignition + +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +const JAN_1ST_2030 = 1893456000; +const ONE_GWEI = 1_000_000_000n; + +export default buildModule("LockModule", (m) => { + const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030); + const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI); + + const lock = m.contract("Lock", [unlockTime], { + value: lockedAmount, + }); + + return { lock }; +}); diff --git a/packages/ccip-js/jest.config.js b/packages/ccip-js/jest.config.js new file mode 100644 index 0000000..0168590 --- /dev/null +++ b/packages/ccip-js/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} **/ +export default { +testEnvironment: 'node', +transform: { +'^.+.tsx?$': ['ts-jest', {}], +}, +workerThreads: true, +}; \ No newline at end of file diff --git a/packages/ccip-js/package.json b/packages/ccip-js/package.json new file mode 100644 index 0000000..283378a --- /dev/null +++ b/packages/ccip-js/package.json @@ -0,0 +1,57 @@ +{ + "name": "@chainlink/ccip-js", + "version": "0.2.1", + "private": false, + "main": "dist/api.js", + "types": "dist/api.d.ts", + "type": "module", + "files": [ + "dist" + ], + "scripts": { + "check": "tsc --noEmit", + "build:watch": "tsc -w", + "build": "tsc && hardhat compile", + "lint": "eslint 'src/**/*.{ts,js}'", + "format": "prettier --write 'src/**/*.{ts,js,json,md}'", + "pretest": "anvil --block-time 2", + "t:int": "jest --coverage -u -t=\"Integration\"", + "t:unit": "jest --coverage -u -t=\"Unit\"", + "test": "jest --coverage", + "test:hh": "hardhat test" + }, + "devDependencies": { + "@babel/preset-typescript": "^7.26.0", + "@chainlink/contracts": "^1.3.0", + "@chainlink/contracts-ccip": "^1.5.0", + "@chainlink/env-enc": "^1.0.5", + "@chainlink/local": "^0.2.2", + "@jest/globals": "^29.7.0", + "@tsconfig/node16": "^16.1.3", + "@types/mocha": "^10.0.7", + "@types/node": "^22.9.3", + "@typescript-eslint/eslint-plugin": "^8.1.0", + "@typescript-eslint/parser": "^8.1.0", + "dotenv": "^16.4.5", + "eslint": "^9.15.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "hardhat": "^2.22.7", + "jest": "^29.7.0", + "prettier": "^3.2.5", + "ts-node": "^10.9.2" + }, + "dependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.8", + "@nomicfoundation/hardhat-ethers": "^3.0.8", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@nomicfoundation/hardhat-viem": "^2.0.5", + "@openzeppelin/contracts": "^5.1.0", + "chai": "^4.5.0", + "ethers": "6.13.4", + "mocha": "^10.7.3", + "ts-jest": "^29.2.5", + "typescript": "^5.5.4", + "viem": "2.21.25" + } +} diff --git a/packages/ccip-js/src/abi/BridgeToken.json b/packages/ccip-js/src/abi/BridgeToken.json new file mode 100644 index 0000000..47435c3 --- /dev/null +++ b/packages/ccip-js/src/abi/BridgeToken.json @@ -0,0 +1,787 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "supplyAfterMint", + "type": "uint256" + } + ], + "name": "MaxSupplyExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotBurner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotMinter", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "BurnAccessGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "BurnAccessRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "MintAccessGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "MintAccessRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "drip", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getBurners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinters", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "grantBurnRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burnAndMinter", + "type": "address" + } + ], + "name": "grantMintAndBurnRoles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "grantMintRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "isBurner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "isMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "burner", + "type": "address" + } + ], + "name": "revokeBurnRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "revokeMintRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/packages/ccip-js/src/abi/CCIPLocalSimulator.json b/packages/ccip-js/src/abi/CCIPLocalSimulator.json new file mode 100644 index 0000000..a6179eb --- /dev/null +++ b/packages/ccip-js/src/abi/CCIPLocalSimulator.json @@ -0,0 +1,143 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CCIPLocalSimulator__MsgSenderIsNotTokenOwner", + "type": "error" + }, + { + "inputs": [], + "name": "configuration", + "outputs": [ + { + "internalType": "uint64", + "name": "chainSelector_", + "type": "uint64" + }, + { + "internalType": "contract IRouterClient", + "name": "sourceRouter_", + "type": "address" + }, + { + "internalType": "contract IRouterClient", + "name": "destinationRouter_", + "type": "address" + }, + { + "internalType": "contract WETH9", + "name": "wrappedNative_", + "type": "address" + }, + { + "internalType": "contract LinkToken", + "name": "linkToken_", + "type": "address" + }, + { + "internalType": "contract BurnMintERC677Helper", + "name": "ccipBnM_", + "type": "address" + }, + { + "internalType": "contract BurnMintERC677Helper", + "name": "ccipLnM_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "getSupportedTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainSelector", + "type": "uint64" + } + ], + "name": "isChainSupported", + "outputs": [ + { + "internalType": "bool", + "name": "supported", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "requestLinkFromFaucet", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "supportNewTokenViaGetCCIPAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "supportNewTokenViaOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/packages/ccip-js/src/abi/IERC20Metadata.json b/packages/ccip-js/src/abi/IERC20Metadata.json new file mode 100644 index 0000000..177ac83 --- /dev/null +++ b/packages/ccip-js/src/abi/IERC20Metadata.json @@ -0,0 +1,224 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/ccip-js/src/abi/IPoolV1.json b/packages/ccip-js/src/abi/IPoolV1.json new file mode 100644 index 0000000..a054068 --- /dev/null +++ b/packages/ccip-js/src/abi/IPoolV1.json @@ -0,0 +1,83 @@ +[ + { + "inputs": [{ "internalType": "uint64", "name": "remoteChainSelector", "type": "uint64" }], + "name": "isSupportedChain", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "isSupportedToken", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bytes", "name": "receiver", "type": "bytes" }, + { "internalType": "uint64", "name": "remoteChainSelector", "type": "uint64" }, + { "internalType": "address", "name": "originalSender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "localToken", "type": "address" } + ], + "internalType": "struct Pool.LockOrBurnInV1", + "name": "lockOrBurnIn", + "type": "tuple" + } + ], + "name": "lockOrBurn", + "outputs": [ + { + "components": [ + { "internalType": "bytes", "name": "destTokenAddress", "type": "bytes" }, + { "internalType": "bytes", "name": "destPoolData", "type": "bytes" } + ], + "internalType": "struct Pool.LockOrBurnOutV1", + "name": "lockOrBurnOut", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bytes", "name": "originalSender", "type": "bytes" }, + { "internalType": "uint64", "name": "remoteChainSelector", "type": "uint64" }, + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "localToken", "type": "address" }, + { "internalType": "bytes", "name": "sourcePoolAddress", "type": "bytes" }, + { "internalType": "bytes", "name": "sourcePoolData", "type": "bytes" }, + { "internalType": "bytes", "name": "offchainTokenData", "type": "bytes" } + ], + "internalType": "struct Pool.ReleaseOrMintInV1", + "name": "releaseOrMintIn", + "type": "tuple" + } + ], + "name": "releaseOrMint", + "outputs": [ + { + "components": [{ "internalType": "uint256", "name": "destinationAmount", "type": "uint256" }], + "internalType": "struct Pool.ReleaseOrMintOutV1", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/packages/ccip-js/src/abi/OffRamp.json b/packages/ccip-js/src/abi/OffRamp.json new file mode 100644 index 0000000..e658545 --- /dev/null +++ b/packages/ccip-js/src/abi/OffRamp.json @@ -0,0 +1,1142 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "staticConfig", + "type": "tuple", + "internalType": "struct EVM2EVMOffRamp.StaticConfig", + "components": [ + { + "name": "commitStore", + "type": "address", + "internalType": "address" + }, + { + "name": "chainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "onRamp", "type": "address", "internalType": "address" }, + { + "name": "prevOffRamp", + "type": "address", + "internalType": "address" + }, + { "name": "armProxy", "type": "address", "internalType": "address" } + ] + }, + { + "name": "sourceTokens", + "type": "address[]", + "internalType": "contract IERC20[]" + }, + { + "name": "pools", + "type": "address[]", + "internalType": "contract IPool[]" + }, + { + "name": "rateLimiterConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "applyPoolUpdates", + "inputs": [ + { + "name": "removes", + "type": "tuple[]", + "internalType": "struct Internal.PoolUpdate[]", + "components": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "pool", "type": "address", "internalType": "address" } + ] + }, + { + "name": "adds", + "type": "tuple[]", + "internalType": "struct Internal.PoolUpdate[]", + "components": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "pool", "type": "address", "internalType": "address" } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "ccipReceive", + "inputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct Client.Any2EVMMessage", + "components": [ + { + "name": "messageId", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "sender", "type": "bytes", "internalType": "bytes" }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "destTokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ] + } + ], + "outputs": [], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "currentRateLimiterState", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct RateLimiter.TokenBucket", + "components": [ + { "name": "tokens", "type": "uint128", "internalType": "uint128" }, + { + "name": "lastUpdated", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "executeSingleMessage", + "inputs": [ + { + "name": "message", + "type": "tuple", + "internalType": "struct Internal.EVM2EVMMessage", + "components": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "sender", "type": "address", "internalType": "address" }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "sequenceNumber", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "gasLimit", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "strict", "type": "bool", "internalType": "bool" }, + { "name": "nonce", "type": "uint64", "internalType": "uint64" }, + { + "name": "feeToken", + "type": "address", + "internalType": "address" + }, + { + "name": "feeTokenAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "tokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "sourceTokenData", + "type": "bytes[]", + "internalType": "bytes[]" + }, + { + "name": "messageId", + "type": "bytes32", + "internalType": "bytes32" + } + ] + }, + { + "name": "offchainTokenData", + "type": "bytes[]", + "internalType": "bytes[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getDestinationToken", + "inputs": [ + { + "name": "sourceToken", + "type": "address", + "internalType": "contract IERC20" + } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "contract IERC20" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getDestinationTokens", + "inputs": [], + "outputs": [ + { + "name": "destTokens", + "type": "address[]", + "internalType": "contract IERC20[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getDynamicConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct EVM2EVMOffRamp.DynamicConfig", + "components": [ + { + "name": "permissionLessExecutionThresholdSeconds", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "router", "type": "address", "internalType": "address" }, + { + "name": "priceRegistry", + "type": "address", + "internalType": "address" + }, + { + "name": "maxNumberOfTokensPerMsg", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "maxDataBytes", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "maxPoolReleaseOrMintGas", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getExecutionState", + "inputs": [{ "name": "sequenceNumber", "type": "uint64", "internalType": "uint64" }], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum Internal.MessageExecutionState" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getPoolByDestToken", + "inputs": [ + { + "name": "destToken", + "type": "address", + "internalType": "contract IERC20" + } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "contract IPool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getPoolBySourceToken", + "inputs": [ + { + "name": "sourceToken", + "type": "address", + "internalType": "contract IERC20" + } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "contract IPool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getSenderNonce", + "inputs": [{ "name": "sender", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "nonce", "type": "uint64", "internalType": "uint64" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getStaticConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct EVM2EVMOffRamp.StaticConfig", + "components": [ + { + "name": "commitStore", + "type": "address", + "internalType": "address" + }, + { + "name": "chainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "onRamp", "type": "address", "internalType": "address" }, + { + "name": "prevOffRamp", + "type": "address", + "internalType": "address" + }, + { "name": "armProxy", "type": "address", "internalType": "address" } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getSupportedTokens", + "inputs": [], + "outputs": [ + { + "name": "sourceTokens", + "type": "address[]", + "internalType": "contract IERC20[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTokenLimitAdmin", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTransmitters", + "inputs": [], + "outputs": [{ "name": "", "type": "address[]", "internalType": "address[]" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "latestConfigDetails", + "inputs": [], + "outputs": [ + { "name": "configCount", "type": "uint32", "internalType": "uint32" }, + { "name": "blockNumber", "type": "uint32", "internalType": "uint32" }, + { "name": "configDigest", "type": "bytes32", "internalType": "bytes32" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "latestConfigDigestAndEpoch", + "inputs": [], + "outputs": [ + { "name": "scanLogs", "type": "bool", "internalType": "bool" }, + { + "name": "configDigest", + "type": "bytes32", + "internalType": "bytes32" + }, + { "name": "epoch", "type": "uint32", "internalType": "uint32" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "manuallyExecute", + "inputs": [ + { + "name": "report", + "type": "tuple", + "internalType": "struct Internal.ExecutionReport", + "components": [ + { + "name": "messages", + "type": "tuple[]", + "internalType": "struct Internal.EVM2EVMMessage[]", + "components": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "sender", + "type": "address", + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "sequenceNumber", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "gasLimit", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "strict", "type": "bool", "internalType": "bool" }, + { "name": "nonce", "type": "uint64", "internalType": "uint64" }, + { + "name": "feeToken", + "type": "address", + "internalType": "address" + }, + { + "name": "feeTokenAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "tokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "sourceTokenData", + "type": "bytes[]", + "internalType": "bytes[]" + }, + { + "name": "messageId", + "type": "bytes32", + "internalType": "bytes32" + } + ] + }, + { + "name": "offchainTokenData", + "type": "bytes[][]", + "internalType": "bytes[][]" + }, + { + "name": "proofs", + "type": "bytes32[]", + "internalType": "bytes32[]" + }, + { + "name": "proofFlagBits", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "gasLimitOverrides", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setAdmin", + "inputs": [{ "name": "newAdmin", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOCR2Config", + "inputs": [ + { "name": "signers", "type": "address[]", "internalType": "address[]" }, + { + "name": "transmitters", + "type": "address[]", + "internalType": "address[]" + }, + { "name": "f", "type": "uint8", "internalType": "uint8" }, + { "name": "onchainConfig", "type": "bytes", "internalType": "bytes" }, + { + "name": "offchainConfigVersion", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "offchainConfig", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setRateLimiterConfig", + "inputs": [ + { + "name": "config", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transmit", + "inputs": [ + { + "name": "reportContext", + "type": "bytes32[3]", + "internalType": "bytes32[3]" + }, + { "name": "report", "type": "bytes", "internalType": "bytes" }, + { "name": "rs", "type": "bytes32[]", "internalType": "bytes32[]" }, + { "name": "ss", "type": "bytes32[]", "internalType": "bytes32[]" }, + { "name": "", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "typeAndVersion", + "inputs": [], + "outputs": [{ "name": "", "type": "string", "internalType": "string" }], + "stateMutability": "view" + }, + { + "type": "event", + "name": "AdminSet", + "inputs": [ + { + "name": "newAdmin", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ConfigSet", + "inputs": [ + { + "name": "staticConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct EVM2EVMOffRamp.StaticConfig", + "components": [ + { + "name": "commitStore", + "type": "address", + "internalType": "address" + }, + { + "name": "chainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "onRamp", "type": "address", "internalType": "address" }, + { + "name": "prevOffRamp", + "type": "address", + "internalType": "address" + }, + { "name": "armProxy", "type": "address", "internalType": "address" } + ] + }, + { + "name": "dynamicConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct EVM2EVMOffRamp.DynamicConfig", + "components": [ + { + "name": "permissionLessExecutionThresholdSeconds", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "router", "type": "address", "internalType": "address" }, + { + "name": "priceRegistry", + "type": "address", + "internalType": "address" + }, + { + "name": "maxNumberOfTokensPerMsg", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "maxDataBytes", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "maxPoolReleaseOrMintGas", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ConfigSet", + "inputs": [ + { + "name": "previousConfigBlockNumber", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "configDigest", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + }, + { + "name": "configCount", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "signers", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + }, + { + "name": "transmitters", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + }, + { + "name": "f", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "onchainConfig", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "offchainConfigVersion", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "offchainConfig", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ExecutionStateChanged", + "inputs": [ + { + "name": "sequenceNumber", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "messageId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "state", + "type": "uint8", + "indexed": false, + "internalType": "enum Internal.MessageExecutionState" + }, + { + "name": "returnData", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferRequested", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PoolAdded", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "pool", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PoolRemoved", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "pool", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SkippedIncorrectNonce", + "inputs": [ + { + "name": "nonce", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SkippedSenderWithPreviousRampMessageInflight", + "inputs": [ + { + "name": "nonce", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transmitted", + "inputs": [ + { + "name": "configDigest", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + }, + { + "name": "epoch", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AggregateValueMaxCapacityExceeded", + "inputs": [ + { "name": "capacity", "type": "uint256", "internalType": "uint256" }, + { "name": "requested", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "AggregateValueRateLimitReached", + "inputs": [ + { + "name": "minWaitInSeconds", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "available", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "AlreadyAttempted", + "inputs": [{ "name": "sequenceNumber", "type": "uint64", "internalType": "uint64" }] + }, + { + "type": "error", + "name": "AlreadyExecuted", + "inputs": [{ "name": "sequenceNumber", "type": "uint64", "internalType": "uint64" }] + }, + { "type": "error", "name": "BadARMSignal", "inputs": [] }, + { "type": "error", "name": "BucketOverfilled", "inputs": [] }, + { "type": "error", "name": "CanOnlySelfCall", "inputs": [] }, + { "type": "error", "name": "CommitStoreAlreadyInUse", "inputs": [] }, + { + "type": "error", + "name": "ConfigDigestMismatch", + "inputs": [ + { "name": "expected", "type": "bytes32", "internalType": "bytes32" }, + { "name": "actual", "type": "bytes32", "internalType": "bytes32" } + ] + }, + { "type": "error", "name": "EmptyReport", "inputs": [] }, + { + "type": "error", + "name": "ExecutionError", + "inputs": [{ "name": "error", "type": "bytes", "internalType": "bytes" }] + }, + { + "type": "error", + "name": "ForkedChain", + "inputs": [ + { "name": "expected", "type": "uint256", "internalType": "uint256" }, + { "name": "actual", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "InvalidConfig", + "inputs": [{ "name": "message", "type": "string", "internalType": "string" }] + }, + { + "type": "error", + "name": "InvalidManualExecutionGasLimit", + "inputs": [ + { "name": "index", "type": "uint256", "internalType": "uint256" }, + { "name": "newLimit", "type": "uint256", "internalType": "uint256" } + ] + }, + { "type": "error", "name": "InvalidMessageId", "inputs": [] }, + { + "type": "error", + "name": "InvalidNewState", + "inputs": [ + { + "name": "sequenceNumber", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "newState", + "type": "uint8", + "internalType": "enum Internal.MessageExecutionState" + } + ] + }, + { + "type": "error", + "name": "InvalidSourceChain", + "inputs": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ] + }, + { "type": "error", "name": "InvalidTokenPoolConfig", "inputs": [] }, + { + "type": "error", + "name": "ManualExecutionGasLimitMismatch", + "inputs": [] + }, + { "type": "error", "name": "ManualExecutionNotYetEnabled", "inputs": [] }, + { + "type": "error", + "name": "MessageTooLarge", + "inputs": [ + { "name": "maxSize", "type": "uint256", "internalType": "uint256" }, + { "name": "actualSize", "type": "uint256", "internalType": "uint256" } + ] + }, + { "type": "error", "name": "OnlyCallableByAdminOrOwner", "inputs": [] }, + { "type": "error", "name": "OracleCannotBeZeroAddress", "inputs": [] }, + { "type": "error", "name": "PoolAlreadyAdded", "inputs": [] }, + { "type": "error", "name": "PoolDoesNotExist", "inputs": [] }, + { + "type": "error", + "name": "PriceNotFoundForToken", + "inputs": [{ "name": "token", "type": "address", "internalType": "address" }] + }, + { + "type": "error", + "name": "ReceiverError", + "inputs": [{ "name": "error", "type": "bytes", "internalType": "bytes" }] + }, + { "type": "error", "name": "RootNotCommitted", "inputs": [] }, + { + "type": "error", + "name": "TokenDataMismatch", + "inputs": [{ "name": "sequenceNumber", "type": "uint64", "internalType": "uint64" }] + }, + { + "type": "error", + "name": "TokenHandlingError", + "inputs": [{ "name": "error", "type": "bytes", "internalType": "bytes" }] + }, + { + "type": "error", + "name": "TokenMaxCapacityExceeded", + "inputs": [ + { "name": "capacity", "type": "uint256", "internalType": "uint256" }, + { "name": "requested", "type": "uint256", "internalType": "uint256" }, + { "name": "tokenAddress", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "TokenPoolMismatch", "inputs": [] }, + { + "type": "error", + "name": "TokenRateLimitReached", + "inputs": [ + { + "name": "minWaitInSeconds", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "available", "type": "uint256", "internalType": "uint256" }, + { "name": "tokenAddress", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "UnauthorizedTransmitter", "inputs": [] }, + { "type": "error", "name": "UnexpectedTokenData", "inputs": [] }, + { + "type": "error", + "name": "UnsupportedNumberOfTokens", + "inputs": [{ "name": "sequenceNumber", "type": "uint64", "internalType": "uint64" }] + }, + { + "type": "error", + "name": "UnsupportedToken", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "contract IERC20" + } + ] + }, + { + "type": "error", + "name": "WrongMessageLength", + "inputs": [ + { "name": "expected", "type": "uint256", "internalType": "uint256" }, + { "name": "actual", "type": "uint256", "internalType": "uint256" } + ] + }, + { "type": "error", "name": "ZeroAddressNotAllowed", "inputs": [] } +] diff --git a/packages/ccip-js/src/abi/OnRamp.json b/packages/ccip-js/src/abi/OnRamp.json new file mode 100644 index 0000000..fa9e4a8 --- /dev/null +++ b/packages/ccip-js/src/abi/OnRamp.json @@ -0,0 +1,785 @@ +[ + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "linkToken", "type": "address" }, + { "internalType": "uint64", "name": "chainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "destChainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "defaultTxGasLimit", "type": "uint64" }, + { "internalType": "uint96", "name": "maxNopFeesJuels", "type": "uint96" }, + { "internalType": "address", "name": "prevOnRamp", "type": "address" }, + { "internalType": "address", "name": "rmnProxy", "type": "address" }, + { "internalType": "address", "name": "tokenAdminRegistry", "type": "address" } + ], + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "staticConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "uint16", "name": "maxNumberOfTokensPerMsg", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerPayloadByte", "type": "uint16" }, + { "internalType": "uint32", "name": "destDataAvailabilityOverheadGas", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerDataAvailabilityByte", "type": "uint16" }, + { "internalType": "uint16", "name": "destDataAvailabilityMultiplierBps", "type": "uint16" }, + { "internalType": "address", "name": "priceRegistry", "type": "address" }, + { "internalType": "uint32", "name": "maxDataBytes", "type": "uint32" }, + { "internalType": "uint32", "name": "maxPerMsgGasLimit", "type": "uint32" }, + { "internalType": "uint16", "name": "defaultTokenFeeUSDCents", "type": "uint16" }, + { "internalType": "uint32", "name": "defaultTokenDestGasOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "enforceOutOfOrder", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "bool", "name": "isEnabled", "type": "bool" }, + { "internalType": "uint128", "name": "capacity", "type": "uint128" }, + { "internalType": "uint128", "name": "rate", "type": "uint128" } + ], + "internalType": "struct RateLimiter.Config", + "name": "rateLimiterConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "networkFeeUSDCents", "type": "uint32" }, + { "internalType": "uint64", "name": "gasMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "uint64", "name": "premiumMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeTokenConfigs", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "minFeeUSDCents", "type": "uint32" }, + { "internalType": "uint32", "name": "maxFeeUSDCents", "type": "uint32" }, + { "internalType": "uint16", "name": "deciBps", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint32", "name": "destBytesOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "aggregateRateLimitEnabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "tokenTransferFeeConfigArgs", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "address", "name": "nop", "type": "address" }, + { "internalType": "uint16", "name": "weight", "type": "uint16" } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "capacity", "type": "uint256" }, + { "internalType": "uint256", "name": "requested", "type": "uint256" } + ], + "name": "AggregateValueMaxCapacityExceeded", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "minWaitInSeconds", "type": "uint256" }, + { "internalType": "uint256", "name": "available", "type": "uint256" } + ], + "name": "AggregateValueRateLimitReached", + "type": "error" + }, + { "inputs": [], "name": "BucketOverfilled", "type": "error" }, + { "inputs": [], "name": "CannotSendZeroTokens", "type": "error" }, + { "inputs": [], "name": "CursedByRMN", "type": "error" }, + { "inputs": [], "name": "ExtraArgOutOfOrderExecutionMustBeTrue", "type": "error" }, + { "inputs": [], "name": "GetSupportedTokensFunctionalityRemovedCheckAdminRegistry", "type": "error" }, + { "inputs": [], "name": "InsufficientBalance", "type": "error" }, + { + "inputs": [{ "internalType": "uint64", "name": "chainSelector", "type": "uint64" }], + "name": "InvalidChainSelector", + "type": "error" + }, + { "inputs": [], "name": "InvalidConfig", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "destBytesOverhead", "type": "uint32" } + ], + "name": "InvalidDestBytesOverhead", + "type": "error" + }, + { + "inputs": [{ "internalType": "bytes", "name": "encodedAddress", "type": "bytes" }], + "name": "InvalidEVMAddress", + "type": "error" + }, + { "inputs": [], "name": "InvalidExtraArgsTag", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "nop", "type": "address" }], + "name": "InvalidNopAddress", + "type": "error" + }, + { "inputs": [], "name": "InvalidWithdrawParams", "type": "error" }, + { "inputs": [], "name": "LinkBalanceNotSettled", "type": "error" }, + { "inputs": [], "name": "MaxFeeBalanceReached", "type": "error" }, + { "inputs": [], "name": "MessageGasLimitTooHigh", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "maxSize", "type": "uint256" }, + { "internalType": "uint256", "name": "actualSize", "type": "uint256" } + ], + "name": "MessageTooLarge", + "type": "error" + }, + { "inputs": [], "name": "MustBeCalledByRouter", "type": "error" }, + { "inputs": [], "name": "NoFeesToPay", "type": "error" }, + { "inputs": [], "name": "NoNopsToPay", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "NotAFeeToken", + "type": "error" + }, + { "inputs": [], "name": "OnlyCallableByAdminOrOwner", "type": "error" }, + { "inputs": [], "name": "OnlyCallableByOwnerOrAdmin", "type": "error" }, + { "inputs": [], "name": "OnlyCallableByOwnerOrAdminOrNop", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "PriceNotFoundForToken", + "type": "error" + }, + { "inputs": [], "name": "RouterMustSetOriginalSender", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "SourceTokenDataTooLarge", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "capacity", "type": "uint256" }, + { "internalType": "uint256", "name": "requested", "type": "uint256" }, + { "internalType": "address", "name": "tokenAddress", "type": "address" } + ], + "name": "TokenMaxCapacityExceeded", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "minWaitInSeconds", "type": "uint256" }, + { "internalType": "uint256", "name": "available", "type": "uint256" }, + { "internalType": "address", "name": "tokenAddress", "type": "address" } + ], + "name": "TokenRateLimitReached", + "type": "error" + }, + { "inputs": [], "name": "TooManyNops", "type": "error" }, + { "inputs": [], "name": "UnsupportedNumberOfTokens", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "UnsupportedToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "newAdmin", "type": "address" }], + "name": "AdminSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { "internalType": "uint64", "name": "sourceChainSelector", "type": "uint64" }, + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "uint64", "name": "sequenceNumber", "type": "uint64" }, + { "internalType": "uint256", "name": "gasLimit", "type": "uint256" }, + { "internalType": "bool", "name": "strict", "type": "bool" }, + { "internalType": "uint64", "name": "nonce", "type": "uint64" }, + { "internalType": "address", "name": "feeToken", "type": "address" }, + { "internalType": "uint256", "name": "feeTokenAmount", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" }, + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { "internalType": "bytes[]", "name": "sourceTokenData", "type": "bytes[]" }, + { "internalType": "bytes32", "name": "messageId", "type": "bytes32" } + ], + "indexed": false, + "internalType": "struct Internal.EVM2EVMMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "CCIPSendRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { "internalType": "bool", "name": "isEnabled", "type": "bool" }, + { "internalType": "uint128", "name": "capacity", "type": "uint128" }, + { "internalType": "uint128", "name": "rate", "type": "uint128" } + ], + "indexed": false, + "internalType": "struct RateLimiter.Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "linkToken", "type": "address" }, + { "internalType": "uint64", "name": "chainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "destChainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "defaultTxGasLimit", "type": "uint64" }, + { "internalType": "uint96", "name": "maxNopFeesJuels", "type": "uint96" }, + { "internalType": "address", "name": "prevOnRamp", "type": "address" }, + { "internalType": "address", "name": "rmnProxy", "type": "address" }, + { "internalType": "address", "name": "tokenAdminRegistry", "type": "address" } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "staticConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "uint16", "name": "maxNumberOfTokensPerMsg", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerPayloadByte", "type": "uint16" }, + { "internalType": "uint32", "name": "destDataAvailabilityOverheadGas", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerDataAvailabilityByte", "type": "uint16" }, + { "internalType": "uint16", "name": "destDataAvailabilityMultiplierBps", "type": "uint16" }, + { "internalType": "address", "name": "priceRegistry", "type": "address" }, + { "internalType": "uint32", "name": "maxDataBytes", "type": "uint32" }, + { "internalType": "uint32", "name": "maxPerMsgGasLimit", "type": "uint32" }, + { "internalType": "uint16", "name": "defaultTokenFeeUSDCents", "type": "uint16" }, + { "internalType": "uint32", "name": "defaultTokenDestGasOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "enforceOutOfOrder", "type": "bool" } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "networkFeeUSDCents", "type": "uint32" }, + { "internalType": "uint64", "name": "gasMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "uint64", "name": "premiumMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeConfig", + "type": "tuple[]" + } + ], + "name": "FeeConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "nop", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "NopPaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "nopWeightsTotal", "type": "uint256" }, + { + "components": [ + { "internalType": "address", "name": "nop", "type": "address" }, + { "internalType": "uint16", "name": "weight", "type": "uint16" } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "name": "NopsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address[]", "name": "tokens", "type": "address[]" }], + "name": "TokenTransferFeeConfigDeleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "minFeeUSDCents", "type": "uint32" }, + { "internalType": "uint32", "name": "maxFeeUSDCents", "type": "uint32" }, + { "internalType": "uint16", "name": "deciBps", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint32", "name": "destBytesOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "aggregateRateLimitEnabled", "type": "bool" } + ], + "indexed": false, + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "transferFeeConfig", + "type": "tuple[]" + } + ], + "name": "TokenTransferFeeConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "tokens", "type": "uint256" }], + "name": "TokensConsumed", + "type": "event" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "currentRateLimiterState", + "outputs": [ + { + "components": [ + { "internalType": "uint128", "name": "tokens", "type": "uint128" }, + { "internalType": "uint32", "name": "lastUpdated", "type": "uint32" }, + { "internalType": "bool", "name": "isEnabled", "type": "bool" }, + { "internalType": "uint128", "name": "capacity", "type": "uint128" }, + { "internalType": "uint128", "name": "rate", "type": "uint128" } + ], + "internalType": "struct RateLimiter.TokenBucket", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint64", "name": "destChainSelector", "type": "uint64" }, + { + "components": [ + { "internalType": "bytes", "name": "receiver", "type": "bytes" }, + { "internalType": "bytes", "name": "data", "type": "bytes" }, + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { "internalType": "address", "name": "feeToken", "type": "address" }, + { "internalType": "bytes", "name": "extraArgs", "type": "bytes" } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + }, + { "internalType": "uint256", "name": "feeTokenAmount", "type": "uint256" }, + { "internalType": "address", "name": "originalSender", "type": "address" } + ], + "name": "forwardFromRouter", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDynamicConfig", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "uint16", "name": "maxNumberOfTokensPerMsg", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerPayloadByte", "type": "uint16" }, + { "internalType": "uint32", "name": "destDataAvailabilityOverheadGas", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerDataAvailabilityByte", "type": "uint16" }, + { "internalType": "uint16", "name": "destDataAvailabilityMultiplierBps", "type": "uint16" }, + { "internalType": "address", "name": "priceRegistry", "type": "address" }, + { "internalType": "uint32", "name": "maxDataBytes", "type": "uint32" }, + { "internalType": "uint32", "name": "maxPerMsgGasLimit", "type": "uint32" }, + { "internalType": "uint16", "name": "defaultTokenFeeUSDCents", "type": "uint16" }, + { "internalType": "uint32", "name": "defaultTokenDestGasOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "enforceOutOfOrder", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpectedNextSequenceNumber", + "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint64", "name": "destChainSelector", "type": "uint64" }, + { + "components": [ + { "internalType": "bytes", "name": "receiver", "type": "bytes" }, + { "internalType": "bytes", "name": "data", "type": "bytes" }, + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "internalType": "struct Client.EVMTokenAmount[]", + "name": "tokenAmounts", + "type": "tuple[]" + }, + { "internalType": "address", "name": "feeToken", "type": "address" }, + { "internalType": "bytes", "name": "extraArgs", "type": "bytes" } + ], + "internalType": "struct Client.EVM2AnyMessage", + "name": "message", + "type": "tuple" + } + ], + "name": "getFee", + "outputs": [{ "internalType": "uint256", "name": "feeTokenAmount", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "getFeeTokenConfig", + "outputs": [ + { + "components": [ + { "internalType": "uint32", "name": "networkFeeUSDCents", "type": "uint32" }, + { "internalType": "uint64", "name": "gasMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "uint64", "name": "premiumMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfig", + "name": "feeTokenConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNopFeesJuels", + "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNops", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "nop", "type": "address" }, + { "internalType": "uint16", "name": "weight", "type": "uint16" } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + }, + { "internalType": "uint256", "name": "weightsTotal", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint64", "name": "", "type": "uint64" }, + { "internalType": "contract IERC20", "name": "sourceToken", "type": "address" } + ], + "name": "getPoolBySourceToken", + "outputs": [{ "internalType": "contract IPoolV1", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }], + "name": "getSenderNonce", + "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticConfig", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "linkToken", "type": "address" }, + { "internalType": "uint64", "name": "chainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "destChainSelector", "type": "uint64" }, + { "internalType": "uint64", "name": "defaultTxGasLimit", "type": "uint64" }, + { "internalType": "uint96", "name": "maxNopFeesJuels", "type": "uint96" }, + { "internalType": "address", "name": "prevOnRamp", "type": "address" }, + { "internalType": "address", "name": "rmnProxy", "type": "address" }, + { "internalType": "address", "name": "tokenAdminRegistry", "type": "address" } + ], + "internalType": "struct EVM2EVMOnRamp.StaticConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], + "name": "getSupportedTokens", + "outputs": [{ "internalType": "address[]", "name": "", "type": "address[]" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getTokenLimitAdmin", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "token", "type": "address" }], + "name": "getTokenTransferFeeConfig", + "outputs": [ + { + "components": [ + { "internalType": "uint32", "name": "minFeeUSDCents", "type": "uint32" }, + { "internalType": "uint32", "name": "maxFeeUSDCents", "type": "uint32" }, + { "internalType": "uint16", "name": "deciBps", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint32", "name": "destBytesOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "aggregateRateLimitEnabled", "type": "bool" }, + { "internalType": "bool", "name": "isEnabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfig", + "name": "tokenTransferFeeConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "linkAvailableForPayment", + "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "payNops", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "newAdmin", "type": "address" }], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "uint16", "name": "maxNumberOfTokensPerMsg", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerPayloadByte", "type": "uint16" }, + { "internalType": "uint32", "name": "destDataAvailabilityOverheadGas", "type": "uint32" }, + { "internalType": "uint16", "name": "destGasPerDataAvailabilityByte", "type": "uint16" }, + { "internalType": "uint16", "name": "destDataAvailabilityMultiplierBps", "type": "uint16" }, + { "internalType": "address", "name": "priceRegistry", "type": "address" }, + { "internalType": "uint32", "name": "maxDataBytes", "type": "uint32" }, + { "internalType": "uint32", "name": "maxPerMsgGasLimit", "type": "uint32" }, + { "internalType": "uint16", "name": "defaultTokenFeeUSDCents", "type": "uint16" }, + { "internalType": "uint32", "name": "defaultTokenDestGasOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "enforceOutOfOrder", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.DynamicConfig", + "name": "dynamicConfig", + "type": "tuple" + } + ], + "name": "setDynamicConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "networkFeeUSDCents", "type": "uint32" }, + { "internalType": "uint64", "name": "gasMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "uint64", "name": "premiumMultiplierWeiPerEth", "type": "uint64" }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.FeeTokenConfigArgs[]", + "name": "feeTokenConfigArgs", + "type": "tuple[]" + } + ], + "name": "setFeeTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "nop", "type": "address" }, + { "internalType": "uint16", "name": "weight", "type": "uint16" } + ], + "internalType": "struct EVM2EVMOnRamp.NopAndWeight[]", + "name": "nopsAndWeights", + "type": "tuple[]" + } + ], + "name": "setNops", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bool", "name": "isEnabled", "type": "bool" }, + { "internalType": "uint128", "name": "capacity", "type": "uint128" }, + { "internalType": "uint128", "name": "rate", "type": "uint128" } + ], + "internalType": "struct RateLimiter.Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setRateLimiterConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint32", "name": "minFeeUSDCents", "type": "uint32" }, + { "internalType": "uint32", "name": "maxFeeUSDCents", "type": "uint32" }, + { "internalType": "uint16", "name": "deciBps", "type": "uint16" }, + { "internalType": "uint32", "name": "destGasOverhead", "type": "uint32" }, + { "internalType": "uint32", "name": "destBytesOverhead", "type": "uint32" }, + { "internalType": "bool", "name": "aggregateRateLimitEnabled", "type": "bool" } + ], + "internalType": "struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]", + "name": "tokenTransferFeeConfigArgs", + "type": "tuple[]" + }, + { "internalType": "address[]", "name": "tokensToUseDefaultFeeConfigs", "type": "address[]" } + ], + "name": "setTokenTransferFeeConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "feeToken", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" } + ], + "name": "withdrawNonLinkFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/ccip-js/src/abi/PriceRegistry.json b/packages/ccip-js/src/abi/PriceRegistry.json new file mode 100644 index 0000000..191f915 --- /dev/null +++ b/packages/ccip-js/src/abi/PriceRegistry.json @@ -0,0 +1,234 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "fromToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fromTokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "toToken", + "type": "address" + } + ], + "name": "convertTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "toTokenAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "getDestinationChainGasPrice", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + } + ], + "name": "getTokenAndGasPrices", + "outputs": [ + { + "internalType": "uint224", + "name": "tokenPrice", + "type": "uint224" + }, + { + "internalType": "uint224", + "name": "gasPrice", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenPrice", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "getTokenPrices", + "outputs": [ + { + "components": [ + { + "internalType": "uint224", + "name": "value", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "internalType": "struct Internal.TimestampedPackedUint224[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getValidatedTokenPrice", + "outputs": [ + { + "internalType": "uint224", + "name": "", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "uint224", + "name": "usdPerToken", + "type": "uint224" + } + ], + "internalType": "struct Internal.TokenPriceUpdate[]", + "name": "tokenPriceUpdates", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "destChainSelector", + "type": "uint64" + }, + { + "internalType": "uint224", + "name": "usdPerUnitGas", + "type": "uint224" + } + ], + "internalType": "struct Internal.GasPriceUpdate[]", + "name": "gasPriceUpdates", + "type": "tuple[]" + } + ], + "internalType": "struct Internal.PriceUpdates", + "name": "priceUpdates", + "type": "tuple" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/ccip-js/src/abi/Router.json b/packages/ccip-js/src/abi/Router.json new file mode 100644 index 0000000..e599809 --- /dev/null +++ b/packages/ccip-js/src/abi/Router.json @@ -0,0 +1,493 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "wrappedNative", + "type": "address", + "internalType": "address" + }, + { "name": "armProxy", "type": "address", "internalType": "address" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "MAX_RET_BYTES", + "inputs": [], + "outputs": [{ "name": "", "type": "uint16", "internalType": "uint16" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "applyRampUpdates", + "inputs": [ + { + "name": "onRampUpdates", + "type": "tuple[]", + "internalType": "struct Router.OnRamp[]", + "components": [ + { + "name": "destChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "onRamp", "type": "address", "internalType": "address" } + ] + }, + { + "name": "offRampRemoves", + "type": "tuple[]", + "internalType": "struct Router.OffRamp[]", + "components": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "offRamp", "type": "address", "internalType": "address" } + ] + }, + { + "name": "offRampAdds", + "type": "tuple[]", + "internalType": "struct Router.OffRamp[]", + "components": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "offRamp", "type": "address", "internalType": "address" } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "ccipSend", + "inputs": [ + { + "name": "destinationChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "message", + "type": "tuple", + "internalType": "struct Client.EVM2AnyMessage", + "components": [ + { "name": "receiver", "type": "bytes", "internalType": "bytes" }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "tokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "feeToken", + "type": "address", + "internalType": "address" + }, + { "name": "extraArgs", "type": "bytes", "internalType": "bytes" } + ] + } + ], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "getArmProxy", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getFee", + "inputs": [ + { + "name": "destinationChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "message", + "type": "tuple", + "internalType": "struct Client.EVM2AnyMessage", + "components": [ + { "name": "receiver", "type": "bytes", "internalType": "bytes" }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "tokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "feeToken", + "type": "address", + "internalType": "address" + }, + { "name": "extraArgs", "type": "bytes", "internalType": "bytes" } + ] + } + ], + "outputs": [{ "name": "fee", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOffRamps", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct Router.OffRamp[]", + "components": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "offRamp", "type": "address", "internalType": "address" } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOnRamp", + "inputs": [ + { + "name": "destChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getWrappedNative", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isChainSupported", + "inputs": [{ "name": "chainSelector", "type": "uint64", "internalType": "uint64" }], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isOffRamp", + "inputs": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "offRamp", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "recoverTokens", + "inputs": [ + { + "name": "tokenAddress", + "type": "address", + "internalType": "address" + }, + { "name": "to", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "routeMessage", + "inputs": [ + { + "name": "message", + "type": "tuple", + "internalType": "struct Client.Any2EVMMessage", + "components": [ + { + "name": "messageId", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "sender", "type": "bytes", "internalType": "bytes" }, + { "name": "data", "type": "bytes", "internalType": "bytes" }, + { + "name": "destTokenAmounts", + "type": "tuple[]", + "internalType": "struct Client.EVMTokenAmount[]", + "components": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ] + }, + { + "name": "gasForCallExactCheck", + "type": "uint16", + "internalType": "uint16" + }, + { "name": "gasLimit", "type": "uint256", "internalType": "uint256" }, + { "name": "receiver", "type": "address", "internalType": "address" } + ], + "outputs": [ + { "name": "success", "type": "bool", "internalType": "bool" }, + { "name": "retData", "type": "bytes", "internalType": "bytes" }, + { "name": "gasUsed", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setWrappedNative", + "inputs": [ + { + "name": "wrappedNative", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "typeAndVersion", + "inputs": [], + "outputs": [{ "name": "", "type": "string", "internalType": "string" }], + "stateMutability": "view" + }, + { + "type": "event", + "name": "MessageExecuted", + "inputs": [ + { + "name": "messageId", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + }, + { + "name": "sourceChainSelector", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "offRamp", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "calldataHash", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OffRampAdded", + "inputs": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "offRamp", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OffRampRemoved", + "inputs": [ + { + "name": "sourceChainSelector", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "offRamp", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OnRampSet", + "inputs": [ + { + "name": "destChainSelector", + "type": "uint64", + "indexed": true, + "internalType": "uint64" + }, + { + "name": "onRamp", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferRequested", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "BadARMSignal", "inputs": [] }, + { "type": "error", "name": "FailedToSendValue", "inputs": [] }, + { "type": "error", "name": "InsufficientFeeTokenAmount", "inputs": [] }, + { "type": "error", "name": "InvalidMsgValue", "inputs": [] }, + { + "type": "error", + "name": "InvalidRecipientAddress", + "inputs": [{ "name": "to", "type": "address", "internalType": "address" }] + }, + { + "type": "error", + "name": "OffRampMismatch", + "inputs": [ + { "name": "chainSelector", "type": "uint64", "internalType": "uint64" }, + { "name": "offRamp", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "OnlyOffRamp", "inputs": [] }, + { + "type": "error", + "name": "UnsupportedDestinationChain", + "inputs": [ + { + "name": "destChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ] + } +] diff --git a/packages/ccip-js/src/abi/TokenAdminRegistry.json b/packages/ccip-js/src/abi/TokenAdminRegistry.json new file mode 100644 index 0000000..0c57c60 --- /dev/null +++ b/packages/ccip-js/src/abi/TokenAdminRegistry.json @@ -0,0 +1,485 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "AlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "InvalidTokenPoolToken", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "OnlyAdministrator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "OnlyPendingAdministrator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "OnlyRegistryModuleOrOwner", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "currentAdmin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdministratorTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdministratorTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "previousPool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newPool", + "type": "address" + } + ], + "name": "PoolSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "RegistryModuleAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "RegistryModuleRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "localToken", + "type": "address" + } + ], + "name": "acceptAdminRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "addRegistryModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "startIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxCount", + "type": "uint64" + } + ], + "name": "getAllConfiguredTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "getPools", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenConfig", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "administrator", + "type": "address" + }, + { + "internalType": "address", + "name": "pendingAdministrator", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenPool", + "type": "address" + } + ], + "internalType": "struct TokenAdminRegistry.TokenConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "localToken", + "type": "address" + }, + { + "internalType": "address", + "name": "administrator", + "type": "address" + } + ], + "name": "isAdministrator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "isRegistryModule", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "localToken", + "type": "address" + }, + { + "internalType": "address", + "name": "administrator", + "type": "address" + } + ], + "name": "proposeAdministrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "removeRegistryModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "localToken", + "type": "address" + }, + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "setPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "localToken", + "type": "address" + }, + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdminRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/packages/ccip-js/src/abi/TokenPool.json b/packages/ccip-js/src/abi/TokenPool.json new file mode 100644 index 0000000..35877fa --- /dev/null +++ b/packages/ccip-js/src/abi/TokenPool.json @@ -0,0 +1,655 @@ +[ + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "applyAllowListUpdates", + "inputs": [ + { "name": "removes", "type": "address[]", "internalType": "address[]" }, + { "name": "adds", "type": "address[]", "internalType": "address[]" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "applyChainUpdates", + "inputs": [ + { + "name": "chains", + "type": "tuple[]", + "internalType": "struct TokenPool.ChainUpdate[]", + "components": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "allowed", "type": "bool", "internalType": "bool" }, + { + "name": "outboundRateLimiterConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + }, + { + "name": "inboundRateLimiterConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAllowList", + "inputs": [], + "outputs": [{ "name": "", "type": "address[]", "internalType": "address[]" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getAllowListEnabled", + "inputs": [], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getArmProxy", + "inputs": [], + "outputs": [{ "name": "armProxy", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getCurrentInboundRateLimiterState", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct RateLimiter.TokenBucket", + "components": [ + { "name": "tokens", "type": "uint128", "internalType": "uint128" }, + { + "name": "lastUpdated", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getCurrentOutboundRateLimiterState", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct RateLimiter.TokenBucket", + "components": [ + { "name": "tokens", "type": "uint128", "internalType": "uint128" }, + { + "name": "lastUpdated", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRouter", + "inputs": [], + "outputs": [{ "name": "router", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getSupportedChains", + "inputs": [], + "outputs": [{ "name": "", "type": "uint64[]", "internalType": "uint64[]" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getToken", + "inputs": [], + "outputs": [ + { + "name": "token", + "type": "address", + "internalType": "contract IERC20" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isSupportedChain", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "lockOrBurn", + "inputs": [ + { + "name": "originalSender", + "type": "address", + "internalType": "address" + }, + { "name": "receiver", "type": "bytes", "internalType": "bytes" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" }, + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "extraArgs", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [{ "name": "", "type": "bytes", "internalType": "bytes" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "releaseOrMint", + "inputs": [ + { "name": "originalSender", "type": "bytes", "internalType": "bytes" }, + { "name": "receiver", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" }, + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { "name": "extraData", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setChainRateLimiterConfig", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "outboundConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + }, + { + "name": "inboundConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setRouter", + "inputs": [{ "name": "newRouter", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [{ "name": "interfaceId", "type": "bytes4", "internalType": "bytes4" }], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "AllowListAdd", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AllowListRemove", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Burned", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainAdded", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "outboundRateLimiterConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + }, + { + "name": "inboundRateLimiterConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainConfigured", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "outboundRateLimiterConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + }, + { + "name": "inboundRateLimiterConfig", + "type": "tuple", + "indexed": false, + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainRemoved", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Locked", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Minted", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferRequested", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Released", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RouterUpdated", + "inputs": [ + { + "name": "oldRouter", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newRouter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "AllowListNotEnabled", "inputs": [] }, + { "type": "error", "name": "BadARMSignal", "inputs": [] }, + { + "type": "error", + "name": "CallerIsNotARampOnRouter", + "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }] + }, + { + "type": "error", + "name": "ChainAlreadyExists", + "inputs": [{ "name": "chainSelector", "type": "uint64", "internalType": "uint64" }] + }, + { + "type": "error", + "name": "ChainNotAllowed", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ] + }, + { + "type": "error", + "name": "DisabledNonZeroRateLimit", + "inputs": [ + { + "name": "config", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ] + }, + { + "type": "error", + "name": "InvalidRatelimitRate", + "inputs": [ + { + "name": "rateLimiterConfig", + "type": "tuple", + "internalType": "struct RateLimiter.Config", + "components": [ + { "name": "isEnabled", "type": "bool", "internalType": "bool" }, + { + "name": "capacity", + "type": "uint128", + "internalType": "uint128" + }, + { "name": "rate", "type": "uint128", "internalType": "uint128" } + ] + } + ] + }, + { + "type": "error", + "name": "NonExistentChain", + "inputs": [ + { + "name": "remoteChainSelector", + "type": "uint64", + "internalType": "uint64" + } + ] + }, + { "type": "error", "name": "RateLimitMustBeDisabled", "inputs": [] }, + { + "type": "error", + "name": "SenderNotAllowed", + "inputs": [{ "name": "sender", "type": "address", "internalType": "address" }] + }, + { "type": "error", "name": "ZeroAddressNotAllowed", "inputs": [] } +] diff --git a/packages/ccip-js/src/api.ts b/packages/ccip-js/src/api.ts new file mode 100644 index 0000000..4d22722 --- /dev/null +++ b/packages/ccip-js/src/api.ts @@ -0,0 +1,1212 @@ +import * as Viem from 'viem' + +import { + readContract, + writeContract, + waitForTransactionReceipt, + getLogs, + getTransactionReceipt as getTxReceipt, + getBlockNumber, +} from 'viem/actions' + +import RouterABI from './abi/Router.json' +import OnRampABI from './abi/OnRamp.json' +import IERC20ABI from './abi/IERC20Metadata.json' +import TokenPoolABI from './abi/TokenPool.json' +import PriceRegistryABI from './abi/PriceRegistry.json' +import TokenAdminRegistryABI from './abi/TokenAdminRegistry.json' +import { TRANSFER_STATUS_FROM_BLOCK_SHIFT, ExecutionStateChangedABI } from './config' + +export { IERC20ABI } + +/** An object containing methods for cross-chain transfer management. + * @typedef {Object} Client */ +export interface Client { + /** + * @param {Viem.WalletClient} options.client - A client with access to wallet actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {Viem.Address} options.tokenAddress - The address of the token contract on the source blockchain. + * @param {bigint} options.amount - The amount of tokens to transfer, specified as a `bigint`. + * @param {boolean} options.waitForReceipt - Should you wait the transaction to be included in a block. + * @param {Object} options.writeContractParameters + * - Override the **optional** write contract parameters for the 'approve' method. + * @param {Object} options.waitForTransactionReceiptParameters + * - Override waitForTransactionReceipt parameters. + * @returns {Promise<{ txHash: Viem.Hash, txReceipt?: Viem.TransactionReceipt }>} A promise that resolves + * to an object containing the transaction hash (`txHash`) + * and optionally a transaction receipt (`txReceipt`) + * if options.waitForReceipt was set to `true`. + * These details are used to track and confirm the transfer. + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const walletClient = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum!) + * }) + * + * const { txHash } = await client.approveRouter({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * amount: 1000000000000000000n, + * }); + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const walletClient = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum!) + * }) + * + * const { txHash, txReceipt } = await client.approveRouter({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * amount: 1000000000000000000n, + * waitForReceipt: true, + * }); + */ + approveRouter(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + tokenAddress: Viem.Address + amount: bigint + waitForReceipt?: boolean + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; txReceipt?: Viem.TransactionReceipt }> + + /** Retrieve the allowance of a specified account for a cross-chain transfer. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {Viem.Address} options.tokenAddress - The address of the token contract on the source blockchain. + * @param {Viem.Address} options.account - The address of the account for which the allowance is being queried. + * This is the account that needs to have sufficient allowance to proceed + * with the transfer. + * @returns {Promise} A promise that resolves to a `bigint` representing the amount of + * allowance granted to the specified account. This value indicates + * how much the account is allowed to transfer or spend. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const allowance = await client.getAllowance({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * account: "0x1234567890abcdef1234567890abcdef12345678", + * }) + */ + getAllowance(options: { + client: Viem.Client + routerAddress: Viem.Address + tokenAddress: Viem.Address + account: Viem.Address + }): Promise + + /** + * Retrieve the onRamp contract address from a router contract + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @returns {Promise} - A promise that resolves to a string, representing the onRamp contract address + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const onRampAddress = await client.getOnRampAddress({ + * client: publicClient + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * }) + */ + getOnRampAddress(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + /** Get a list of supported fee tokens for provided lane for the cross-chain transfer. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @returns {Promise} A promise that resolves to an array of ERC-20 token addresses that + * can be used to pee the transfer fee on a given lane. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const feeTokens = await client.getSupportedFeeTokens({ + * client: publicClient + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * }); + */ + getSupportedFeeTokens(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + /** Retrieve the rate refill limits for the specified lane. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @returns {Promise} A promise that resolves to the current state of the + * lane rate limiter, including token balance, capacity, + * and refill rate. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const { tokens, lastUpdated, isEnabled, capacity, rate } = await client.getLaneRateRefillLimits({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * }); + */ + getLaneRateRefillLimits(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + }): Promise + + /** Retrieve the rate refill limits for the specified token. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {number} options.supportedTokenAddress - The address of the token (supported by this lane) to check limits for. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @returns {Promise} A promise that resolves to the current state of the + * lane rate limiter, including token balance, capacity, + * and refill rate. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const { tokens, lastUpdated, isEnabled, capacity, rate } = await client.getTokenRateLimitByLane({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * supportedTokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * }); + */ + getTokenRateLimitByLane(options: { + client: Viem.Client + routerAddress: Viem.Address + supportedTokenAddress: Viem.Address + destinationChainSelector: string + }): Promise + + /** Get the fee required for the cross-chain transfer and/or sending cross-chain message. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {Viem.Address} options.destinationAccount - The address of the recipient account on the destination blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @param {bigint} options.amount - The amount of tokens to transfer, specified as a `bigint`. + * @param {Viem.Address} options.tokenAddress - The address of the token contract on the source blockchain. + * @param {Viem.Address} options.feeTokenAddress - The address of the token used for paying fees. If not specified the chain's native token will be used. + * @param {Viem.Hex} options.data - Arbitrary message to send, ABI encoded + * @param {EVMExtraArgsV2} options.extraArgs - Pass extraArgs. Check [CCIP Docs](https://docs.chain.link/ccip/best-practices#using-extraargs) how to use it + * @returns {Promise} A promise that resolves to a number representing the fee required + * for the transfer. The fee is typically expressed in the smallest unit + * of the token or currency used. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const fee = await client.getFee({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * destinationChainSelector: "1234" + * amount: 1000000000000000000n, + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * }); + */ + getFee(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationAccount: Viem.Address + destinationChainSelector: string + amount?: bigint + tokenAddress?: Viem.Address + feeTokenAddress?: Viem.Address + data?: Viem.Hex + extraArgs?: EVMExtraArgsV2 + }): Promise + + /** * Retrieve the token admin registry contract address from an onRamp contract + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @param {Viem.Address} options.tokenAddress - The address of the token contract on the source blockchain. + * @returns {Promise} A promise that resolves to a boolean value indicating whether the token + * is supported on the destination chain. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const tokenAdminRegistryAddress = await client.getTokenAdminRegistry({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * }); + */ + getTokenAdminRegistry(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + tokenAddress: Viem.Address + }): Promise + + /** Check if the token is supported on the destination chain. + * @param {Viem.Client} options.client - A client with access to public actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @param {Viem.Address} options.tokenAddress - The address of the token contract on the source blockchain. + * @returns {Promise} A promise that resolves to a boolean value indicating whether the token + * is supported on the destination chain. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const isTokenSupported = await client.isTokenSupported({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * }); + */ + isTokenSupported(options: { + client: Viem.Client + routerAddress: Viem.Address + destinationChainSelector: string + tokenAddress: Viem.Address + }): Promise + + /** Initiate the token transfer and returns the transaction hash and message ID. + * @param {Viem.WalletClient} options.client - A client with access to wallet actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @param {bigint} options.amount - Amount to transfer. + * @param {Viem.Address} options.destinationAccount - Address of recipient. + * @param {Viem.Address} options.tokenAddress - Address of transferred token. + * @param {Viem.Address} options.feeTokenAddress - The address of the token used for paying fees. If not specified the chain's native token will be used. + * @param {Viem.Hex} options.data - Arbitrary data to send along with the transaction. ABI encoded + * @param {EVMExtraArgsV2} options.extraArgs - Pass extraArgs. Check [CCIP Docs](https://docs.chain.link/ccip/best-practices#using-extraargs) how to use it + * @param {Object} options.writeContractParameters + * - Override the **optional** write contract parameters for the 'approve' method. + * @param {Object} options.waitForTransactionReceiptParameters + * - Override waitForTransactionReceipt parameters. + * @returns {Promise<{ txHash: Viem.Hash, messageId: Viem.Hash, txReceipt?: Viem.TransactionReceipt }>} A promise + * that resolves to an object containing the transaction + * hash (`txHash`) and message ID (`messageId`) and transaction + * receipt (`txReceipt`). + * These details are used to track and confirm the transfer. + * @example + * import { createWalletClient, custom, encodeAbiParameters } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const walletClient = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum!) + * }) + * + * const { txHash, messageId } = await client.transferTokens({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * amount: 1000000000000000000n, + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * data: encodeAbiParameters([{ type: 'string', name: 'data' }], ["Hello"]) + * }); + * + */ + transferTokens(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + amount: bigint + destinationAccount: Viem.Address + tokenAddress: Viem.Address + feeTokenAddress?: Viem.Address + data?: Viem.Hex + extraArgs?: EVMExtraArgsV2 + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> + + /** Send arbitrary message through CCIP. The message should be ABI encoded data. + * It can be encoded via `viem`'s `encodeAbiParameters` data. + * Check [encodeAbiParameters](https://viem.sh/docs/abi/encodeAbiParameters.html) and [ABI specification](https://docs.soliditylang.org/en/latest/abi-spec.html) for more information + * @param {Viem.WalletClient} options.client - A client with access to wallet actions on the source blockchain. + * @param {Viem.Address} options.routerAddress - The address of the router contract on the source blockchain. + * @param {string} options.destinationChainSelector - The selector for the destination chain. + * @param {Viem.Address} options.destinationAccount - Address of recipient. + * @param {Viem.Address} options.feeTokenAddress - The address of the token used for paying fees. If not specified the chain's native token will be used. + * @param {Viem.Hex} options.data - Arbitrary message to send, ABI encoded + * @param {EVMExtraArgsV2} options.extraArgs - Pass extraArgs. Check [CCIP Docs](https://docs.chain.link/ccip/best-practices#using-extraargs) how to use it + * @param {Object} options.writeContractParameters + * - Override the **optional** write contract parameters for the 'approve' method. + * @param {Object} options.waitForTransactionReceiptParameters + * - Override waitForTransactionReceipt parameters. + * @returns {Promise<{ txHash: Viem.Hash, messageId: Viem.Hash, txReceipt?: Viem.TransactionReceipt }>} A promise + * that resolves to an object containing the transaction + * hash (`txHash`) and message ID (`messageId`) and transaction + * receipt (`txReceipt`). + * These details are used to track and confirm the transfer. + * @example + * import { createWalletClient, custom, encodeAbiParameters, encodeFunctionData, erc20Abi, parseEther } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const walletClient = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum!) + * }) + * + * //Send string message + * const { txHash, messageId } = await client.transferTokens({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * data: encodeAbiParameters([{ type: 'string', name: 'data' }], ["Hello"]) + * }); + * + * //Send function data + * const { txHash, messageId } = await client.transferTokens({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * destinationChainSelector: "1234" + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * data: encodeFunctionData({ + * abi: erc20Abi, + * functionName: 'transfer', + * args: ["0x1234567890abcdef1234567890abcdef12345678", parseEther('0.1')], + * }) + * }); + * + */ + sendCCIPMessage(options: { + client: Viem.WalletClient + routerAddress: Viem.Address + destinationChainSelector: string + destinationAccount: Viem.Address + feeTokenAddress?: Viem.Address + data?: Viem.Hex + extraArgs?: EVMExtraArgsV2 + writeContractParameters?: Partial<{ + gas: bigint + gasPrice: bigint + nonce: number + }> + waitForTransactionReceiptParameters?: Partial<{ + confirmations: number + pollingInterval: number + }> + }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }> + + /** Retrieve the status of a cross-chain transfer based on the message ID. + * @param {Viem.Client} options.client - A client with access to public actions on the destination blockchain. + * @param {Viem.Address} options.destinationRouterAddress - The address of the router contract on the destination blockchain. + * @param {Viem.Chain} options.destinationChain - The destination blockchain. + * @param {string} options.sourceChainSelector - The selector for the source chain. + * @param {Viem.Hash} options.messageId - The unique identifier of the cross-chain transfer message. + * @param {bigint} options.fromBlockNumber - The block number to start searching for logs. + * @returns {Promise} A promise that resolves to the status of the cross-chain transfer. + * @example + * import { createPublicClient, http } from 'viem' + * import { polygon } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: polygon, + * transport: http() + * }) + * + * const status = await client.getTransferStatus({ + * client: publicClient, + * destinationRouterAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * sourceChainSelector: "1234", + * messageId: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * }); + */ + getTransferStatus(options: { + client: Viem.Client + destinationRouterAddress: Viem.Address + sourceChainSelector: string + messageId: Viem.Hash + fromBlockNumber?: bigint + }): Promise + + /** Retrieve the transaction receipt based on the transaction hash. + * @param {Viem.Client} options.client - A client with access to public actions + * @param {Viem.Hash} options.hash - The transaction hash of the cross-chain transfer. This unique identifier + * is used to locate and retrieve the transaction receipt. + * @returns {Promise} A promise that resolves to the transaction + * receipt. The receipt contains detailed information about the transaction, + * including its status, gas usage, logs, and other relevant data. + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const receipt = await client.getTransactionReceipt({ + * client: publicClient, + * hash: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef" + * }); + */ + getTransactionReceipt(options: { client: Viem.Client; hash: Viem.Hash }): Promise +} + +/** + * Creates a client for managing cross-chain transfers, configured for either testnet or mainnet environments. + * + * The `createClient` function initializes and returns a client object tailored for interacting with cross-chain + * transfer functionalities. The function also allows for custom CCIP routers to be provided, with + * sensible defaults based on the environment. + * + * @function createClient + * @returns {Client} The client object with methods for cross-chain transfer management. + * + * @example + * // Example usage of createClient function + * import * as CCIP from '@chainlink/ccip-js'; + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const ccipClient = CCIP.createClient(); + * + * const publicClient = createPublicClient({ + * chain: mainnet, + * transport: http() + * }) + * + * const walletClient = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum!) + * }) + * + * const { txHash, txReceipt } = await ccipClient.approve({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * amount: 1000000000000000000n, + * waitForReceipt: true, + * }); + * + * console.log(`Transfer approved. Transaction hash: ${txHash}. Transaction receipt: ${txReceipt}`); + * + * const fee = await ccipClient.getFee({ + * client: publicClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * amount: 1000000000000000000n, + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * destinationChainSelector: "1234" + * }); + * + * console.log(`Fee: ${fee.toLocaleString()}`); + * + * const { txHash, messageId } = await client.transferTokens({ + * client: walletClient, + * routerAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * tokenAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + * amount: 1000000000000000000n, + * destinationAccount: "0x1234567890abcdef1234567890abcdef12345678", + * destinationChainSelector: "1234" + * }); + * + * console.log(`Transfer success. Transaction hash: ${txHash}. Message ID: ${messageId}`) + */ +export const createClient = (): Client => { + return { + approveRouter, + getAllowance, + getOnRampAddress, + getSupportedFeeTokens, + getLaneRateRefillLimits, + getTokenRateLimitByLane, + getFee, + getTokenAdminRegistry, + isTokenSupported, + transferTokens, + sendCCIPMessage, + getTransferStatus, + getTransactionReceipt, + } + + async function approveRouter(options: Parameters[0]) { + checikIsWalletAccountValid(options) + + checkIsAddressValid( + options.routerAddress, + `PARAMETER INPUT ERROR: Router address ${options.routerAddress} is not valid`, + ) + checkIsAddressValid( + options.tokenAddress, + `PARAMETER INPUT ERROR: Token address ${options.tokenAddress} is not valid`, + ) + + if (options.amount < BigInt(0)) { + throw new Error('PARAMETER INPUT ERROR: Invalid approve amount. Amount can not be negative') + } + + const approveTxHash = await writeContract(options.client, { + chain: options.client.chain, + account: options.client.account!.address, + abi: IERC20ABI, + address: options.tokenAddress, + functionName: 'approve', + args: [options.routerAddress, options.amount], + ...options.writeContractParameters, + }) + + if (!options.waitForReceipt) { + return { txHash: approveTxHash } + } + + const txReceipt = await waitForTransactionReceipt(options.client, { + hash: approveTxHash, + confirmations: 2, + ...options.waitForTransactionReceiptParameters, + }) + + return { txHash: approveTxHash, txReceipt: txReceipt as Viem.TransactionReceipt } + } + + async function getAllowance(options: Parameters[0]) { + checkIsAddressValid( + options.routerAddress, + `PARAMETER INPUT ERROR: Router address ${options.routerAddress} is not valid`, + ) + checkIsAddressValid( + options.tokenAddress, + `PARAMETER INPUT ERROR: Token address ${options.tokenAddress} is not valid`, + ) + checkIsAddressValid(options.account, `PARAMETER INPUT ERROR: Account address ${options.account} is not valid`) + + const allowance = await readContract(options.client, { + abi: IERC20ABI, + address: options.tokenAddress, + functionName: 'allowance', + args: [options.account, options.routerAddress], + }) + return allowance as bigint + } + + async function getOnRampAddress(options: Parameters[0]) { + checkIsAddressValid( + options.routerAddress, + `PARAMETER INPUT ERROR: Router address ${options.routerAddress} is not valid`, + ) + + const onRampAddress = (await readContract(options.client, { + abi: RouterABI, + address: options.routerAddress, + functionName: 'getOnRamp', + args: [options.destinationChainSelector], + })) as Viem.Address + + checkIsAddressValid( + onRampAddress, + 'CONTRACT CALL ERROR: OnRamp address is not valid. Execution can not be continued', + ) + + return onRampAddress + } + + async function getSupportedFeeTokens(options: Parameters[0]) { + const onRampAddress = await getOnRampAddress(options) + + const dynamicConfig = await readContract(options.client, { + abi: OnRampABI, + address: onRampAddress, + functionName: 'getDynamicConfig', + }) + + const priceRegistry = (dynamicConfig as DynamicConfig).priceRegistry + + checkIsAddressValid( + priceRegistry, + 'CONTRACT CALL ERROR: Price regisry is not valid. Execution can not be continued', + ) + + const feeTokens = await readContract(options.client, { + abi: PriceRegistryABI, + address: priceRegistry, + functionName: 'getFeeTokens', + }) + return feeTokens as Viem.Address[] + } + + async function getLaneRateRefillLimits(options: Parameters[0]) { + const onRampAddress = await getOnRampAddress(options) + + const currentRateLimiterState = await readContract(options.client, { + abi: OnRampABI, + address: onRampAddress, + functionName: 'currentRateLimiterState', + }) + return currentRateLimiterState as RateLimiterState + } + + async function getTokenRateLimitByLane(options: Parameters[0]) { + checkIsAddressValid( + options.supportedTokenAddress, + `PARAMETER INPUT ERROR: Token address ${options.supportedTokenAddress} is not valid. Execution can not be continued`, + ) + + const onRampAddress = await getOnRampAddress(options) + + const laneTokenTransferPool = (await readContract(options.client, { + abi: OnRampABI, + address: onRampAddress, + functionName: 'getPoolBySourceToken', + args: [options.destinationChainSelector, options.supportedTokenAddress], + })) as Viem.Address + + checkIsAddressValid( + laneTokenTransferPool, + `CONTRACT CALL ERROR: Token pool for ${options.supportedTokenAddress} is missing. Execution can not be continued`, + ) + + const transferPoolTokenOutboundLimit = await readContract(options.client, { + abi: TokenPoolABI, + address: laneTokenTransferPool as Viem.Address, + functionName: 'getCurrentOutboundRateLimiterState', + args: [options.destinationChainSelector], + }) + + return transferPoolTokenOutboundLimit as RateLimiterState + } + + async function getFee(options: Parameters[0]) { + checkIsAddressValid( + options.routerAddress, + `PARAMETER INPUT ERROR: Router address ${options.routerAddress} is not valid`, + ) + + if (options.amount && options.amount < BigInt(0)) { + throw new Error('PARAMETER INPUT ERROR: Invalid amount. Amount can not be negative') + } + + if (!Viem.isAddress(options.destinationAccount)) { + throw new Error( + `PARAMETER INPUT ERROR: ${options.destinationAccount} is not a valid destionation account address`, + ) + } + + if (options.tokenAddress) { + checkIsAddressValid( + options.tokenAddress, + `PARAMETER INPUT ERROR: Token address ${options.tokenAddress} is not valid`, + ) + } + + if (options.feeTokenAddress) { + if (!Viem.isAddress(options.feeTokenAddress)) { + throw new Error(`PARAMETER INPUT ERROR: ${options.feeTokenAddress} is not a valid fee token address`) + } + } + + return (await readContract(options.client, { + abi: RouterABI, + address: options.routerAddress, + functionName: 'getFee', + args: buildArgs(options), + })) as bigint + } + + async function getTokenAdminRegistry(options: Parameters[0]) { + if (!Viem.isAddress(options.tokenAddress) || Viem.isAddressEqual(options.tokenAddress, Viem.zeroAddress)) { + throw new Error(`PARAMETER INPUT ERROR: Token address ${options.tokenAddress} is not valid`) + } + + const onRampAddress = await getOnRampAddress(options) + + const staticConfig = await readContract(options.client, { + abi: OnRampABI, + address: onRampAddress, + functionName: 'getStaticConfig', + }) + + const tokenAdminRegistryAddress = (staticConfig as StaticConfig).tokenAdminRegistry + + checkIsAddressValid( + tokenAdminRegistryAddress, + 'CONTRACT CALL ERROR: Token admin registry address is not valid. Execution can not be continued', + ) + return tokenAdminRegistryAddress + } + + async function isTokenSupported(options: Parameters[0]) { + const tokenAdminRegistryAddress = await getTokenAdminRegistry(options) + + const tokenPoolAddress = (await readContract(options.client, { + abi: TokenAdminRegistryABI, + address: tokenAdminRegistryAddress, + functionName: 'getPool', + args: [options.tokenAddress], + })) as Viem.Address + + if (!Viem.isAddress(tokenPoolAddress) || Viem.isAddressEqual(tokenPoolAddress, Viem.zeroAddress)) { + return false + } + + const isSupported = (await readContract(options.client, { + abi: TokenPoolABI, + address: tokenPoolAddress, + functionName: 'isSupportedChain', + args: [options.destinationChainSelector], + })) as boolean + + return isSupported + } + + async function transferTokens(options: Parameters[0]) { + checikIsWalletAccountValid(options) + + if (!options.amount || options.amount <= BigInt(0)) { + throw new Error('PARAMETER INPUT ERROR: Invalid amount. Amount must be greater than 0') + } + + if (!Viem.isAddress(options.destinationAccount)) { + throw new Error( + `PARAMETER INPUT ERROR: ${options.destinationAccount} is not a valid destionation account address`, + ) + } + + if (options.feeTokenAddress) { + if (!Viem.isAddress(options.feeTokenAddress)) { + throw new Error(`PARAMETER INPUT ERROR: ${options.feeTokenAddress} is not a valid fee token address`) + } + } + + const writeContractParameters = { + chain: options.client.chain, + abi: RouterABI, + address: options.routerAddress, + functionName: 'ccipSend', + args: buildArgs(options), + account: options.client.account!.address, + ...(!options.feeTokenAddress && { + value: await getFee(options), + }), + ...options.writeContractParameters, + } + + const transferTokensTxHash = await writeContract(options.client, writeContractParameters) + + const txReceipt = await waitForTransactionReceipt(options.client, { + hash: transferTokensTxHash, + confirmations: 2, + ...options.waitForTransactionReceiptParameters, + }) + + const parsedLog = Viem.parseEventLogs({ + abi: OnRampABI, + logs: txReceipt.logs, + eventName: 'CCIPSendRequested', + }) as CCIPTrasnferReceipt[] + + const messageId = parsedLog[0]?.args?.message?.messageId + if (!messageId) { + throw new Error('EVENTS LOG ERROR: Message ID not found in the transaction logs') + } + + return { + txHash: transferTokensTxHash, + messageId: messageId, + txReceipt: txReceipt as Viem.TransactionReceipt, + } + } + + async function sendCCIPMessage(options: Parameters[0]) { + checikIsWalletAccountValid(options) + checkIsAddressValid(options.routerAddress, `Router address ${options.routerAddress} is not valid`) + + if (!Viem.isAddress(options.destinationAccount)) { + throw new Error(`${options.destinationAccount} is not a valid destionation account address`) + } + + if (options.feeTokenAddress) { + if (!Viem.isAddress(options.feeTokenAddress)) { + throw new Error(`PARAMETER INPUT ERROR: ${options.feeTokenAddress} is not a valid fee token address`) + } + } + + const writeContractParameters = { + chain: options.client.chain, + abi: RouterABI, + address: options.routerAddress, + functionName: 'ccipSend', + args: buildArgs(options), + account: options.client.account!.address, + ...(!options.feeTokenAddress && { + value: await getFee(options), + }), + ...options.writeContractParameters, + } + + const transferTokensTxHash = await writeContract(options.client, writeContractParameters) + + const txReceipt = await waitForTransactionReceipt(options.client, { + hash: transferTokensTxHash, + confirmations: 2, + ...options.waitForTransactionReceiptParameters, + }) + + const parsedLog = Viem.parseEventLogs({ + abi: OnRampABI, + logs: txReceipt.logs, + eventName: 'CCIPSendRequested', + }) as CCIPTrasnferReceipt[] + + const messageId = parsedLog[0]?.args?.message?.messageId + if (!messageId) { + throw new Error('EVENTS LOG ERROR: Message ID not found in the transaction logs') + } + + return { + txHash: transferTokensTxHash, + messageId: messageId, + txReceipt: txReceipt as Viem.TransactionReceipt, + } + } + + async function getTransferStatus(options: Parameters[0]) { + checkIsAddressValid( + options.destinationRouterAddress, + `PARAMETER INPUT ERROR: Destination router address ${options.destinationRouterAddress} is not valid`, + ) + + if (!Viem.isHash(options.messageId)) { + throw new Error(`PARAMETER INPUT ERROR: ${options.messageId} is not a valid message ID`) + } + if (!options.sourceChainSelector) { + throw new Error('PARAMETER INPUT ERROR: Source chain selector is missing or invalid') + } + + const offRamps = (await readContract(options.client, { + abi: RouterABI, + address: options.destinationRouterAddress, + functionName: 'getOffRamps', + })) as OffRamp[] + + const matchingOffRamps = offRamps.filter( + (offRamp) => String(offRamp.sourceChainSelector) === options.sourceChainSelector, + ) + if (matchingOffRamps.length === 0) { + throw new Error('CONTRACT CALL ERROR: No matching off-ramp found') + } + + let fromBlock = options.fromBlockNumber + if (!fromBlock) { + const blockNumber = await getBlockNumber(options.client) + fromBlock = blockNumber - BigInt(TRANSFER_STATUS_FROM_BLOCK_SHIFT) + } + for (const offRamp of matchingOffRamps) { + const logs = await getLogs(options.client, { + event: ExecutionStateChangedABI, + address: offRamp.offRamp, + args: { messageId: options.messageId }, + fromBlock, + }) + if (logs && logs.length > 0) { + const { state } = logs[0].args + if (state) return state as TransferStatus + } + } + return null + } + + async function getTransactionReceipt( + options: Parameters[0], + ): Promise { + if (!Viem.isHash(options.hash)) { + throw new Error(`PARAMETER INPUT ERROR: ${options.hash} is not a valid transaction hash`) + } + return await getTxReceipt(options.client, { hash: options.hash }) + } + + function buildArgs(options: { + amount?: bigint + destinationChainSelector: string + destinationAccount: Viem.Address + tokenAddress?: Viem.Address + feeTokenAddress?: Viem.Address + data?: Viem.Hex + extraArgs?: EVMExtraArgsV2 + }) { + const { + destinationAccount, + destinationChainSelector, + tokenAddress, + amount, + feeTokenAddress, + data, + extraArgs: evmExtraArgsV2, + } = options + const gasLimit = BigInt(evmExtraArgsV2?.gasLimit ?? 0) + // Controls the execution order of your messages on the destination blockchain. + // Setting this to true allows messages to be executed in any order. Setting it to false + // ensures messages are executed in sequence, so a message will only be executed if the + // preceeding one has been executed. On lanes where `Out of Order Execution` is required, + // you must set this to true; otherwise, the transaction will revert. + const allowOutOfOrderExecution = evmExtraArgsV2?.allowOutOfOrderExecution === false ? false : true + const extraArgsEncoded = Viem.encodeAbiParameters( + [ + { type: 'uint256', name: 'gasLimit' }, + { type: 'bool', name: 'allowOutOfOrderExecution' }, + ], + [gasLimit, allowOutOfOrderExecution], + ) + const evmExtraArgsV2Tag = '0x181dcf10' + const extraArgs = evmExtraArgsV2Tag + extraArgsEncoded.slice(2) + return [ + destinationChainSelector, + { + receiver: Viem.encodeAbiParameters([{ type: 'address', name: 'receiver' }], [destinationAccount]), + data: data ?? Viem.zeroHash, + tokenAmounts: amount && tokenAddress ? [{ token: tokenAddress, amount }] : [], + feeToken: feeTokenAddress || Viem.zeroAddress, + extraArgs, + }, + ] + } + + function checkIsAddressValid(address: Viem.Address, errorMessage?: string) { + if (!Viem.isAddress(address) || Viem.isAddressEqual(address, Viem.zeroAddress)) { + throw new Error(errorMessage) + } + } + + function checikIsWalletAccountValid(options: { client: Viem.Client }) { + if (!options.client.account) { + throw new Error('WALLET ERROR: account is not valid') + } + + checkIsAddressValid(options.client.account.address, 'WALLET ERROR: account address is not valid') + } +} + +/** + * Represents the state of a rate limiter using a token bucket algorithm. + * + * @typedef {Object} RateLimiterState + * @prop {bigint} tokens - Current number of tokens that are in the bucket. + * This represents the available capacity for requests. + * @prop {number} lastUpdated - Timestamp in seconds of the last token refill, + * allows tracking token consumption over time. + * This is designed to be accurate for over 100 years. + * @prop {boolean} isEnabled - Indicates whether the rate limiting feature is enabled or disabled. + * @prop {bigint} capacity - Maximum number of tokens that can be in the bucket, + * representing the total capacity of the limiter. + * @prop {bigint} rate - The rate at which tokens are refilled in the bucket, + * typically measured in tokens per second. + */ +export interface RateLimiterState { + tokens: bigint + lastUpdated: number + isEnabled: boolean + capacity: bigint + rate: bigint +} + +/** + * Configuration settings for dynamic aspects of cross-chain transfers. + * + * The `DynamicConfig` type defines the structure of an object that holds various + * dynamic configuration parameters for cross-chain transactions. These settings + * are used to control the behavior and limits of transfers, such as gas calculations, + * data availability, and message size constraints. + * + * @typedef {Object} DynamicConfig + * @property {Viem.Address} router - The address of the router responsible for handling + * the cross-chain transfers. This address is used to + * route the transaction through the correct path. + * @property {number} maxNumberOfTokensPerMsg - The maximum number of tokens that can be + * included in a single message. This parameter + * limits the token batch size in cross-chain + * transfers to prevent overly large transactions. + * @property {number} destGasOverhead - The overhead in gas that is added to the destination + * chain to account for base transaction costs. This value + * helps ensure that the transaction has enough gas to + * cover additional overhead on the destination chain. + * @property {number} destGasPerPayloadByte - The amount of gas required per byte of payload + * on the destination chain. This parameter is used + * to calculate the total gas needed based on the + * size of the payload being transferred. + * @property {number} destDataAvailabilityOverheadGas - The additional gas required on the + * destination chain to account for data + * availability overhead. This value is used + * to ensure that enough gas is allocated + * for the availability of the data being + * transferred. + * @property {number} destGasPerDataAvailabilityByte - The gas cost per byte of data availability + * on the destination chain. This parameter + * contributes to the overall gas calculation + * for data availability during the transfer. + * @property {number} destDataAvailabilityMultiplierBps - The multiplier in basis points (bps) + * applied to the data availability gas + * cost. This value is used to adjust the + * cost of data availability by applying + * a scaling factor. + * @property {Viem.Address} priceRegistry - The address of the price registry used to obtain + * pricing information for gas and other costs during + * the transfer. This registry helps ensure that the + * correct prices are applied to the transaction. + * @property {number} maxDataBytes - The maximum number of data bytes that can be included in + * a single message. This parameter limits the size of the + * data payload to prevent excessive data in one transfer. + * @property {number} maxPerMsgGasLimit - The maximum gas limit that can be applied to a single + * message. This parameter ensures that the transaction + * does not exceed a certain gas threshold, preventing + * overly costly operations. + */ +export type DynamicConfig = { + router: Viem.Address + maxNumberOfTokensPerMsg: number + destGasOverhead: number + destGasPerPayloadByte: number + destDataAvailabilityOverheadGas: number + destGasPerDataAvailabilityByte: number + destDataAvailabilityMultiplierBps: number + priceRegistry: Viem.Address + maxDataBytes: number + maxPerMsgGasLimit: number + defaultTokenFeeUSDCents: number + defaultTokenDestGasOverhead: number + enforceOutOfOrder: boolean +} + +/** + * Configuration settings for static aspects of cross-chain transfers. + * + * The `StaticConfig` type defines the structure of an object that holds various + * static configuration parameters for cross-chain transactions. + * + * @typedef {Object} StaticConfig + * + * @property {bigint} chainSelector - The selector for the source chain. + * @property {bigint} defaultTxGasLimit - Default gas limit per transaction. + * @property {bigint} destChainSelector - The selector for the destination chain. + * @property {Viem.Address} linkToken - The address of the LINK token on the source chain. + * @property {bigint} maxNopFeesJuels - Maxium nop fee in juels. + * @property {bigint} maxNopFeesJuels - Maxium nop fee in juels. + * @property {Viem.Address} prevOnRamp - Previous onRamp contract address. + * @property {Viem.Address} rmnProxy - RMN proxy contract address. + * @property {Viem.Address} tokenAdminRegistryAddress - The address of the token admin registry contract. + */ +export type StaticConfig = { + chainSelector: bigint + defaultTxGasLimit: bigint + destChainSelector: bigint + linkToken: Viem.Address + maxNopFeesJuels: bigint + prevOnRamp: Viem.Address + rmnProxy: Viem.Address + tokenAdminRegistry: Viem.Address +} + +/** + * Represents the off-ramp configuration for a cross-chain transfer. + * + * @typedef {Object} OffRamp + * @property {Viem.Address} offRamp - The address of the off-ramp contract on the destination blockchain. + * @property {bigint} sourceChainSelector - The selector for the source chain. + */ +export type OffRamp = { + offRamp: Viem.Address + sourceChainSelector: bigint +} + +/** + * Represents the transaction status of a cross-chain transfer. + */ +export enum TransferStatus { + Untouched = 0, + InProgress = 1, + Success = 2, + Failure = 3, +} + +/** + * Extends the Viem.Log type to fetch cross-chain trasnfer messageId. + */ +export type CCIPTrasnferReceipt = Viem.Log & { + args: { + message?: { + messageId?: Viem.Hash + } + } +} + +export type EVMExtraArgsV2 = { + gasLimit?: number + allowOutOfOrderExecution?: boolean +} diff --git a/packages/ccip-js/src/config.ts b/packages/ccip-js/src/config.ts new file mode 100644 index 0000000..ca92344 --- /dev/null +++ b/packages/ccip-js/src/config.ts @@ -0,0 +1,39 @@ +/** + * The default number of blocks to shift when querying logs for transfers. + */ +export const TRANSFER_STATUS_FROM_BLOCK_SHIFT = 100 + +/** + * The ABI for the transfer status event on the off-ramp contract. + * @dev This is used because of the enum type in the event. + */ +export const ExecutionStateChangedABI = { + type: 'event', + name: 'ExecutionStateChanged', + inputs: [ + { + name: 'sequenceNumber', + type: 'uint64', + indexed: true, + internalType: 'uint64', + }, + { + name: 'messageId', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'state', + type: 'uint8', + indexed: false, + internalType: 'enum Internal.MessageExecutionState', + }, + { + name: 'returnData', + type: 'bytes', + indexed: false, + internalType: 'bytes', + }, + ], +} as const diff --git a/packages/ccip-js/src/contracts/BridgeToken.sol b/packages/ccip-js/src/contracts/BridgeToken.sol new file mode 100644 index 0000000..d435d1f --- /dev/null +++ b/packages/ccip-js/src/contracts/BridgeToken.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.24; + +import {BurnMintERC677} from '@chainlink/contracts-ccip/src/v0.8/shared/token/ERC677/BurnMintERC677.sol'; + +/// @title BridgeToken +/// @notice This contract extends the functionality of the BurnMintERC677 token contract to include a `drip` function that mints one full token to a specified address. +/// @dev Inherits from the BurnMintERC677 contract and sets the token name, symbol, decimals, and initial supply in the constructor. +contract BridgeToken is BurnMintERC677 { + /** + * @notice Constructor to initialize the BridgeToken contract with a name and symbol. + * @dev Calls the parent constructor of BurnMintERC677 with fixed decimals (18) and initial supply (0). + * @param name - The name of the token. + * @param symbol - The symbol of the token. + */ + constructor(string memory name, string memory symbol) BurnMintERC677(name, symbol, 18, 0) {} + + /** + * @notice Mints one full token (1e18) to the specified address. + * @dev Calls the internal `_mint` function from the BurnMintERC677 contract. + * @param to - The address to receive the minted token. + */ + function drip(address to) external { + _mint(to, 1e18); + } +} diff --git a/packages/ccip-js/src/contracts/CCIPLocalSimulator.sol b/packages/ccip-js/src/contracts/CCIPLocalSimulator.sol new file mode 100644 index 0000000..5f70fa0 --- /dev/null +++ b/packages/ccip-js/src/contracts/CCIPLocalSimulator.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {CCIPLocalSimulator} from '@chainlink/local/src/ccip/CCIPLocalSimulator.sol'; diff --git a/packages/ccip-js/src/contracts/EVM2EVMOffRamp.sol b/packages/ccip-js/src/contracts/EVM2EVMOffRamp.sol new file mode 100644 index 0000000..6fa0b66 --- /dev/null +++ b/packages/ccip-js/src/contracts/EVM2EVMOffRamp.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {EVM2EVMOffRamp} from "@chainlink/contracts-ccip/src/v0.8/ccip/offRamp/EVM2EVMOffRamp.sol"; \ No newline at end of file diff --git a/packages/ccip-js/src/contracts/EVM2EVMOnRamp.sol b/packages/ccip-js/src/contracts/EVM2EVMOnRamp.sol new file mode 100644 index 0000000..3e74b8e --- /dev/null +++ b/packages/ccip-js/src/contracts/EVM2EVMOnRamp.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {EVM2EVMOnRamp} from '@chainlink/contracts-ccip/src/v0.8/ccip/onRamp/EVM2EVMOnRamp.sol'; diff --git a/packages/ccip-js/src/contracts/PriceRegistry.sol b/packages/ccip-js/src/contracts/PriceRegistry.sol new file mode 100644 index 0000000..0802069 --- /dev/null +++ b/packages/ccip-js/src/contracts/PriceRegistry.sol @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.24; + +import {IPriceRegistry} from '@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IPriceRegistry.sol'; +import {OwnerIsCreator} from '@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol'; +import {Internal} from '@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Internal.sol'; +import {USDPriceWith18Decimals} from '@chainlink/contracts-ccip/src/v0.8/ccip/libraries/USDPriceWith18Decimals.sol'; +import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; + +// import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.0/utils/structs/EnumerableSet.sol"; + +/// @notice The PriceRegistry contract responsibility is to store the current gas price in USD for a given destination chain, +/// and the price of a token in USD allowing the owner or priceUpdater to update this value. +contract PriceRegistry is IPriceRegistry, OwnerIsCreator { + using EnumerableSet for EnumerableSet.AddressSet; + using USDPriceWith18Decimals for uint224; + + error TokenNotSupported(address token); + error NotAFeeToken(address token); + error ChainNotSupported(uint64 chain); + error OnlyCallableByUpdaterOrOwner(); + error StaleGasPrice(uint64 destChainSelector, uint256 threshold, uint256 timePassed); + error StaleTokenPrice(address token, uint256 threshold, uint256 timePassed); + error InvalidStalenessThreshold(); + + event PriceUpdaterSet(address indexed priceUpdater); + event PriceUpdaterRemoved(address indexed priceUpdater); + event FeeTokenAdded(address indexed feeToken); + event FeeTokenRemoved(address indexed feeToken); + event UsdPerUnitGasUpdated(uint64 indexed destChain, uint256 value, uint256 timestamp); + event UsdPerTokenUpdated(address indexed token, uint256 value, uint256 timestamp); + + /// @dev The price, in USD with 18 decimals, of 1 unit of gas for a given destination chain. + /// @dev Price of 1e18 is 1 USD. Examples: + /// Very Expensive: 1 unit of gas costs 1 USD -> 1e18 + /// Expensive: 1 unit of gas costs 0.1 USD -> 1e17 + /// Cheap: 1 unit of gas costs 0.000001 USD -> 1e12 + mapping(uint64 => Internal.TimestampedPackedUint224) private s_usdPerUnitGasByDestChainSelector; + + /// @dev The price, in USD with 18 decimals, per 1e18 of the smallest token denomination. + /// @dev Price of 1e18 represents 1 USD per 1e18 token amount. + /// 1 USDC = 1.00 USD per full token, each full token is 1e6 units -> 1 * 1e18 * 1e18 / 1e6 = 1e30 + /// 1 ETH = 2,000 USD per full token, each full token is 1e18 units -> 2000 * 1e18 * 1e18 / 1e18 = 2_000e18 + /// 1 LINK = 5.00 USD per full token, each full token is 1e18 units -> 5 * 1e18 * 1e18 / 1e18 = 5e18 + mapping(address => Internal.TimestampedPackedUint224) private s_usdPerToken; + address[] public updaters = [address(msg.sender)]; + address[] public fTokens = [ + address(0x779877A7B0D9E8603169DdbD7836e478b4624789), + address(0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534), + address(0xc4bF5CbDaBE595361438F8c6a187bDc330539c60) + ]; + + // Price updaters are allowed to update the prices. + EnumerableSet.AddressSet private s_priceUpdaters; + // Subset of tokens which prices tracked by this registry which are fee tokens. + EnumerableSet.AddressSet private s_feeTokens; + // The amount of time a price can be stale before it is considered invalid. + uint32 private immutable i_stalenessThreshold = 3 days; + + constructor() { + // priceUpdaters.push(address(this)); + Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + _updatePrices(priceUpdates); + _applyPriceUpdatersUpdates(updaters, new address[](0)); + _applyFeeTokensUpdates(fTokens, new address[](0)); + // if (stalenessThreshold == 0) revert InvalidStalenessThreshold(); + // i_stalenessThreshold = stalenessThreshold; + } + + // ================================================================ + // | Price calculations | + // ================================================================ + + // @inheritdoc IPriceRegistry + function getTokenPrice(address token) public view override returns (Internal.TimestampedPackedUint224 memory) { + return s_usdPerToken[token]; + } + + // @inheritdoc IPriceRegistry + function getValidatedTokenPrice(address token) external view override returns (uint224) { + return _getValidatedTokenPrice(token); + } + + // @inheritdoc IPriceRegistry + function getTokenPrices( + address[] calldata tokens + ) external view override returns (Internal.TimestampedPackedUint224[] memory) { + uint256 length = tokens.length; + Internal.TimestampedPackedUint224[] memory tokenPrices = new Internal.TimestampedPackedUint224[](length); + for (uint256 i = 0; i < length; ++i) { + tokenPrices[i] = getTokenPrice(tokens[i]); + } + return tokenPrices; + } + + /// @notice Get the staleness threshold. + /// @return stalenessThreshold The staleness threshold. + function getStalenessThreshold() external view returns (uint128) { + return i_stalenessThreshold; + } + + // @inheritdoc IPriceRegistry + function getDestinationChainGasPrice( + uint64 destChainSelector + ) external view override returns (Internal.TimestampedPackedUint224 memory) { + return s_usdPerUnitGasByDestChainSelector[destChainSelector]; + } + + function getTokenAndGasPrices( + address feeToken, + uint64 destChainSelector + ) external view override returns (uint224 feeTokenPrice, uint224 gasPriceValue) { + if (!s_feeTokens.contains(feeToken)) revert NotAFeeToken(feeToken); + + Internal.TimestampedPackedUint224 memory gasPrice = s_usdPerUnitGasByDestChainSelector[destChainSelector]; + // We do allow a gas price of 0, but no stale or unset gas prices + if (gasPrice.timestamp == 0) revert ChainNotSupported(destChainSelector); + uint256 timePassed = block.timestamp - gasPrice.timestamp; + if (timePassed > i_stalenessThreshold) revert StaleGasPrice(destChainSelector, i_stalenessThreshold, timePassed); + + return (_getValidatedTokenPrice(feeToken), gasPrice.value); + } + + /// @inheritdoc IPriceRegistry + /// @dev this function assumed that no more than 1e59 dollar, is sent as payment. + /// If more is sent, the multiplication of feeTokenAmount and feeTokenValue will overflow. + /// Since there isn't even close to 1e59 dollars in the world economy this is safe. + function convertTokenAmount( + address fromToken, + uint256 fromTokenAmount, + address toToken + ) external view override returns (uint256) { + /// Example: + /// fromTokenAmount: 1e18 // 1 ETH + /// ETH: 2_000e18 + /// LINK: 5e18 + /// return: 1e18 * 2_000e18 / 5e18 = 400e18 (400 LINK) + return (fromTokenAmount * _getValidatedTokenPrice(fromToken)) / _getValidatedTokenPrice(toToken); + } + + /// @notice Gets the token price for a given token and revert if the token is either + /// not supported or the price is stale. + /// @param token The address of the token to get the price for + /// @return the token price + function _getValidatedTokenPrice(address token) internal view returns (uint224) { + Internal.TimestampedPackedUint224 memory tokenPrice = s_usdPerToken[token]; + if (tokenPrice.timestamp == 0 || tokenPrice.value == 0) revert TokenNotSupported(token); + uint256 timePassed = block.timestamp - tokenPrice.timestamp; + if (timePassed > i_stalenessThreshold) revert StaleTokenPrice(token, i_stalenessThreshold, timePassed); + return tokenPrice.value; + } + + // ================================================================ + // | Fee tokens | + // ================================================================ + + /// @notice Get the list of fee tokens. + /// @return feeTokens The tokens set as fee tokens. + function getFeeTokens() external view returns (address[] memory feeTokens) { + feeTokens = new address[](s_feeTokens.length()); + for (uint256 i = 0; i < s_feeTokens.length(); ++i) { + feeTokens[i] = s_feeTokens.at(i); + } + } + + /// @notice Add and remove tokens from feeTokens set. + /// @param feeTokensToAdd The addresses of the tokens which are now considered fee tokens + /// and can be used to calculate fees. + /// @param feeTokensToRemove The addresses of the tokens which are no longer considered feeTokens. + function applyFeeTokensUpdates( + address[] memory feeTokensToAdd, + address[] memory feeTokensToRemove + ) external onlyOwner { + _applyFeeTokensUpdates(feeTokensToAdd, feeTokensToRemove); + } + + /// @notice Add and remove tokens from feeTokens set. + /// @param feeTokensToAdd The addresses of the tokens which are now considered fee tokens + /// and can be used to calculate fees. + /// @param feeTokensToRemove The addresses of the tokens which are no longer considered feeTokens. + function _applyFeeTokensUpdates(address[] memory feeTokensToAdd, address[] memory feeTokensToRemove) private { + for (uint256 i = 0; i < feeTokensToAdd.length; ++i) { + if (s_feeTokens.add(feeTokensToAdd[i])) { + emit FeeTokenAdded(feeTokensToAdd[i]); + } + } + for (uint256 i = 0; i < feeTokensToRemove.length; ++i) { + if (s_feeTokens.remove(feeTokensToRemove[i])) { + emit FeeTokenRemoved(feeTokensToRemove[i]); + } + } + } + + // ================================================================ + // | Price updates | + // ================================================================ + + // @inheritdoc IPriceRegistry + function updatePrices(Internal.PriceUpdates memory priceUpdates) external override requireUpdaterOrOwner { + _updatePrices(priceUpdates); + } + + /// @notice Updates all prices in the priceUpdates struct. + /// @param priceUpdates The struct containing all the price updates. + function _updatePrices(Internal.PriceUpdates memory priceUpdates) private { + uint256 priceUpdatesLength = priceUpdates.tokenPriceUpdates.length; + + for (uint256 i = 0; i < priceUpdatesLength; ++i) { + Internal.TokenPriceUpdate memory update = priceUpdates.tokenPriceUpdates[i]; + s_usdPerToken[update.sourceToken] = Internal.TimestampedPackedUint224({ + value: update.usdPerToken, + timestamp: uint32(block.timestamp) + }); + emit UsdPerTokenUpdated(update.sourceToken, update.usdPerToken, block.timestamp); + if (priceUpdates.gasPriceUpdates[i].destChainSelector != 0) { + //.destChainSelector != 0) { + s_usdPerUnitGasByDestChainSelector[priceUpdates.gasPriceUpdates[i].destChainSelector] = Internal + .TimestampedPackedUint224({ + value: priceUpdates.gasPriceUpdates[i].usdPerUnitGas, + timestamp: uint32(block.timestamp) + }); + emit UsdPerUnitGasUpdated( + priceUpdates.gasPriceUpdates[i].destChainSelector, + priceUpdates.gasPriceUpdates[i].usdPerUnitGas, + block.timestamp + ); + } + } + } + + // ================================================================ + // | Access | + // ================================================================ + + /// @notice Get the list of price updaters. + /// @return priceUpdaters The price updaters. + function getPriceUpdaters() external view returns (address[] memory priceUpdaters) { + priceUpdaters = new address[](s_priceUpdaters.length()); + for (uint256 i = 0; i < s_priceUpdaters.length(); ++i) { + priceUpdaters[i] = s_priceUpdaters.at(i); + } + } + + /// @notice Adds new priceUpdaters and remove existing ones. + /// @param priceUpdatersToAdd The addresses of the priceUpdaters that are now allowed + /// to send fee updates. + /// @param priceUpdatersToRemove The addresses of the priceUpdaters that are no longer allowed + /// to send fee updates. + function applyPriceUpdatersUpdates( + address[] memory priceUpdatersToAdd, + address[] memory priceUpdatersToRemove + ) external onlyOwner { + _applyPriceUpdatersUpdates(priceUpdatersToAdd, priceUpdatersToRemove); + } + + /// @notice Adds new priceUpdaters and remove existing ones. + /// @param priceUpdatersToAdd The addresses of the priceUpdaters that are now allowed + /// to send fee updates. + /// @param priceUpdatersToRemove The addresses of the priceUpdaters that are no longer allowed + /// to send fee updates. + function _applyPriceUpdatersUpdates( + address[] memory priceUpdatersToAdd, + address[] memory priceUpdatersToRemove + ) private { + for (uint256 i = 0; i < priceUpdatersToAdd.length; ++i) { + if (s_priceUpdaters.add(priceUpdatersToAdd[i])) { + emit PriceUpdaterSet(priceUpdatersToAdd[i]); + } + } + for (uint256 i = 0; i < priceUpdatersToRemove.length; ++i) { + if (s_priceUpdaters.remove(priceUpdatersToRemove[i])) { + emit PriceUpdaterRemoved(priceUpdatersToRemove[i]); + } + } + } + + /// @notice Require that the caller is the owner or a fee updater. + modifier requireUpdaterOrOwner() { + if (msg.sender != owner() && !s_priceUpdaters.contains(msg.sender)) revert OnlyCallableByUpdaterOrOwner(); + _; + } +} diff --git a/packages/ccip-js/src/contracts/Router.sol b/packages/ccip-js/src/contracts/Router.sol new file mode 100644 index 0000000..bd46053 --- /dev/null +++ b/packages/ccip-js/src/contracts/Router.sol @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; +import { Router } from "@chainlink/contracts-ccip/src/v0.8/ccip/Router.sol"; + +// import { +// ITypeAndVersion, +// IAny2EVMMessageReceiver, +// IEVM2AnyOnRamp, +// IRMN, +// IRouter, +// IRouterClient, +// IWrappedNative, +// OwnerIsCreator, +// CallWithExactGas, +// Client, +// Internal, +// IERC20, +// SafeERC20, +// EnumerableSet +// } from "@chainlink/contracts-ccip/src/v0.8/ccip/Router.sol"; + +// /// @title Router +// /// @notice This is the entry point for the end user wishing to send data across chains. +// /// @dev This contract is used as a router for both on-ramps and off-ramps +// contract Router is IRouter, IRouterClient, ITypeAndVersion, OwnerIsCreator { +// using SafeERC20 for IERC20; +// using EnumerableSet for EnumerableSet.UintSet; + +// error FailedToSendValue(); +// error InvalidRecipientAddress(address to); +// error OffRampMismatch(uint64 chainSelector, address offRamp); +// error BadARMSignal(); + +// event OnRampSet(uint64 indexed destChainSelector, address onRamp); +// event OffRampAdded(uint64 indexed sourceChainSelector, address offRamp); +// event OffRampRemoved(uint64 indexed sourceChainSelector, address offRamp); +// event MessageExecuted(bytes32 messageId, uint64 sourceChainSelector, address offRamp, bytes32 calldataHash); + +// struct OnRamp { +// uint64 destChainSelector; +// address onRamp; +// } + +// struct OffRamp { +// uint64 sourceChainSelector; +// address offRamp; +// } + +// string public constant override typeAndVersion = "Router 1.2.0"; +// // We limit return data to a selector plus 4 words. This is to avoid +// // malicious contracts from returning large amounts of data and causing +// // repeated out-of-gas scenarios. +// uint16 public constant MAX_RET_BYTES = 4 + 4 * 32; +// // STATIC CONFIG +// // Address of RMN proxy contract (formerly known as ARM) +// address private immutable i_armProxy; + +// // DYNAMIC CONFIG +// address private s_wrappedNative; +// // destChainSelector => onRamp address +// // Only ever one onRamp enabled at a time for a given destChainSelector. +// mapping(uint256 destChainSelector => address onRamp) private s_onRamps; +// // Stores [sourceChainSelector << 160 + offramp] as a pair to allow for +// // lookups for specific chain/offramp pairs. +// EnumerableSet.UintSet private s_chainSelectorAndOffRamps; + +// constructor(address wrappedNative, address armProxy) { +// // Zero address indicates unsupported auto-wrapping, therefore, unsupported +// // native fee token payments. +// s_wrappedNative = wrappedNative; +// i_armProxy = armProxy; +// } + +// // ================================================================ +// // │ Message sending │ +// // ================================================================ + +// /// @inheritdoc IRouterClient +// function getFee( +// uint64 destinationChainSelector, +// Client.EVM2AnyMessage memory message +// ) external view returns (uint256 fee) { +// if (message.feeToken == address(0)) { +// // For empty feeToken return native quote. +// message.feeToken = address(s_wrappedNative); +// } +// address onRamp = s_onRamps[destinationChainSelector]; +// if (onRamp == address(0)) revert UnsupportedDestinationChain(destinationChainSelector); +// return IEVM2AnyOnRamp(onRamp).getFee(destinationChainSelector, message); +// } + +// /// @notice This functionality has been removed and will revert when called. +// function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory) { +// if (!isChainSupported(chainSelector)) { +// return new address[](0); +// } +// return new address[](0); +// // IEVM2AnyOnRamp(s_onRamps[uint256(chainSelector)]).getSupportedTokens(chainSelector); +// } + +// /// @inheritdoc IRouterClient +// function isChainSupported(uint64 chainSelector) public view returns (bool) { +// return true; // s_onRamps[chainSelector] != address(0); +// } + +// /// @inheritdoc IRouterClient +// function ccipSend( +// uint64 destinationChainSelector, +// Client.EVM2AnyMessage memory message +// ) external payable whenNotCursed returns (bytes32) { +// address onRamp = s_onRamps[destinationChainSelector]; +// if (onRamp == address(0)) revert UnsupportedDestinationChain(destinationChainSelector); +// uint256 feeTokenAmount; +// // address(0) signals payment in true native +// if (message.feeToken == address(0)) { +// // for fee calculation we check the wrapped native price as we wrap +// // as part of the native fee coin payment. +// message.feeToken = s_wrappedNative; +// // We rely on getFee to validate that the feeToken is whitelisted. +// feeTokenAmount = IEVM2AnyOnRamp(onRamp).getFee(destinationChainSelector, message); +// // Ensure sufficient native. +// if (msg.value < feeTokenAmount) revert InsufficientFeeTokenAmount(); +// // Wrap and send native payment. +// // Note we take the whole msg.value regardless if its larger. +// feeTokenAmount = msg.value; +// IWrappedNative(message.feeToken).deposit{value: feeTokenAmount}(); +// IERC20(message.feeToken).safeTransfer(onRamp, feeTokenAmount); +// } else { +// if (msg.value > 0) revert InvalidMsgValue(); +// // We rely on getFee to validate that the feeToken is whitelisted. +// feeTokenAmount = IEVM2AnyOnRamp(onRamp).getFee(destinationChainSelector, message); +// IERC20(message.feeToken).safeTransferFrom(msg.sender, onRamp, feeTokenAmount); +// } + +// // Transfer the tokens to the token pools. +// for (uint256 i = 0; i < message.tokenAmounts.length; ++i) { +// IERC20 token = IERC20(message.tokenAmounts[i].token); +// // We rely on getPoolBySourceToken to validate that the token is whitelisted. +// token.safeTransferFrom( +// msg.sender, +// address(IEVM2AnyOnRamp(onRamp).getPoolBySourceToken(destinationChainSelector, token)), +// message.tokenAmounts[i].amount +// ); +// } + +// return IEVM2AnyOnRamp(onRamp).forwardFromRouter(destinationChainSelector, message, feeTokenAmount, msg.sender); +// } + +// // ================================================================ +// // │ Message execution │ +// // ================================================================ + +// /// @inheritdoc IRouter +// /// @dev _callWithExactGas protects against return data bombs by capping the return data size at MAX_RET_BYTES. +// function routeMessage( +// Client.Any2EVMMessage calldata message, +// uint16 gasForCallExactCheck, +// uint256 gasLimit, +// address receiver +// ) external override whenNotCursed returns (bool success, bytes memory retData, uint256 gasUsed) { +// // We only permit offRamps to call this function. +// if (!isOffRamp(message.sourceChainSelector, msg.sender)) revert OnlyOffRamp(); + +// // We encode here instead of the offRamps to constrain specifically what functions +// // can be called from the router. +// bytes memory data = abi.encodeWithSelector(IAny2EVMMessageReceiver.ccipReceive.selector, message); + +// (success, retData, gasUsed) = CallWithExactGas._callWithExactGasSafeReturnData( +// data, receiver, gasLimit, gasForCallExactCheck, Internal.MAX_RET_BYTES +// ); + +// emit MessageExecuted(message.messageId, message.sourceChainSelector, msg.sender, keccak256(data)); +// return (success, retData, gasUsed); +// } + +// // @notice Merges a chain selector and offRamp address into a single uint256 by shifting the +// // chain selector 160 bits to the left. +// function _mergeChainSelectorAndOffRamp( +// uint64 sourceChainSelector, +// address offRampAddress +// ) internal pure returns (uint256) { +// return (uint256(sourceChainSelector) << 160) + uint160(offRampAddress); +// } + +// // ================================================================ +// // │ Config │ +// // ================================================================ + +// /// @notice Gets the wrapped representation of the native fee coin. +// /// @return The address of the ERC20 wrapped native. +// function getWrappedNative() external view returns (address) { +// return s_wrappedNative; +// } + +// /// @notice Sets a new wrapped native token. +// /// @param wrappedNative The address of the new wrapped native ERC20 token. +// function setWrappedNative(address wrappedNative) external onlyOwner { +// s_wrappedNative = wrappedNative; +// } + +// /// @notice Gets the RMN address, formerly known as ARM +// /// @return The address of the RMN proxy contract, formerly known as ARM +// function getArmProxy() external view returns (address) { +// return i_armProxy; +// } + +// /// @inheritdoc IRouter +// function getOnRamp(uint64 destChainSelector) external view returns (address) { +// return address(0xbae); // s_onRamps[destChainSelector]; +// } + +// function getOffRamps() external view returns (OffRamp[] memory) { +// uint256[] memory encodedOffRamps = s_chainSelectorAndOffRamps.values(); +// OffRamp[] memory offRamps = new OffRamp[](encodedOffRamps.length); +// for (uint256 i = 0; i < encodedOffRamps.length; ++i) { +// uint256 encodedOffRamp = encodedOffRamps[i]; +// offRamps[i] = +// OffRamp({sourceChainSelector: uint64(encodedOffRamp >> 160), offRamp: address(uint160(encodedOffRamp))}); +// } +// return offRamps; +// } + +// /// @inheritdoc IRouter +// function isOffRamp(uint64 sourceChainSelector, address offRamp) public view returns (bool) { +// // We have to encode the sourceChainSelector and offRamp into a uint256 to use as a key in the set. +// return s_chainSelectorAndOffRamps.contains(_mergeChainSelectorAndOffRamp(sourceChainSelector, offRamp)); +// } + +// /// @notice applyRampUpdates applies a set of ramp changes which provides +// /// the ability to add new chains and upgrade ramps. +// function applyRampUpdates( +// OnRamp[] calldata onRampUpdates, +// OffRamp[] calldata offRampRemoves, +// OffRamp[] calldata offRampAdds +// ) external onlyOwner { +// // Apply egress updates. +// // We permit zero address as way to disable egress. +// for (uint256 i = 0; i < onRampUpdates.length; ++i) { +// OnRamp memory onRampUpdate = onRampUpdates[i]; +// s_onRamps[onRampUpdate.destChainSelector] = onRampUpdate.onRamp; +// emit OnRampSet(onRampUpdate.destChainSelector, onRampUpdate.onRamp); +// } + +// // Apply ingress updates. +// for (uint256 i = 0; i < offRampRemoves.length; ++i) { +// uint64 sourceChainSelector = offRampRemoves[i].sourceChainSelector; +// address offRampAddress = offRampRemoves[i].offRamp; + +// // If the selector-offRamp pair does not exist, revert. +// if (!s_chainSelectorAndOffRamps.remove(_mergeChainSelectorAndOffRamp(sourceChainSelector, offRampAddress))) { +// revert OffRampMismatch(sourceChainSelector, offRampAddress); +// } + +// emit OffRampRemoved(sourceChainSelector, offRampAddress); +// } + +// for (uint256 i = 0; i < offRampAdds.length; ++i) { +// uint64 sourceChainSelector = offRampAdds[i].sourceChainSelector; +// address offRampAddress = offRampAdds[i].offRamp; + +// if (s_chainSelectorAndOffRamps.add(_mergeChainSelectorAndOffRamp(sourceChainSelector, offRampAddress))) { +// emit OffRampAdded(sourceChainSelector, offRampAddress); +// } +// } +// } + +// /// @notice Provides the ability for the owner to recover any tokens accidentally +// /// sent to this contract. +// /// @dev Must be onlyOwner to avoid malicious token contract calls. +// /// @param tokenAddress ERC20-token to recover +// /// @param to Destination address to send the tokens to. +// function recoverTokens(address tokenAddress, address to, uint256 amount) external onlyOwner { +// if (to == address(0)) revert InvalidRecipientAddress(to); + +// if (tokenAddress == address(0)) { +// (bool success,) = to.call{value: amount}(""); +// if (!success) revert FailedToSendValue(); +// return; +// } +// IERC20(tokenAddress).safeTransfer(to, amount); +// } + +// // ================================================================ +// // │ Access │ +// // ================================================================ + +// /// @notice Ensure that the RMN has not cursed the network. +// modifier whenNotCursed() { +// if (IRMN(i_armProxy).isCursed()) revert BadARMSignal(); +// _; +// } +// } diff --git a/packages/ccip-js/src/contracts/TokenProxy.sol b/packages/ccip-js/src/contracts/TokenProxy.sol new file mode 100644 index 0000000..5a1b79f --- /dev/null +++ b/packages/ccip-js/src/contracts/TokenProxy.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { TokenProxy } from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/TokenProxy.sol"; diff --git a/packages/ccip-js/tasks/helpers.ts b/packages/ccip-js/tasks/helpers.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/ccip-js/test/helpers/accounts.ts b/packages/ccip-js/test/helpers/accounts.ts new file mode 100644 index 0000000..b7f331e --- /dev/null +++ b/packages/ccip-js/test/helpers/accounts.ts @@ -0,0 +1,81 @@ +import { Address, formatEther, parseEther } from 'viem' +import { account, bridgeTokenAbi } from './constants' +import { forkClient, testClient } from './clients' +// import { getContractAddresses } from './contracts' +// import { mineBlock } from './utils' + +interface BalanceOptions { + isFork: boolean + amount: string +} +// interface AllowanceOptions { +// isFork: boolean +// amount: string +// contractAddress: string +// } + +export const increaseBalance = async ({ isFork, amount }: BalanceOptions) => { + const client = isFork ? forkClient : testClient + + const startBalance = await client.getBalance({ + address: account.address, + }) + + await client.setBalance({ + address: account.address, + value: startBalance + parseEther(amount), + }) + + const balance = await client.getBalance({ + address: account.address, + }) + + console.log('balance:', formatEther(balance)) +} + +export const setBalance = async ({ isFork, amount }: BalanceOptions) => { + const client = isFork ? forkClient : testClient + + await client.setBalance({ + address: account.address, + value: parseEther(amount), + }) + + const balance = await client.getBalance({ + address: account.address, + }) + + console.log('balance:', formatEther(balance)) + + return balance +} + +export const getBalance = async ({ isFork }: BalanceOptions) => { + const client = isFork ? forkClient : testClient + + const balance = await client.getBalance({ + address: account.address, + }) + + console.log('balance:', formatEther(balance)) + + return balance +} + + +// export const setAllowance = async ({ isFork, amount, contract }: AllowanceOptions) => { +// const client = isFork ? forkClient : testClient + +// await client.setAllowance({ +// address: account.address, +// value: parseEther(amount), +// }) + +// const allowance = await client.getAllowance({ +// address: account.address, +// }) + +// console.log('allowance:', formatEther(allowance)) + +// return allowance +// } diff --git a/packages/ccip-js/test/helpers/clients.ts b/packages/ccip-js/test/helpers/clients.ts new file mode 100644 index 0000000..d39fbd6 --- /dev/null +++ b/packages/ccip-js/test/helpers/clients.ts @@ -0,0 +1,21 @@ +import { account } from './constants' +import { createTestClient, http, publicActions, walletActions } from 'viem' +import { hardhat, sepolia } from 'viem/chains' + +export const testClient = createTestClient({ + chain: hardhat, + transport: http(), + mode: 'anvil', + account, +}) + .extend(publicActions) + .extend(walletActions) + +export const forkClient = createTestClient({ + chain: sepolia, + transport: http(), + mode: 'anvil', + account, +}) + .extend(publicActions) + .extend(walletActions) diff --git a/packages/ccip-js/test/helpers/config.ts b/packages/ccip-js/test/helpers/config.ts new file mode 100644 index 0000000..322f847 --- /dev/null +++ b/packages/ccip-js/test/helpers/config.ts @@ -0,0 +1,177 @@ +import { Address, zeroAddress } from "viem" +import { DynamicConfig, FeeTokenConfigArgs, NopAndWeight, RateLimiterConfig, StaticConfig, TokenTransferFeeConfigArgs } from "./types" +import { getContractAddresses, getContracts } from "./contracts" +import { mineBlock } from "./utils" + +interface ConfigOptions { + chainSelector: bigint +} + +interface DynamicConfigOptions { + priceRegistryAddress: Address +} + +interface FeeTokenConfigOptions { + chainSelector: bigint + tokenAddress: Address +} + +interface RampOptions { + routerAddress: Address + chainSelector: bigint +} + +export const getTokenAdminRegistry = async ({ chainSelector }: ConfigOptions) => { + const staticConfig = await setStaticConfig({ chainSelector }) + mineBlock(false) + console.log({ staticConfig }) + const tokenAdminRegistryAddress: Address = await (staticConfig as StaticConfig).tokenAdminRegistryAddress ?? + zeroAddress + + return { + tokenAdminRegistryAddress: tokenAdminRegistryAddress + } +} + +export const setStaticConfig = async ({ + chainSelector +}: ConfigOptions) => { + const { linkTokenAddress } = await getContractAddresses() + console.log({ linkTokenAddress }) + const { tokenAdminRegistryAddress } = await getTokenAdminRegistry({ + chainSelector: chainSelector + }) + console.log({ tokenAdminRegistryAddress }) + return { + linkToken: linkTokenAddress, + chainSelector: chainSelector, + destChainSelector: chainSelector, + defaultTxGasLimit: 200000n, + maxNopFeesJuels: 100000000000000000000000000n, + prevOnRamp: zeroAddress, + armProxy: zeroAddress, + rmnProxy: zeroAddress, + tokenAdminRegistryAddress: tokenAdminRegistryAddress + } as StaticConfig +} + +const setDynamicConfig = async ({ priceRegistryAddress }: DynamicConfigOptions) => { + const { routerAddress } = await getContractAddresses() + return { + router: routerAddress, + maxNumberOfTokensPerMsg: 1n, + destGasPerPayloadByte: 1n, + destDataAvailabilityOverheadGas: 1n, + destGasOverhead: 1n, + destGasPerDataAvailabilityByte: 1n, + destDataAvailabilityMultiplierBps: 1n, + priceRegistry: priceRegistryAddress, + maxDataBytes: 1n, + maxPerMsgGasLimit: 1n + } as DynamicConfig +} + +const setNopsAndWeights = async ({ chainSelector }: ConfigOptions) => { + const nopsAndWeights = + [ + { + nop: zeroAddress, + weight: 1n, + } + ] as NopAndWeight[] + + return nopsAndWeights +} + +// todo: verify +const setTokenAdminRegistry = async ({ chainSelector }: ConfigOptions) => { + const tokenAdminRegistryAddress = await getTokenAdminRegistry({ chainSelector }) + return tokenAdminRegistryAddress +} + +// const setRateLimiterConfig = async ({ chainSelector }: ConfigOptions) => { +// return { +// isEnabled: false, +// capacity: 1n, +// rate: 1n +// } as RateLimiterConfig +// } + +const setFeeTokenConfigs = async ({ chainSelector, tokenAddress }: FeeTokenConfigOptions) => { + return [ + { + token: tokenAddress, // ───────╮ Token address + networkFeeUSDCents: 1n, // │ Flat network fee to charge for messages, multiples of 0.01 USD + gasMultiplierWeiPerEth: 1n, // ─────╯ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost + premiumMultiplierWeiPerEth: 1n, // ───────╮ Multiplier for fee-token-specific premiums, 1e18 based + enabled: true // ───────╯ Whether this fee token is enabled + } + ] as FeeTokenConfigArgs[] +} + +const setTokenTransferFeeConfigArgs = async ({ chainSelector, tokenAddress }: FeeTokenConfigOptions) => { + const tokenTransferFeeConfigArgs = + [ + { + token: tokenAddress, // ────────────╮ Token address + minFeeUSDCents: 1n, // │ Minimum fee to charge per token transfer, multiples of 0.01 USD + maxFeeUSDCents: 1n, // │ Maximum fee to charge per token transfer, multiples of 0.01 USD + deciBps: 1n, // ───────────╯ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 + destGasOverhead: 1n, // ──╮ Gas charged to execute the token transfer on the destination chain + destBytesOverhead: 1n // ─╯ Extra data availability bytes on top of fixed transfer data, including sourceTokenData and offchainData + } + ] as TokenTransferFeeConfigArgs[] + + return tokenTransferFeeConfigArgs +} + +export const getStandardConfigs = async ({ chainSelector }: ConfigOptions) => { + const { priceRegistryAddress, routerAddress } = await getContractAddresses() + const staticConfig = await setStaticConfig({ chainSelector }) + const rateLimiterConfig = { + isEnabled: false, + capacity: 1n, + rate: 1n + } as RateLimiterConfig + // await setRateLimiterConfig({ chainSelector }) + const dynamicConfig = await setDynamicConfig({ priceRegistryAddress }) + + return { + staticConfig, + rateLimiterConfig, + dynamicConfig, + } +} + +export const getSupportedFeeTokens = async () => { + const { priceRegistry } = await getContracts() + const feeTokens = await priceRegistry.read.getFeeTokens() as Address[] + return feeTokens +} + +export const getTokenConfigs = async ({ chainSelector }: ConfigOptions) => { + const { linkTokenAddress } = await getContractAddresses() + const tokenAdminRegistryAddress = await setTokenAdminRegistry({ chainSelector }) + const feeTokenConfigs = await setFeeTokenConfigs({ chainSelector, tokenAddress: linkTokenAddress }) + const tokenTransferFeeConfigArgs = await setTokenTransferFeeConfigArgs({ chainSelector, tokenAddress: linkTokenAddress }) + + return { + tokenAdminRegistryAddress, + feeTokenConfigs, + tokenTransferFeeConfigArgs, + } +} + +export const getRampConfigs = async ({ chainSelector }: ConfigOptions) => { + const { routerAddress } = await getContractAddresses() + const nopsAndWeights = await setNopsAndWeights({ chainSelector }) + const rampOptions: RampOptions = { + routerAddress, + chainSelector: chainSelector, + } + + return { + nopsAndWeights, + rampOptions + } +} diff --git a/packages/ccip-js/test/helpers/constants.ts b/packages/ccip-js/test/helpers/constants.ts new file mode 100644 index 0000000..8cb0b4a --- /dev/null +++ b/packages/ccip-js/test/helpers/constants.ts @@ -0,0 +1,122 @@ +import { Address, Hash, Hex, TransactionReceipt } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import dotenv from 'dotenv' +import bridgeJson from '../../artifacts-compile/BridgeToken.json' +import onRampJson from '../../artifacts-compile/EVM2EVMOnRamp.json' +import routerJson from '../../artifacts-compile/Router.json' +import simulatorJson from '../../artifacts-compile/CCIPLocalSimulator.json' +import priceRegistryJson from '../../artifacts-compile/PriceRegistry.json' + +// load.env file for private key +// replace with your own private key (optional) +dotenv.config() + +// default anvil PK +export const privateKey = + (process.env.PRIVATE_KEY as Hex) || '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' +export const account = privateKeyToAccount(privateKey) + +// bridge token contract +export const { bridgeTokenAbi, bridgeTokenBin } = bridgeJson['contracts']['src/contracts/BridgeToken.sol:BridgeToken'] +// note: no need to deploy +export const { onRampAbi, onRampBin } = onRampJson['contracts']['src/contracts/EVM2EVMOnRamp.sol:EVM2EVMOnRamp'] +export const { routerAbi, routerBin } = routerJson['contracts']['src/contracts/Router.sol:Router'] +export const { simulatorAbi, simulatorBin } = simulatorJson['contracts']['src/contracts/CCIPLocalSimulator.sol:CCIPLocalSimulator'] +export const { priceRegistryAbi, priceRegistryBin } = priceRegistryJson['contracts']['src/contracts/PriceRegistry.sol:PriceRegistry'] + +// CCIP testing data for simulations +export const ccipTxHash = '0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e' + +export const ccipLog = [ + { + address: '0x0aec1ac9f6d0c21332d7a66ddf1fbcb32cf3b0b3' as Address, + blockHash: '0xd8a5943213a52e0e453c0c7ffe921f3c4c84b15ee02915e59ada0d058f33ab2a' as Hash, + args: { + message: { + data: '0x0000000000000000000000000000000000000000000000000000000000000000', + feeToken: '0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846', + feeTokenAmount: 317861394337043364n, + gasLimit: 0n, + messageId: '0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062', + nonce: 7n, + receiver: '0x748Cab9A6993A24CA6208160130b3f7b79098c6d', + sender: '0x748Cab9A6993A24CA6208160130b3f7b79098c6d', + sequenceNumber: 1265n, + sourceChainSelector: 14767482510784806043n, + }, + }, + blockNumber: 36381795n, + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000ccf0a31a221f3c9b000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d00000000000000000000000000000000000000000000000000000000000004f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000b9d5d9136855f6fec3c0993fee6e9ce8a29784600000000000000000000000000000000000000000000000004694541094513a400000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240de438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf06200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070f5c5c40b873ea597776da2c21929a8282a3b35000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000015f9000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008e35eb0dfb39ec5f84254c3f863986a913171e0b0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c0000000000000000000000000000000000000000000000000000000000000000', + eventName: 'CCIPSendRequested', + logIndex: 8, + removed: false, + topics: ['0xd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd' as Address], + transactionHash: '0xb96c7d676f69e904fe283312386f375352e407446b7b0c4b8452d09a13cc6b10', + transactionIndex: 0, + }, +] + +export const ccipLogWOMessageId = [ + { + address: '0x0aec1ac9f6d0c21332d7a66ddf1fbcb32cf3b0b3' as Address, + blockHash: '0xd8a5943213a52e0e453c0c7ffe921f3c4c84b15ee02915e59ada0d058f33ab2a' as Hash, + args: {}, + blockNumber: 36381795n, + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000ccf0a31a221f3c9b000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d00000000000000000000000000000000000000000000000000000000000004f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000b9d5d9136855f6fec3c0993fee6e9ce8a29784600000000000000000000000000000000000000000000000004694541094513a400000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240de438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf06200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070f5c5c40b873ea597776da2c21929a8282a3b35000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000015f9000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008e35eb0dfb39ec5f84254c3f863986a913171e0b0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c0000000000000000000000000000000000000000000000000000000000000000', + eventName: 'CCIPSendRequested', + logIndex: 8, + removed: false, + topics: ['0xd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd' as Address], + transactionHash: '0xb96c7d676f69e904fe283312386f375352e407446b7b0c4b8452d09a13cc6b10', + transactionIndex: 0, + }, +] + +export const ccipTxReceipt: TransactionReceipt = { + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + contractAddress: null, + cumulativeGasUsed: 288318n, + effectiveGasPrice: 25000000001n, + from: '0x748cab9a6993a24ca6208160130b3f7b79098c6d', + gasUsed: 288318n, + logs: [ + { + address: '0x0b9d5d9136855f6fec3c0993fee6e9ce8a297846', + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + data: '0x0000000000000000000000000000000000000000000000000070c7afd6bb1899', + logIndex: 0, + removed: false, + topics: [ + '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', + '0x000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d', + '0x000000000000000000000000a9946ba30daec98745755e4410d6e8e894edc53b', + ], + transactionHash: ccipTxHash, + transactionIndex: 0, + }, + { + address: '0x70f5c5c40b873ea597776da2c21929a8282a3b35', + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + data: '0x0000000000000000000000000000000000000000000000000000000000000000', + logIndex: 1, + removed: false, + topics: [ + '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925', + '0x000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d', + '0x000000000000000000000000f694e193200268f9a4868e4aa017a0118c9a8177', + ], + transactionHash: ccipTxHash, + transactionIndex: 0, + }, + ], + logsBloom: + '0x01000000000000000001000000000004000000000000800000000000000000000000000008000000002000000000000000000000000000000000080000200000000000020000020000000008000000000100100000000000000010000000000000000000060000000000004000000802040000000000000000001050000000000002000000000000200000000000000400000000000080000800040000000000020000000000200600000000000000000080000000000000008100000000008100000002000000000001000000000000000000000000000800000000000024000010000000000000000000000000000004000200000000000000000000000000', + status: 'success', + to: '0xf694e193200268f9a4868e4aa017a0118c9a8177', + transactionHash: ccipTxHash, + transactionIndex: 0, + type: 'eip1559', +} \ No newline at end of file diff --git a/packages/ccip-js/test/helpers/contracts.ts b/packages/ccip-js/test/helpers/contracts.ts new file mode 100644 index 0000000..db50968 --- /dev/null +++ b/packages/ccip-js/test/helpers/contracts.ts @@ -0,0 +1,181 @@ +import { formatEther, getContract, type Address, type Hex } from 'viem' +import { account, bridgeTokenAbi, bridgeTokenBin, onRampAbi, onRampBin, priceRegistryAbi, priceRegistryBin, routerAbi, routerBin, simulatorAbi, simulatorBin } from './constants' +import { mineBlock } from './utils' +import { forkClient, testClient } from './clients' +import { readContract } from 'viem/actions' + +import { getRampConfigs, getStandardConfigs, getTokenConfigs } from './config' + +interface DeployContractOptions { + isFork: boolean + args: string[] + abi: any + bin: Hex +} + +interface AllowanceOptions { + isFork: boolean + contractAddress: Address + spenderAddress: Address + amount?: string +} + +export const deployContract = async ({ isFork, args, abi, bin }: DeployContractOptions) => { + const client = isFork ? forkClient : testClient + const txHash = await client.deployContract({ + abi: abi, + account, + args: args, + bytecode: bin, + }) + + await mineBlock(isFork) + + const { contractAddress } = await client.getTransactionReceipt({ hash: txHash }) + // console.log('deployed address:', contractAddress) + return contractAddress +} + +export const getContractAddresses = async () => { + const bridgeTokenAddress = await deployContract({ + isFork: false, + args: ['CCIP Burn & Mint Token', 'CCIP-BnM'], + abi: bridgeTokenAbi, + bin: `0x${bridgeTokenBin}` + }) as Address + const linkTokenAddress = await deployContract({ + isFork: false, + args: ['Chainlink', ' LINK'], + abi: bridgeTokenAbi, + bin: `0x${bridgeTokenBin}` + }) as Address + const localSimulatorAddress = await deployContract({ + isFork: false, + args: [], + abi: simulatorAbi, + bin: `0x${simulatorBin}` + }) as Address + const routerAddress = await deployContract({ + isFork: false, + args: [ + linkTokenAddress, linkTokenAddress + ], + abi: routerAbi, + bin: `0x${routerBin}` + }) as Address + const priceRegistryAddress = await deployContract({ + isFork: false, + args: [], + abi: priceRegistryAbi, + bin: `0x${priceRegistryBin}` + }) as Address + return { + localSimulatorAddress: localSimulatorAddress, + bridgeTokenAddress: bridgeTokenAddress, + linkTokenAddress: linkTokenAddress, + routerAddress: routerAddress, + priceRegistryAddress: priceRegistryAddress + } +} +export const getOnRampAddress = async () => { + const { staticConfig, dynamicConfig, rateLimiterConfig } = await getStandardConfigs({ chainSelector: 16015286601757825753n }) + const { feeTokenConfigs, tokenTransferFeeConfigArgs } = await getTokenConfigs({ chainSelector: 16015286601757825753n }) + const { nopsAndWeights, rampOptions } = await getRampConfigs({ chainSelector: 16015286601757825753n }) + const onRampAddress = await deployContract({ + isFork: false, + args: [ + staticConfig.toString(), + dynamicConfig.toString(), + rateLimiterConfig.toString(), + feeTokenConfigs.toString(), + tokenTransferFeeConfigArgs.toString(), + nopsAndWeights.toString() + ], + abi: onRampAbi, + bin: `0x${onRampBin}` + }) as Address + mineBlock(false) + return { + onRampAddress: onRampAddress + } + +} + +export const getContracts = async () => { + const { localSimulatorAddress, bridgeTokenAddress, priceRegistryAddress, routerAddress } = await getContractAddresses() + const bridgeToken = await getContract({ + address: bridgeTokenAddress, + abi: bridgeTokenAbi, + client: testClient, + }) + const router = await getContract({ + address: routerAddress, + abi: routerAbi, + client: testClient, + }) + const localSimulator = await getContract({ + address: localSimulatorAddress, + abi: simulatorAbi, + client: testClient, + }) + // const onRamp = await getContract({ + // address: onRampAddress, + // abi: onRampAbi, + // client: testClient, + // }) + const priceRegistry = await getContract({ + address: priceRegistryAddress, + abi: priceRegistryAbi, + client: testClient, + }) + mineBlock(false) + return { + bridgeToken: bridgeToken, + router: router, + localSimulator: localSimulator, + // onRamp: onRamp, + priceRegistry: priceRegistry + } +} + +export const getApprovalAmount = async ({ isFork, contractAddress, spenderAddress }: AllowanceOptions) => { + const client = isFork ? forkClient : testClient + + const allowance = await client.readContract({ + address: contractAddress, + abi: bridgeTokenAbi, + functionName: 'allowance', + args: [account.address, spenderAddress], // owner, spender + }) as bigint + + return allowance +} + +interface OnRampOptions { + destinationChainSelector: string + localSimulatorAddress?: Address +} + +export async function setOnRampAddress({ destinationChainSelector }: OnRampOptions) { + const { router } = await getContracts() + await router.write.applyRampUpdates([ + [ + { + destChainSelector: destinationChainSelector, + onRamp: '0x8F35B097022135E0F46831f798a240Cc8c4b0B01' + } + ], + [], + [] + ]); + mineBlock(false) + + const onRampAddress = await readContract(testClient, { + abi: routerAbi, + address: router.address, + functionName: 'getOnRamp', + args: [destinationChainSelector], + }) as Address + + return onRampAddress +} \ No newline at end of file diff --git a/packages/ccip-js/test/helpers/types.ts b/packages/ccip-js/test/helpers/types.ts new file mode 100644 index 0000000..13e1405 --- /dev/null +++ b/packages/ccip-js/test/helpers/types.ts @@ -0,0 +1,106 @@ +import { Address } from "viem"; + +const MAX_NUMBER_OF_NOPS = 64; +/** + * Configuration settings for static aspects of cross-chain transfers. + * + * The `StaticConfig` type defines the structure of an object that holds various + * static configuration parameters for cross-chain transactions. + * + * @typedef {Object} StaticConfig + * + * @property {Address} linkToken - The address of the LINK token on the source chain. + * @property {bigint} chainSelector - The selector for the source chain. + * @property {bigint} destChainSelector - The selector for the destination chain. + * @property {bigint} defaultTxGasLimit - Default gas limit per transaction. + * @property {bigint} maxNopFeesJuels - Maxium nop fee in juels. + * @property {Address} prevOnRamp - Previous onRamp contract address. + * @property {Address} rmnProxy - RMN proxy contract address. + * @property {Address} tokenAdminRegistryAddress - The address of the token admin registry contract. + */ +export type StaticConfig = { + linkToken: Address // ────────╮ Link token address + chainSelector: bigint // ─────╯ Source chainSelector + destChainSelector: bigint // ─╮ Destination chainSelector + defaultTxGasLimit: bigint // │ Default gas limit for a tx + maxNopFeesJuels: bigint // ───╯ Max nop fee balance onramp can have + prevOnRamp: Address // ────────╮ Address of previous-version OnRamp + armProxy: Address // | Address of ARM proxy + rmnProxy: Address // | Address of RMN proxy + tokenAdminRegistryAddress: Address // ────╯ Token admin registry address +} + +/** +* @typedef {Object} DynamicConfig +* +* @property {Address} router - The address of the router responsible for handling the cross-chain transfers. This address is used to route the transaction through the correct path. +* @property {bigint} maxNumberOfTokensPerMsg - The maximum number of tokens that can be included in a single message. This parameter limits the token batch size in cross-chain transfers to prevent overly large transactions. +* @property {bigint} destGasPerPayloadByte - The amount of gas required per byte of payload on the destination chain. This parameter is used to calculate the total gas needed based on the size of the payload being transferred. +* @property {bigint} destDataAvailabilityOverheadGas - The additional gas required on the destination chain to account for data availability overhead. This value is used to ensure that enough gas is allocated for the availability of the data being transferred. +* @property {bigint} destGasOverhead - The overhead in gas that is added to the destination chain to account for base transaction costs. This value helps ensure that the transaction has enough gas to cover additional overhead on the destination chain. +* @property {bigint} destGasPerDataAvailabilityByte - The gas cost per byte of data availability on the destination chain. This parameter contributes to the overall gas calculation for data availability during the transfer. +* @property {bigint} destDataAvailabilityMultiplierBps - The multiplier in basis points (bps) applied to the data availability gas cost. This value is used to adjust the cost of data availability by applying a scaling factor. +* @property {Address} priceRegistry - The address of the price registry used to obtain pricing information for gas and other costs during the transfer. This registry helps ensure that the correct prices are applied to the transaction. +* @property {bigint} maxDataBytes - The maximum number of data bytes that can be included in a single message. This parameter limits the size of the data payload to prevent excessive data in one transfer. +* @property {bigint} maxPerMsgGasLimit - The maximum gas limit that can be applied to a single message. This parameter ensures that the transaction does not exceed a certain gas threshold, preventing overly costly operations. +*/ +export type DynamicConfig = { + router: Address // ──────────────────────────╮ Router address + maxNumberOfTokensPerMsg: bigint // │ Maximum number of distinct ERC20 token transferred per message + destGasOverhead: bigint // │ Gas charged on top of the gasLimit to cover destination chain costs + destGasPerPayloadByte: bigint // │ Destination chain gas charged for passing each byte of `data` payload to receiver + destDataAvailabilityOverheadGas: bigint // ──╯ Extra data availability gas charged on top of the message, e.g. for OCR + destGasPerDataAvailabilityByte: bigint // ───╮ Amount of gas to charge per byte of message data that needs availability + destDataAvailabilityMultiplierBps: bigint // │ Multiplier for data availability gas, multiples of bps, or 0.0001 + priceRegistry: Address // │ Price registry address + maxDataBytes: bigint // │ Maximum payload data size in bytes + maxPerMsgGasLimit: bigint // ────────────────╯ Maximum gas limit for messages targeting EVMs +} + +export type RateLimiterConfig = { + isEnabled: boolean; // Indication whether the rate limiting should be enabled + capacity: bigint; // ────╮ Specifies the capacity of the rate limiter + rate: bigint; // ───────╯ Specifies the rate of the rate limiter +} + +export type FeeTokenConfigArgs = { + token: Address; // ─────────────────────╮ Token address + networkFeeUSDCents: bigint; // │ Flat network fee to charge for messages, multiples of 0.01 USD + gasMultiplierWeiPerEth: bigint; // ─────╯ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost + premiumMultiplierWeiPerEth: bigint; // ─╮ Multiplier for fee-token-specific premiums, 1e18 based + enabled: boolean; // ──────────────────────╯ Whether this fee token is enabled +} + +export type TokenTransferFeeConfigArgs = { + token: Address; // ────────────╮ Token address + minFeeUSDCents: bigint; // │ Minimum fee to charge per token transfer, multiples of 0.01 USD + maxFeeUSDCents: bigint; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD + deciBps: bigint; // ───────────╯ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 + destGasOverhead: bigint; // ───╮ Gas charged to execute the token transfer on the destination chain + destBytesOverhead: bigint; // ─╯ Extra data availability bytes on top of fixed transfer data, including sourceTokenData and offchainData +} + +export type NopAndWeight = { + nop: Address; // ────╮ Address of the node operator + weight: bigint; // ──╯ Weight for nop rewards +} + + +// /// @dev Struct to hold the execution fee configuration for a fee token +// struct FeeTokenConfig { +// uint32 networkFeeUSDCents; // ─────────╮ Flat network fee to charge for messages, multiples of 0.01 USD +// uint64 gasMultiplierWeiPerEth; // │ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost. +// uint64 premiumMultiplierWeiPerEth; // │ Multiplier for fee-token-specific premiums +// bool enabled; // ──────────────────────╯ Whether this fee token is enabled +// } + +// /// @dev Struct to hold the transfer fee configuration for token transfers +// struct TokenTransferFeeConfig { +// uint32 minFeeUSDCents; // ────╮ Minimum fee to charge per token transfer, multiples of 0.01 USD +// uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD +// uint16 deciBps; // │ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 +// uint32 destGasOverhead; // │ Gas charged to execute the token transfer on the destination chain +// uint32 destBytesOverhead; // ─╯ Extra data availability bytes on top of fixed transfer data, including sourceTokenData and offchainData +// } + + diff --git a/packages/ccip-js/test/helpers/utils.ts b/packages/ccip-js/test/helpers/utils.ts new file mode 100644 index 0000000..bdd23a2 --- /dev/null +++ b/packages/ccip-js/test/helpers/utils.ts @@ -0,0 +1,8 @@ +import { forkClient, testClient } from "./clients"; + +export const mineBlock = async (isFork: boolean) => { + const client = isFork? forkClient : testClient; + await client.mine({ + blocks: 1 + }) +}; \ No newline at end of file diff --git a/packages/ccip-js/test/helpers/write.ts b/packages/ccip-js/test/helpers/write.ts new file mode 100644 index 0000000..8f1c436 --- /dev/null +++ b/packages/ccip-js/test/helpers/write.ts @@ -0,0 +1,45 @@ +import { Address, getContract } from 'viem' +import { mineBlock } from './utils' +import { forkClient, testClient } from './clients' + +interface WriteOptions { + isFork: boolean + contractAddress: Address + abi: any +} + +// export const manifestContract = async ({ isFork, contractAddress, abi}: WriteOptions) => { +// const client = isFork? forkClient : testClient + +// await mineBlock(isFork) +// const contract = await getContract({ +// address: contractAddress, +// abi: abi, +// client: client, +// }) + +// return contract +// } + +// const contractAddress = await deployContract({ +// isFork, +// args: ['CCIP Burn & Mint Token', +// 'CCIP-BnM'], +// abi: abi, +// bin: `0x${bin}`, +// }) +// await mineBlock(isFork); +// bridgeTokenAddress = contractAddress as Address +// console.log({ bridgeTokenAddress }); +// if (bridgeTokenAddress) { +// const contract = getContract({ +// address: bridgeTokenAddress, +// abi: abi, +// client: testClient, +// }); +// tokenSupply = await contract.read.totalSupply() as bigint +// // console.log({ tokenSupply }) + +// await contract.write.drip([account.address]); +// // console.log({ tokenSupply }) +// await mineBlock(isFork); diff --git a/packages/ccip-js/test/integration.test.ts b/packages/ccip-js/test/integration.test.ts new file mode 100644 index 0000000..78a542f --- /dev/null +++ b/packages/ccip-js/test/integration.test.ts @@ -0,0 +1,423 @@ +import { jest, expect, it, describe, afterEach } from '@jest/globals' +import * as CCIP from '../src/api' +import * as Viem from 'viem' +import * as viemActions from 'viem/actions' +import { + Address, + encodeAbiParameters, + encodeFunctionData, + getContract, + parseEther, + zeroAddress, +} from 'viem' + +import { testClient } from './helpers/clients' +import { + account, + ccipLog, + ccipTxHash, + ccipTxReceipt, + onRampAbi, + routerAbi, +} from './helpers/constants' +import { getContracts, setOnRampAddress } from './helpers/contracts' +// getSupportedFeeTokens +import { mineBlock } from './helpers/utils' + +import { expect as expectChai } from 'chai' +import { getSupportedFeeTokens } from './helpers/config' +// import { readContract } from 'viem/actions' +// import { getTokenAdminRegistry } from './helpers/config' + +const ccipClient = CCIP.createClient() +const isFork = false + +const readContractMock = jest.spyOn(viemActions, 'readContract') +const writeContractMock = jest.spyOn(viemActions, 'writeContract') +const waitForTransactionReceiptMock = jest.spyOn(viemActions, 'waitForTransactionReceipt') +const parseEventLogsMock = jest.spyOn(Viem, 'parseEventLogs') + +describe('Integration', () => { + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('√ deploy on HH', () => { + it("Should Deploy Router.sol", async function () { + const { router } = await getContracts() + expectChai(router.address).to.not.equal(0); + }); + it("Should Deploy BridgeToken.sol", async function () { + const { bridgeToken } = await getContracts() + expectChai(bridgeToken.address).to.not.equal(0); + }); + it("Should Deploy CCIPLocalSimulator.sol", async function () { + const { localSimulator } = await getContracts() + expectChai(localSimulator.address).to.not.equal(0); + }) + + console.log('\u2705 | Deployed Smart Contracts on local Hardhat') + }) + + describe('√ approve', () => { + + it('√ should succeed with valid input', async () => { + const { bridgeToken, localSimulator, router } = await getContracts() + + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValue(ccipTxReceipt) + const approvedAmount = parseEther('10') + + // HH: Approval Transaction + await bridgeToken.write.approve([ + router.address, // spender + approvedAmount // amount + ]) + + // CCIP: Approval Transaction + const ccipApprove = await ccipClient.approveRouter({ + client: testClient, + routerAddress: router.address, + amount: approvedAmount, + tokenAddress: bridgeToken.address, + waitForReceipt: true, + }) + + expect(ccipApprove).toStrictEqual({ txHash: ccipTxHash, txReceipt: ccipTxReceipt }) + + console.log('\u2705 | Approves with valid input') + }) + + it('√ should get txReceipt if approve invoked with waitForReceipt', async () => { + // writeContractMock.mockResolvedValueOnce(ccipTxHash) + // waitForTransactionReceiptMock.mockResolvedValue(ccipTxReceipt) + const { bridgeToken, localSimulator, router } = await getContracts() + const approvedAmount = parseEther('0') + + const { txReceipt } = await ccipClient.approveRouter({ + client: testClient, + routerAddress: router.address, + amount: approvedAmount, + tokenAddress: bridgeToken.address, + waitForReceipt: true, + }) + + // @ts-ignore + const txHash = txReceipt.transactionHash + expect(txHash).toStrictEqual(ccipTxHash) + console.log('\u2705 | Gets txReceipt if approve invoked with waitForReceipt') + }) + + it('√ should not get txReceipt if approve invoked without waitForReceipt', async () => { + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + const { router } = await getContracts() + + const { txReceipt } = await ccipClient.approveRouter({ + client: testClient, + routerAddress: router.address, + amount: 0n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }) + + expect(txReceipt).toBe(undefined) + console.log('\u2705 | Does not get txReceipt if approve invoked without waitForReceipt') + }) + }) + + describe('√ getAllowance', () => { + it('√ should return the allowance for a given account', async () => { + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValue(ccipTxReceipt) + const { bridgeToken, router } = await getContracts() + const approvedAmount = parseEther('10') + + // HH: Approval Transaction + await bridgeToken.write.approve([ + router.address, // spender + approvedAmount // amount + ]) + + mineBlock(isFork) + + const hhApprovedAmount = await bridgeToken.read.allowance([ + account.address, // owner + router.address // spender + ]) + + await ccipClient.approveRouter({ + client: testClient, + routerAddress: router.address, + amount: approvedAmount, + tokenAddress: bridgeToken.address, + waitForReceipt: true, + }) + + const ccipApprovedAmount = await bridgeToken.read.allowance([ + account.address, // owner + router.address // spender + ]) + + expect(hhApprovedAmount).toBe(approvedAmount) + expect(ccipApprovedAmount).toBe(approvedAmount) + expect(hhApprovedAmount).toBe(ccipApprovedAmount) + + console.log('\u2705 | Returns the allowance for a given account') + }) + }) + + describe('√ getOnRampAddress', () => { + + it('√ should return the address of the onRamp contract', async () => { + const { router } = await getContracts() + const expectedOnRampAddress = '0x8F35B097022135E0F46831f798a240Cc8c4b0B01' + // HH OnRamp Address + const hhOnRampAddress = await setOnRampAddress({ + destinationChainSelector: '14767482510784806043', + }) + mineBlock(isFork) + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + + // CCIP OnRamp Address + const ccipOnRampAddress = await ccipClient.getOnRampAddress({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: '14767482510784806043', + }) + + expect(hhOnRampAddress).toBe(expectedOnRampAddress) + expect(ccipOnRampAddress).toBe(expectedOnRampAddress) + expect(hhOnRampAddress).toBe(ccipOnRampAddress) + console.log('\u2705 | Returns the address of the onRamp contract') + }) + }) + + describe('√ getSupportedFeeTokens', () => { + + it('√ should return supported fee tokens for valid chains', async () => { + const { router } = await getContracts() + const supportedFeeTokens = [ + '0x779877A7B0D9E8603169DdbD7836e478b4624789', + '0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534', + '0xc4bF5CbDaBE595361438F8c6a187bDc330539c60', + ] + + const readContractMock = jest.spyOn(viemActions, 'readContract') + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + readContractMock.mockResolvedValueOnce({ priceRegistry: '0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc' }) + readContractMock.mockResolvedValueOnce(supportedFeeTokens) + + const hhSupportedFeeTokens = await getSupportedFeeTokens() + mineBlock(isFork) + + const ccipSupportedFeeTokens = await ccipClient.getSupportedFeeTokens({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: "16015286601757825753", + }) + + expect(hhSupportedFeeTokens).toStrictEqual(supportedFeeTokens) + expect(ccipSupportedFeeTokens).toStrictEqual(supportedFeeTokens) + expect(ccipSupportedFeeTokens).toStrictEqual(hhSupportedFeeTokens) + console.log('\u2705 | Returns supported fee tokens for valid chains.') + }) + }) + + // describe('getFee', () => { + + // it('should return the correct fee for a transfer', async () => { + // const { router } = await getContracts() + // const expectedFee = 300000000000000n + // readContractMock.mockResolvedValueOnce(expectedFee) + // const data = encodeFunctionData({ + // abi: CCIP.IERC20ABI, + // functionName: 'transfer', + // args: [Viem.zeroAddress, Viem.parseEther('0.12')], + // }) + // const hhFee = await router.read.getFee([ + // '14767482510784806043', // destinationChainSelector: '14767482510784806043', + // encodeAbiParameters([{ type: 'string', name: 'data' }], ["Hello"]) // data: encodeAbiParameters([{ type: 'string', name: 'data' }], ["Hello"]) + // ]) as bigint + // mineBlock(isFork) + // console.log({ hhFee }) + // const ccipFee = await ccipClient.getFee({ + // client: testClient, + // routerAddress: router.address, + // destinationChainSelector: '14767482510784806043', + // destinationAccount: zeroAddress, + // amount: 1000000000000000000n, + // tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + // }) + // console.log({ ccipFee }) + // // expect(ccipFee).toEqual(expectedFee) + // }) + // }) + + // describe('getTokenAdminRegistry', () => { + // it('should return token admin registry', async () => { + // const routerAddress = router.address + // // await expect( + // // async () => { + // // const tokenAdminRegistry = await ccipClient.getTokenAdminRegistry({ + // // client: testClient, + // // routerAddress: routerAddress, + // // destinationChainSelector: '14767482510784806043', + // // tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + // // }) + // // console.log({tokenAdminRegistry}) + // // expect(tokenAdminRegistry).toBe('0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82') + // // } + // // ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + // }) + // }) + + describe('isTokenSupported', () => { + it('should return true if token is supported', async () => { + const { router } = await getContracts() + + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + linkToken: '0x779877A7B0D9E8603169DdbD7836e478b4624789', + chainSelector: 16015286601757825753n, + destChainSelector: 14767482510784806043n, + defaultTxGasLimit: 200000n, + maxNopFeesJuels: 100000000000000000000000000n, + prevOnRamp: '0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4', + rmnProxy: '0xba3f6251de62dED61Ff98590cB2fDf6871FbB991', + tokenAdminRegistry: '0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82', + }) + readContractMock.mockResolvedValueOnce('0xF081aCC599dFD65cdFD43934d2D8e2C7ad0277aE') + readContractMock.mockResolvedValueOnce(true) + + // const hhTokenSupported = await router.read.isTokenSupported([ + // '14767482510784806043', + // '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + // ]) + + // console.log({ hhTokenSupported }) + const ccipTokenSupported = await ccipClient.isTokenSupported({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: '14767482510784806043', + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }) + + expect(ccipTokenSupported).toBe(true) + }) + }) + + describe('transferTokens', () => { + it('should successfully transfer tokens with minimal input', async () => { + const { router } = await getContracts() + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + parseEventLogsMock.mockReturnValue(ccipLog as never) + mineBlock(isFork) + + // const hhTransfer = await router.write.ccipSend([ + // 14767482510784806043n, // destinationChainSelector + // zeroAddress // destinationAccount + // ]) + // mineBlock(isFork) + // console.log({ hhTransfer }) + + const transfer = await ccipClient.transferTokens({ + client: testClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + }) + expect(transfer.txHash).toEqual('0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e') + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should successfully transfer tokens with all inputs', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + parseEventLogsMock.mockReturnValue(ccipLog as never) + + const transfer = await ccipClient.transferTokens({ + client: testClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(transfer.txHash).toEqual('0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e') + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + }) + describe('sendCCIPMessage', () => { + + it('should successfully send message', async () => { + const { router } = await getContracts() + + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + parseEventLogsMock.mockReturnValue(ccipLog as never) + + const transfer = await ccipClient.sendCCIPMessage({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: '14767482510784806043', + destinationAccount: zeroAddress, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(transfer.txHash).toEqual(ccipTxHash) + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should get messageId on sendCCIPMessage', async () => { + const { bridgeToken, localSimulator, router } = await getContracts() + + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + parseEventLogsMock.mockReturnValue(ccipLog as never) + + const { messageId } = await ccipClient.sendCCIPMessage({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: '14767482510784806043', + destinationAccount: zeroAddress, + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + }) + }) + it('should send message with a function as data', async () => { + const { bridgeToken, localSimulator, router } = await getContracts() + + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(ccipTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(ccipTxReceipt) + parseEventLogsMock.mockReturnValue(ccipLog as never) + + const { messageId } = await ccipClient.sendCCIPMessage({ + client: testClient, + routerAddress: router.address, + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + data: Viem.encodeFunctionData({ + abi: CCIP.IERC20ABI, + functionName: 'transfer', + args: [Viem.zeroAddress, Viem.parseEther('0.12')], + }), + }) + expect(messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + }) +}) diff --git a/packages/ccip-js/test/unit.test.ts b/packages/ccip-js/test/unit.test.ts new file mode 100644 index 0000000..9ad7040 --- /dev/null +++ b/packages/ccip-js/test/unit.test.ts @@ -0,0 +1,1125 @@ +import { jest, expect, it, describe, afterEach } from '@jest/globals' +import * as CCIP from '../src/api' +import * as Viem from 'viem' +import * as viemActions from 'viem/actions' +import { sepolia } from 'viem/chains' +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' +import { forkClient } from './helpers/clients' + +const ccipClient = CCIP.createClient() + +const readContractMock = jest.spyOn(viemActions, 'readContract') +const writeContractMock = jest.spyOn(viemActions, 'writeContract') +const waitForTransactionReceiptMock = jest.spyOn(viemActions, 'waitForTransactionReceipt') +const getLogsMock = jest.spyOn(viemActions, 'getLogs') +const parseEventLogsMock = jest.spyOn(Viem, 'parseEventLogs') + +const mockTxHash = '0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e' +const mockTxReceipt: Viem.TransactionReceipt = { + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + contractAddress: null, + cumulativeGasUsed: 288318n, + effectiveGasPrice: 25000000001n, + from: '0x748cab9a6993a24ca6208160130b3f7b79098c6d', + gasUsed: 288318n, + logs: [ + { + address: '0x0b9d5d9136855f6fec3c0993fee6e9ce8a297846', + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + data: '0x0000000000000000000000000000000000000000000000000070c7afd6bb1899', + logIndex: 0, + removed: false, + topics: [ + '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', + '0x000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d', + '0x000000000000000000000000a9946ba30daec98745755e4410d6e8e894edc53b', + ], + transactionHash: '0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e', + transactionIndex: 0, + }, + { + address: '0x70f5c5c40b873ea597776da2c21929a8282a3b35', + blockHash: '0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b', + blockNumber: 36381676n, + data: '0x0000000000000000000000000000000000000000000000000000000000000000', + logIndex: 1, + removed: false, + topics: [ + '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925', + '0x000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d', + '0x000000000000000000000000f694e193200268f9a4868e4aa017a0118c9a8177', + ], + transactionHash: '0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e', + transactionIndex: 0, + }, + ], + logsBloom: + '0x01000000000000000001000000000004000000000000800000000000000000000000000008000000002000000000000000000000000000000000080000200000000000020000020000000008000000000100100000000000000010000000000000000000060000000000004000000802040000000000000000001050000000000002000000000000200000000000000400000000000080000800040000000000020000000000200600000000000000000080000000000000008100000000008100000002000000000001000000000000000000000000000800000000000024000010000000000000000000000000000004000200000000000000000000000000', + status: 'success', + to: '0xf694e193200268f9a4868e4aa017a0118c9a8177', + transactionHash: '0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e', + transactionIndex: 0, + type: 'eip1559', +} +const mockLog = [ + { + address: '0x0aec1ac9f6d0c21332d7a66ddf1fbcb32cf3b0b3' as Viem.Address, + blockHash: '0xd8a5943213a52e0e453c0c7ffe921f3c4c84b15ee02915e59ada0d058f33ab2a' as Viem.Hash, + args: { + message: { + data: '0x0000000000000000000000000000000000000000000000000000000000000000', + feeToken: '0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846', + feeTokenAmount: 317861394337043364n, + gasLimit: 0n, + messageId: '0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062', + nonce: 7n, + receiver: '0x748Cab9A6993A24CA6208160130b3f7b79098c6d', + sender: '0x748Cab9A6993A24CA6208160130b3f7b79098c6d', + sequenceNumber: 1265n, + sourceChainSelector: 14767482510784806043n, + }, + }, + blockNumber: 36381795n, + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000ccf0a31a221f3c9b000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d00000000000000000000000000000000000000000000000000000000000004f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000b9d5d9136855f6fec3c0993fee6e9ce8a29784600000000000000000000000000000000000000000000000004694541094513a400000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240de438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf06200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070f5c5c40b873ea597776da2c21929a8282a3b35000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000015f9000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008e35eb0dfb39ec5f84254c3f863986a913171e0b0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c0000000000000000000000000000000000000000000000000000000000000000', + eventName: 'CCIPSendRequested', + logIndex: 8, + removed: false, + topics: ['0xd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd' as Viem.Address], + transactionHash: '0xb96c7d676f69e904fe283312386f375352e407446b7b0c4b8452d09a13cc6b10', + transactionIndex: 0, + }, +] +const mockLogWOMessageId = [ + { + address: '0x0aec1ac9f6d0c21332d7a66ddf1fbcb32cf3b0b3' as Viem.Address, + blockHash: '0xd8a5943213a52e0e453c0c7ffe921f3c4c84b15ee02915e59ada0d058f33ab2a' as Viem.Hash, + args: {}, + blockNumber: 36381795n, + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000ccf0a31a221f3c9b000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d000000000000000000000000748cab9a6993a24ca6208160130b3f7b79098c6d00000000000000000000000000000000000000000000000000000000000004f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000b9d5d9136855f6fec3c0993fee6e9ce8a29784600000000000000000000000000000000000000000000000004694541094513a400000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240de438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf06200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070f5c5c40b873ea597776da2c21929a8282a3b35000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000015f9000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008e35eb0dfb39ec5f84254c3f863986a913171e0b0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c0000000000000000000000000000000000000000000000000000000000000000', + eventName: 'CCIPSendRequested', + logIndex: 8, + removed: false, + topics: ['0xd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd' as Viem.Address], + transactionHash: '0xb96c7d676f69e904fe283312386f375352e407446b7b0c4b8452d09a13cc6b10', + transactionIndex: 0, + }, +] + +describe('Unit', () => { + afterEach(() => { + jest.clearAllMocks() + }) + describe('approve router', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.approveRouter({ + client: forkClient, + routerAddress: Viem.zeroAddress, + amount: 0n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject with invalid amount', async () => { + await expect( + async () => + await ccipClient.approveRouter({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + amount: -1n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }), + ).rejects.toThrow('Invalid approve amount. Amount can not be negative') + }) + + it('should reject with invalid token address', async () => { + await expect( + async () => + await ccipClient.approveRouter({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + amount: 0n, + tokenAddress: Viem.zeroAddress, + }), + ).rejects.toThrow('Token address 0x0000000000000000000000000000000000000000 is not valid') + }) + + // it('should reject with invalid account', async () => { + // await expect( + // async () => + // await ccipClient.approveRouter({ + // client: walletClientWOAccount, + // routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + // amount: 0n, + // tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + // waitForReceipt: true, + // }), + // ).rejects.toThrow('Account address is not valid') + // }) + + it('should succeed with valid input', async () => { + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValue(mockTxReceipt) + + const approve = await ccipClient.approveRouter({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + amount: 0n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + waitForReceipt: true, + }) + + expect(approve).toStrictEqual({ txHash: mockTxHash, txReceipt: mockTxReceipt }) + }) + + it('should get txReceipt if approve invoked with waitForReceipt', async () => { + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValue(mockTxReceipt) + + const { txReceipt } = await ccipClient.approveRouter({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + amount: 0n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + waitForReceipt: true, + }) + + expect(txReceipt).toStrictEqual(mockTxReceipt) + }) + + it('should not get txReceipt if approve invoked without waitForReceipt', async () => { + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + + const { txReceipt } = await ccipClient.approveRouter({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + amount: 0n, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }) + + expect(txReceipt).toBe(undefined) + }) + }) + + describe('getAllowance', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.getAllowance({ + client: forkClient, + routerAddress: Viem.zeroAddress, + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + account: '0x0000000000000000000000000000000000000001', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject with invalid account address', async () => { + await expect( + async () => + await ccipClient.getAllowance({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + account: Viem.zeroAddress, + }), + ).rejects.toThrow('Account address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject with invalid token address', async () => { + await expect( + async () => + await ccipClient.getAllowance({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + tokenAddress: Viem.zeroAddress, + account: '0x0000000000000000000000000000000000000001', + }), + ).rejects.toThrow('Token address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should return the allowance for a given account', async () => { + readContractMock.mockResolvedValueOnce(1000000000000000000n) + const allowance = await ccipClient.getAllowance({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + account: '0x0000000000000000000000000000000000000001', + }) + expect(allowance).toBe(1000000000000000000n) + }) + }) + + describe('getOnRampAddress', () => { + it('should reject if onRamp is not valid', async () => { + await expect( + async () => + await ccipClient.getOnRampAddress({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '0', + }), + ).rejects.toThrow('OnRamp address is not valid. Execution can not be continued') + }) + it('should reject if router address is not valid', async () => { + await expect( + async () => + await ccipClient.getOnRampAddress({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '0', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + it('should return the address of the onRamp contract', async () => { + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + const onRampAddress = await ccipClient.getOnRampAddress({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + }) + expect(onRampAddress).toBe('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + }) + }) + + describe('getSupportedFeeTokens', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.getSupportedFeeTokens({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if onRamp is not valid', async () => { + readContractMock.mockResolvedValueOnce(Viem.zeroAddress) + await expect( + async () => + await ccipClient.getSupportedFeeTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '0', + }), + ).rejects.toThrow('OnRamp address is not valid. Execution can not be continued') + }) + + it('should return supported fee tokens for valid chains', async () => { + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + readContractMock.mockResolvedValueOnce({ priceRegistry: '0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc' }) + readContractMock.mockResolvedValueOnce([ + '0x779877A7B0D9E8603169DdbD7836e478b4624789', + '0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534', + '0xc4bF5CbDaBE595361438F8c6a187bDc330539c60', + ]) + + const supportedFeeTokens = await ccipClient.getSupportedFeeTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + }) + + expect(supportedFeeTokens).toStrictEqual([ + '0x779877A7B0D9E8603169DdbD7836e478b4624789', + '0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534', + '0xc4bF5CbDaBE595361438F8c6a187bDc330539c60', + ]) + }) + }) + describe('getLaneRateRefillLimits', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.getLaneRateRefillLimits({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if onRamp is not valid', async () => { + await expect( + async () => + await ccipClient.getLaneRateRefillLimits({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '0', + }), + ).rejects.toThrow('OnRamp address is not valid. Execution can not be continued') + }) + + it('should return lane rate refill limits for valid chains', async () => { + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + readContractMock.mockResolvedValueOnce({ + tokens: 0n, + lastUpdated: 1729679256, + isEnabled: false, + capacity: 0n, + rate: 0n, + }) + + const laneRateRefillLimits = await ccipClient.getLaneRateRefillLimits({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + }) + + expect(laneRateRefillLimits).toStrictEqual({ + tokens: 0n, + lastUpdated: 1729679256, + isEnabled: false, + capacity: 0n, + rate: 0n, + }) + }) + }) + + describe('getTokenRateLimitByLane', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.getTokenRateLimitByLane({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + supportedTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if token address is not valid', async () => { + await expect( + async () => + await ccipClient.getTokenRateLimitByLane({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + supportedTokenAddress: Viem.zeroAddress, + }), + ).rejects.toThrow( + 'Token address 0x0000000000000000000000000000000000000000 is not valid. Execution can not be continued', + ) + }) + + it('should reject if onRamp is not valid', async () => { + await expect(async () => + ccipClient.getTokenRateLimitByLane({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '0', + supportedTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('OnRamp address is not valid. Execution can not be continued') + }) + + it('should reject if laneTokenTransferPool is not set or zero-address', async () => { + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + readContractMock.mockResolvedValueOnce(Viem.zeroAddress) + await expect(async () => + ccipClient.getTokenRateLimitByLane({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '0', + supportedTokenAddress: '0xc4bF5CbDaBE595361438F8c6a187bDc330539c60', + }), + ).rejects.toThrow( + 'Token pool for 0xc4bF5CbDaBE595361438F8c6a187bDc330539c60 is missing. Execution can not be continued', + ) + }) + + it('should return token rate limit by lane for a supported token', async () => { + readContractMock.mockResolvedValueOnce('0x8F35B097022135E0F46831f798a240Cc8c4b0B01') + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + tokens: 0n, + lastUpdated: 1729679256, + isEnabled: false, + capacity: 0n, + rate: 0n, + }) + + const tokenRateLimitByLane = await ccipClient.getTokenRateLimitByLane({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + supportedTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }) + + expect(tokenRateLimitByLane).toStrictEqual({ + tokens: 0n, + lastUpdated: 1729679256, + isEnabled: false, + capacity: 0n, + rate: 0n, + }) + }) + }) + + describe('getFee', () => { + it('should reject with invalid router address', async () => { + await expect(async () => + ccipClient.getFee({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if token address is not valid', async () => { + await expect(async () => + ccipClient.getFee({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: Viem.zeroAddress, + }), + ).rejects.toThrow('Token address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if amount is not valid', async () => { + await expect(async () => + ccipClient.getFee({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: -1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Invalid amount. Amount can not be negative') + }) + + it('should reject if destination account address is not valid', async () => { + await expect(async () => + ccipClient.getFee({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: 'address' as Viem.Address, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('address is not a valid destionation account address') + }) + + it('should reject if fee token is not valid', async () => { + await expect(async () => + ccipClient.getFee({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: 'address' as Viem.Address, + }), + ).rejects.toThrow('PARAMETER INPUT ERROR: address is not a valid fee token address') + }) + + it('should return the correct fee for a transfer', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + const fee = await ccipClient.getFee({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }) + + expect(fee).toEqual(300000000000000n) + }) + }) + + describe('getTransactionReceipt', () => { + it('should reject if hash is invalid', async () => { + await expect(async () => + ccipClient.getTransactionReceipt({ + client: forkClient, + hash: 'hash' as Viem.Hash, + }), + ).rejects.toThrow('hash is not a valid transaction hash') + }) + + it('should return the status of a transfer', async () => { + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + const transferStatus = await ccipClient.getTransactionReceipt({ + client: forkClient, + hash: '0xc94dff6318a839d806aaff3bbf32cfe5898319ad4af25ecfbc24fa09b0ef0d4d', + }) + + expect(transferStatus.blockHash).toBe('0xeb3e2e65c939bd65d6983704a21dda6ae7157079b1e6637ff11bb97228accdc2') + }) + }) + + describe('getTransferStatus', () => { + it('should reject with invalid message id', async () => { + await expect(async () => + ccipClient.getTransferStatus({ + client: forkClient, + messageId: 'hash' as Viem.Hash, + destinationRouterAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + sourceChainSelector: '14767482510784806043', + }), + ).rejects.toThrow('hash is not a valid message ID') + }) + + it('should reject with invalid destination router address', async () => { + await expect(async () => + ccipClient.getTransferStatus({ + client: forkClient, + messageId: '0xc94dff6318a839d806aaff3bbf32cfe5898319ad4af25ecfbc24fa09b0ef0d4d', + destinationRouterAddress: Viem.zeroAddress, + sourceChainSelector: '14767482510784806043', + }), + ).rejects.toThrow(`Destination router address ${Viem.zeroAddress} is not valid`) + }) + + it('should reject with invalid source chain selector', async () => { + await expect(async () => + ccipClient.getTransferStatus({ + client: forkClient, + messageId: '0xc94dff6318a839d806aaff3bbf32cfe5898319ad4af25ecfbc24fa09b0ef0d4d', + destinationRouterAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + sourceChainSelector: '', + }), + ).rejects.toThrow('Source chain selector is missing or invalid') + }) + + it('should reject if no matching off-ramps found', async () => { + readContractMock.mockResolvedValueOnce([{ offRamp: Viem.zeroAddress, sourceChainSelector: 69n }]) + await expect(async () => + ccipClient.getTransferStatus({ + client: forkClient, + messageId: '0xc94dff6318a839d806aaff3bbf32cfe5898319ad4af25ecfbc24fa09b0ef0d4d', + destinationRouterAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + sourceChainSelector: '1', + }), + ).rejects.toThrow('No matching off-ramp found') + }) + + it('should return null if the status of a transfer is undefined', async () => { + readContractMock.mockResolvedValueOnce([ + { + sourceChainSelector: 9284632837123596123n, + offRamp: '0x46b639a3C1a4CBfD326b94a2dB7415c27157282f', + }, + { + sourceChainSelector: 14767482510784806043n, + offRamp: '0x000b26f604eAadC3D874a4404bde6D64a97d95ca', + }, + { + sourceChainSelector: 2027362563942762617n, + offRamp: '0x4e897e5cF3aC307F0541B2151A88bCD781c153a3', + }, + ]) + const transferStatus = await ccipClient.getTransferStatus({ + client: forkClient, + messageId: Viem.zeroHash, + destinationRouterAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + sourceChainSelector: '14767482510784806043', + }) + + expect(transferStatus).toBe(null) + }) + + it('should return the status of a transfer', async () => { + readContractMock.mockResolvedValueOnce([ + { + sourceChainSelector: 9284632837123596123n, + offRamp: '0x46b639a3C1a4CBfD326b94a2dB7415c27157282f', + }, + { + sourceChainSelector: 14767482510784806043n, + offRamp: '0x000b26f604eAadC3D874a4404bde6D64a97d95ca', + }, + { + sourceChainSelector: 2027362563942762617n, + offRamp: '0x4e897e5cF3aC307F0541B2151A88bCD781c153a3', + }, + ]) + getLogsMock.mockResolvedValueOnce([ + { + address: '0x3ab3a3d35cac95ffcfccc127ef01ea8d87b0a64e', + args: { + sequenceNumber: 1266n, + messageId: '0xa59eff480402ef673b5edf4bb52ff7c2f18426dc59cf3295b525f14b0779186a', + state: 2, + returnData: '0x', + }, + blockHash: '0xf160e33291d0b85251fbb52cd395cc374a096ca1f0724a8e7731aab33ab8b9c2', + blockNumber: 16962416n, + data: '0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000', + logIndex: 33, + transactionHash: '0x29e10b1e1fa029d378f573c3c11d979e139894482a5e1835970d2ec57a403f58', + transactionIndex: 14, + removed: false, + eventName: 'ExecutionStateChanged', + topics: [], + }, + ]) + + const transferStatus = await ccipClient.getTransferStatus({ + client: forkClient, + messageId: '0xc94dff6318a839d806aaff3bbf32cfe5898319ad4af25ecfbc24fa09b0ef0d4d', + destinationRouterAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + sourceChainSelector: '14767482510784806043', + }) + + expect(transferStatus).toBe(2) + }) + }) + + describe('getTokenAdminRegistry', () => { + it('should reject with invalid router address', async () => { + await expect( + async () => + await ccipClient.getTokenAdminRegistry({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + it('should reject with invalid token address', async () => { + await expect( + async () => + await ccipClient.getTokenAdminRegistry({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + tokenAddress: Viem.zeroAddress, + }), + ).rejects.toThrow('Token address 0x0000000000000000000000000000000000000000 is not valid') + }) + it('should reject if token admin registry address is not valid', async () => { + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + linkToken: '0x', + chainSelector: 0n, + destChainSelector: 0n, + defaultTxGasLimit: 0n, + maxNopFeesJuels: 0n, + prevOnRamp: '0x', + rmnProxy: '0x', + tokenAdminRegistry: '0x', + }) + await expect( + async () => + await ccipClient.getTokenAdminRegistry({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + tokenAddress: '0x1525C31ebf98c8b0C90c59e04f7d04F421c1b746', + }), + ).rejects.toThrow('Token admin registry address is not valid. Execution can not be continued') + }) + }) + + describe('isTokenSupported', () => { + it('should return true if token is supported', async () => { + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + linkToken: '0x779877A7B0D9E8603169DdbD7836e478b4624789', + chainSelector: 16015286601757825753n, + destChainSelector: 14767482510784806043n, + defaultTxGasLimit: 200000n, + maxNopFeesJuels: 100000000000000000000000000n, + prevOnRamp: '0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4', + rmnProxy: '0xba3f6251de62dED61Ff98590cB2fDf6871FbB991', + tokenAdminRegistry: '0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82', + }) + readContractMock.mockResolvedValueOnce('0xF081aCC599dFD65cdFD43934d2D8e2C7ad0277aE') + readContractMock.mockResolvedValueOnce(true) + + const isTokenSupported = await ccipClient.isTokenSupported({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + tokenAddress: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + }) + + expect(isTokenSupported).toBe(true) + }) + + it('should return false if token is not supported on the destination chain', async () => { + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + linkToken: '0x779877A7B0D9E8603169DdbD7836e478b4624789', + chainSelector: 16015286601757825753n, + destChainSelector: 14767482510784806043n, + defaultTxGasLimit: 200000n, + maxNopFeesJuels: 100000000000000000000000000n, + prevOnRamp: '0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4', + rmnProxy: '0xba3f6251de62dED61Ff98590cB2fDf6871FbB991', + tokenAdminRegistry: '0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82', + }) + readContractMock.mockResolvedValueOnce('0xF081aCC599dFD65cdFD43934d2D8e2C7ad0277aE') + readContractMock.mockResolvedValueOnce(false) + + const isTokenSupported = await ccipClient.isTokenSupported({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + tokenAddress: '0x1525C31ebf98c8b0C90c59e04f7d04F421c1b746', + }) + + expect(isTokenSupported).toBe(false) + }) + + it('should return false if there is no pool for the token', async () => { + readContractMock.mockResolvedValueOnce('0x12492154714fBD28F28219f6fc4315d19de1025B') + readContractMock.mockResolvedValueOnce({ + linkToken: '0x779877A7B0D9E8603169DdbD7836e478b4624789', + chainSelector: 16015286601757825753n, + destChainSelector: 14767482510784806043n, + defaultTxGasLimit: 200000n, + maxNopFeesJuels: 100000000000000000000000000n, + prevOnRamp: '0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4', + rmnProxy: '0xba3f6251de62dED61Ff98590cB2fDf6871FbB991', + tokenAdminRegistry: '0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82', + }) + readContractMock.mockResolvedValueOnce('0x') + + const isTokenSupported = await ccipClient.isTokenSupported({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '1111111111111', + tokenAddress: '0x1525C31ebf98c8b0C90c59e04f7d04F421c1b746', + }) + + expect(isTokenSupported).toBe(false) + }) + }) + + describe('transferTokens', () => { + it('should reject with invalid router address', async () => { + await expect(async () => + ccipClient.transferTokens({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if token address is not valid', async () => { + await expect(async () => + ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: Viem.zeroAddress, + }), + ).rejects.toThrow('Token address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if amount is not valid', async () => { + await expect(async () => + ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 0n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('Invalid amount. Amount must be greater than 0') + }) + + it('should reject if destination account address is not valid', async () => { + await expect(async () => + ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: 'address' as Viem.Address, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }), + ).rejects.toThrow('address is not a valid destionation account address') + }) + + it('should reject if fee token is not valid', async () => { + await expect( + async () => + await ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + amount: 1000000000000000000n, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: 'address' as Viem.Address, + }), + ).rejects.toThrow('PARAMETER INPUT ERROR: address is not a valid fee token address') + }) + + it('should successfully transfer tokens with minimal input', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const transfer = await ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + }) + expect(transfer.txHash).toEqual('0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e') + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should successfully transfer tokens with all inputs', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const transfer = await ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(transfer.txHash).toEqual('0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e') + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should get txReceipt if transferTokens invoked', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const { txReceipt } = await ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + }) + + expect(txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should throw if messageId can not be retrieved', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLogWOMessageId as never) + + await expect(async () => + ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + waitForTransactionReceiptParameters: { + confirmations: 3, + }, + }), + ).rejects.toThrow('Message ID not found in the transaction logs') + }) + + it('should get messageId on transferTokens', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const { messageId } = await ccipClient.transferTokens({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + tokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + amount: 1000000000000000000n, + }) + expect(messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + }) + }) + + describe('sendCCIPMessage', () => { + it('should reject with invalid router address', async () => { + await expect(async () => + ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: Viem.zeroAddress, + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }), + ).rejects.toThrow('Router address 0x0000000000000000000000000000000000000000 is not valid') + }) + + it('should reject if destination account address is not valid', async () => { + await expect(async () => + ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: 'address' as Viem.Address, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }), + ).rejects.toThrow('address is not a valid destionation account address') + }) + + it('should reject if fee token is not valid', async () => { + await expect( + async () => + await ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + feeTokenAddress: 'address' as Viem.Address, + }), + ).rejects.toThrow('PARAMETER INPUT ERROR: address is not a valid fee token address') + }) + + // it('should reject with invalid account', async () => { + // await expect( + // async () => + // await ccipClient.sendCCIPMessage({ + // client: walletClientWOAccount, + // routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + // destinationChainSelector: '14767482510784806043', + // destinationAccount: Viem.zeroAddress, + // data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + // }), + // ).rejects.toThrow('Account address is not valid') + // }) + + it('should successfully send message', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const transfer = await ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(transfer.txHash).toEqual('0xc55d92b1212dd24db843e1cbbcaebb1fffe3cd1751313e0fd02cf26bf72b359e') + expect(transfer.messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + expect(transfer.txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should get txReceipt if transferTokens invoked', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const { txReceipt } = await ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + }) + + expect(txReceipt.blockHash).toEqual('0x565f99df4e32e15432f44c19b3d1d15447c41ca185a09aaf8d53356ce4086d8b') + }) + + it('should throw if messageId can not be retrieved', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLogWOMessageId as never) + + await expect(async () => + ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + waitForTransactionReceiptParameters: { + confirmations: 3, + }, + }), + ).rejects.toThrow('Message ID not found in the transaction logs') + }) + + it('should get messageId on sendCCIPMessage', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const { messageId } = await ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + data: Viem.encodeAbiParameters([{ type: 'string', name: 'data' }], ['Hello']), + }) + expect(messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + }) + + it('should send message with a function as data', async () => { + readContractMock.mockResolvedValueOnce(300000000000000n) + writeContractMock.mockResolvedValueOnce(mockTxHash) + waitForTransactionReceiptMock.mockResolvedValueOnce(mockTxReceipt) + parseEventLogsMock.mockReturnValue(mockLog as never) + + const { messageId } = await ccipClient.sendCCIPMessage({ + client: forkClient, + routerAddress: '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59', + destinationChainSelector: '14767482510784806043', + destinationAccount: Viem.zeroAddress, + feeTokenAddress: '0x94095e6514411C65E7809761F21eF0febe69A977', + data: Viem.encodeFunctionData({ + abi: CCIP.IERC20ABI, + functionName: 'transfer', + args: [Viem.zeroAddress, Viem.parseEther('0.12')], + }), + }) + expect(messageId).toEqual('0xde438245515b78c2294263a821316b5d5b49af90464dafcedaf13901050bf062') + }) + }) +}) diff --git a/packages/ccip-js/tsconfig.json b/packages/ccip-js/tsconfig.json new file mode 100644 index 0000000..752b76f --- /dev/null +++ b/packages/ccip-js/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "lib": ["es2020", "esnext"], + "target": "ES2020", + "module": "esnext", + "moduleResolution": "node10", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "resolveJsonModule": true, + "sourceMap": true + }, + "include": ["src"] +} \ No newline at end of file diff --git a/packages/ccip-react-components/.eslintrc.cjs b/packages/ccip-react-components/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/packages/ccip-react-components/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/packages/ccip-react-components/.gitignore b/packages/ccip-react-components/.gitignore new file mode 100644 index 0000000..93c859f --- /dev/null +++ b/packages/ccip-react-components/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +coverage \ No newline at end of file diff --git a/packages/ccip-react-components/LICENSE b/packages/ccip-react-components/LICENSE new file mode 100644 index 0000000..0d68762 --- /dev/null +++ b/packages/ccip-react-components/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2024 SmartContract ChainLink Limited SEZC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/ccip-react-components/README.md b/packages/ccip-react-components/README.md new file mode 100644 index 0000000..722a55e --- /dev/null +++ b/packages/ccip-react-components/README.md @@ -0,0 +1,361 @@ +# CCIP-REACT-COMPONENTS + +The CCIP-REACT-COMPONENTS package is a set of prebuilt ready-to-use UI components built on top of [CCIP-JS](../ccip-js/README.md). Using both packages, you can add a fully featured CCIP bridge to your app that can be styled to match your app design. + +## Table of contents + +- [CCIP-REACT-COMPONENTS](#ccip-react-components) + - [Table of contents](#table-of-contents) + - [Installation](#installation) + - [Quick start](#quick-start) + - [Tokens](#tokens) + - [Config](#config) + - [Chains](#chains) + - [Preselected chains](#preselected-chains) + - [Preselect token](#preselect-token) + + - [Wallets](#wallets) + - [Theme](#theme) + - [Variants](#variants) + - [Drawer](#drawer) + + - [Contributing](#contributing) + - [License](#license) + +## Installation + +Install `@chainlink/ccip-react-components`: + +Using NPM: + +```sh +npm install @chainlink/ccip-react-components +``` + +Using Yarn: + +```sh +yarn add @chainlink/ccip-react-components +``` + +Using PNPM: + +```sh +pnpm add @chainlink/ccip-react-components +``` + +## Quick start + +```tsx +import 'ccip-react-components/dist/style.css'; +import { CCIPWidget, Config, Token } from 'ccip-react-components'; +import { sepolia, optimismSepolia } from 'viem/chains'; + +const tokensList: Token[] = [ + { + symbol: 'CCIP-BnM', + address: { + [arbitrumSepolia.id]:'0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D', + [avalancheFuji.id]: '0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4', + [baseSepolia.id]: '0x88A2d74F47a237a62e7A51cdDa67270CE381555e', + [bscTestnet.id]: '0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9', + [optimismSepolia.id]: '0x8aF4204e30565DF93352fE8E1De78925F6664dA7', + [polygonAmoy.id]: '0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4', + [sepolia.id]: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05' + }, + logoURL: 'https://smartcontract.imgix.net/tokens/ccip-bnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + }, + { + symbol: 'CCIP-LnM', + address: { + [optimismSepolia.id]: '0x044a6B4b561af69D2319A2f4be5Ec327a6975D0a', + [sepolia.id]: '0x466D489b6d36E7E3b824ef491C225F5830E81cC1' + }, + logoURL: 'https://smartcontract.imgix.net/tokens/ccip-lnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + } +]; + +const config: Config = { + theme: { + pallette: { + background: '#FFFFFF', + border: '#B3B7C0', + text: '#000000', + } + shape: { + radius: 6 + }, + } +}; + +; +``` + +## Tokens + +You should provide a list of tokens that your app will support transferring. For a list of all currently supported tokens, refer to the [CCIP Supported Networks](https://docs.chain.link/ccip/supported-networks) page. Learn [how to get CCIP Test Tokens](https://docs.chain.link/ccip/test-tokens#mint-tokens-in-a-block-explorer). + +```tsx +import { CCIPWidget, Token } from 'ccip-react-components'; +import { sepolia, optimismSepolia } from 'viem/chains'; + +const tokensList: Token[] = [ + { + symbol: 'CCIP-BnM', + address: { + [arbitrumSepolia.id]:'0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D', + [avalancheFuji.id]: '0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4', + [baseSepolia.id]: '0x88A2d74F47a237a62e7A51cdDa67270CE381555e', + [bscTestnet.id]: '0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9', + [optimismSepolia.id]: '0x8aF4204e30565DF93352fE8E1De78925F6664dA7', + [polygonAmoy.id]: '0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4', + [sepolia.id]: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05' + }, + logoURL: 'https://smartcontract.imgix.net/tokens/ccip-bnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + }, + { + symbol: 'CCIP-LnM', + address: { + [optimismSepolia.id]: '0x044a6B4b561af69D2319A2f4be5Ec327a6975D0a', + [sepolia.id]: '0x466D489b6d36E7E3b824ef491C225F5830E81cC1', + }, + logoURL: 'https://smartcontract.imgix.net/tokens/ccip-lnm.webp?auto=compress%2Cformat', + tags: ['chainlink', 'default'] + }, +]; + + +``` + +`tokensList` accepts an array of `Token` objects + +```ts +export declare type TokenMap = { + [chainId: number]: Address | undefined; +} + +export declare type Token = { + /** The token's symbol that will be shown in the UI */ + symbol: string; + /** The token's address, represented as a mapping by chainId */ + address: TokenMap; + /** URL of the token's logo that will be shown in the UI */ + logoURL: string; + /** A list of meta information tags for organizing and sorting (optional) */ + tags?: string[]; +}; + +type TokenMap = { + [chainId: number]: Address | undefined; +}; +``` + +## Config + +All `Config` properties are optional. + +```tsx +import { CCIPWidget, Config } from 'ccip-react-components'; + +const config: Config = { + ... +}; + +; +``` + +### Chains + +`allow` and `deny` configuration options are provided to control which chains can be used. + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // Only Ethereum and Avalanche will be available + chains: { allow: [mainnet.id, avalanche.id] }, +}; +``` + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // All chains except Ethereum and Avalanche will be available + chains: { deny: [mainnet.id, avalanche.id] }, +}; +``` + +You can also specify source chains (chains from which transfers can be sent) and destination chains (chains from which transfers can be received). + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // Transfers can be sent only from Ethereum and Avalanche + chains: { from: { allow: [mainnet.id, avalanche.id] } }, +}; +``` + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // Transfers can be sent from all chains except Ethereum and Avalanche + chains: { from: { deny: [mainnet.id, avalanche.id] } }, +}; +``` + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // Transfers can be received only to Ethereum and Avalanche + chains: { to: { allow: [mainnet.id, avalanche.id] } }, +}; +``` + +```typescript +import { mainnet, avalanche } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { + // Transfers can be received on all chains except Ethereum and Avalanche + chains: { to: { deny: [mainnet.id, avalanche.id] } }, +}; +``` + +#### Preselected chains + +You can configure default chains: as soon as the component loads, the chains will display as preselected. + +Preselect a chain to send a transfer from: + +```typescript +import { mainnet } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { fromChain: mainnet.id }; +``` + +Preselect a chain to receive a transfer: + +```typescript +import { mainnet } from 'viem/chains'; +import { Config } from 'ccip-react-components'; +const config: Config = { toChain: mainnet.id }; +``` + +#### Preselect Token + +You can also specify a default token to display as preselected: + +```typescript +import { Config } from 'ccip-react-components'; +const config: Config = { token: 'USDC' }; +``` + +### Wallets + +If needed, you can pass additional wallet configurations: + +```typescript +import { Config } from 'ccip-react-components'; +const config: Config = { walletConfig: { + /** Configure the connection options for Injected Connectors + * More info: https://wagmi.sh/react/api/connectors/injected + */ + injected?: InjectedParameters; + /** Configure the connection options for MetaMask + * More info: https://wagmi.sh/react/api/connectors/metaMask + */ + metamask?: MetaMaskParameters; + /** Configure the connection options for Wallet Connect + * NOTE: A valid projectId should be provided in order to use Wallet Connect! + * More info: https://wagmi.sh/react/api/connectors/walletConnect + */ + walletConnect?: WalletConnectParameters; + /** Configure the connection options for Coinbase Wallet + * More info: https://wagmi.sh/react/api/connectors/coinbaseWallet + */ + coinbaseWallet?: CoinbaseWalletParameters; +} }; +``` + +### Theme + +You can customize the component's theme to be in line with your app design. + +```typescript +import { Config } from 'ccip-react-components'; +const config: Config = { theme: + { + /** Define the app colors in HEX format */ + palette?: { + /** Titles color and primary button background, default #000000 */ + primary?: string; + /** Background color, default '#FFFFFF' */ + background?: string; + /** Border color, default '#B3B7C0' */ + border?: string; + /** Text color, default '#000000' */ + text?: string; + /** Secondary text, inactive and placeholders color, default '#6D7480' */ + muted?: string; + /** Input fields background color, default '#FFFFFF' */ + input?: string; + /** Popovers, dropdowns and select fields background color, default '#F5F7FA' */ + popover?: string; + /** Selected field from a dropdown background color, default '#D7DBE0' */ + selected?: string; + /** Warning text color, default '#F7B955' */ + warning?: string; + /** Warning text background color, default '#FFF5E0' */ + warningBackground?: string; + }; + shape?: { + /** Border radius size in px default 6 */ + radius?: number; + }; + };} +``` + +### Variants + +There are three variants: `default`, `compact` and `drawer` + +#### Drawer + +If you chose the `drawer` variant, you need to create and assign a `ref` in order to control it: + +```tsx +import { useRef } from 'react'; +import { TDrawer, CCIPWidget, Config } from 'ccip-react-components'; + +export const Page = () = { + const drawerRef = useRef(null); + const toggleDrawer = () => drawerRef.current?.toggleDrawer(); + + return ( +
+ + +
+ ); +} +``` + +## Contributing + +Contributions are welcome! Please open an issue or submit a pull request on GitHub. + +1. Fork the repository. +1. Create your feature branch (git checkout -b feature/my-feature). +1. Commit your changes (git commit -m 'Add some feature'). +1. Push to the branch (git push origin feature/my-feature). +1. Open a pull request. + +## License + +CCIP-React-Components is available under the MIT license. diff --git a/packages/ccip-react-components/components.json b/packages/ccip-react-components/components.json new file mode 100644 index 0000000..b26ca43 --- /dev/null +++ b/packages/ccip-react-components/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "lib/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/utils" + } +} diff --git a/packages/ccip-react-components/lib/App.test.tsx b/packages/ccip-react-components/lib/App.test.tsx new file mode 100644 index 0000000..187106f --- /dev/null +++ b/packages/ccip-react-components/lib/App.test.tsx @@ -0,0 +1,56 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import { App } from './App'; +import { describe, expect, test } from 'vitest'; +import { optimismSepolia, sepolia } from 'viem/chains'; +import { Token } from './types'; + +const tokensList: Token[] = [ + { + symbol: 'CCIP-BnM', + address: { + [sepolia.id]: '0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05', + [optimismSepolia.id]: '0x8aF4204e30565DF93352fE8E1De78925F6664dA7', + }, + logoURL: + 'https://smartcontract.imgix.net/tokens/ccip-bnm.webp?auto=compress%2Cformat', + } +]; + +describe('App', () => { + test('renders the default App component', () => { + render(); + waitFor(() => { + expect(screen.getByText('Detecting...')).toBeNull(); + }); + const appContainer = screen.getAllByRole('generic')[1]; + expect(appContainer).toHaveClass('md:w-[473px]'); + const heading = screen.getByRole('heading', { level: 1 }); + expect(heading).toHaveTextContent('Transfer'); + }); + test('renders the App in compact variant', () => { + render(); + waitFor(() => { + expect(screen.getByText('Detecting...')).toBeNull(); + }); + const appContainer = screen.getAllByRole('generic')[1]; + expect(appContainer).toHaveClass('md:w-[375px]'); + const heading = screen.getByRole('heading', { level: 1 }); + expect(heading).toHaveTextContent('Transfer'); + }); + test('renders the App in drawer variant', () => { + render( + + ); + waitFor(() => { + expect(screen.getByText('Detecting...')).toBeNull(); + }); + const dialog = screen.getByRole('dialog'); + expect(dialog).toBeInTheDocument(); + const heading = screen.getByRole('heading', { level: 1 }); + expect(heading).toHaveTextContent('Transfer'); + }); +}); diff --git a/packages/ccip-react-components/lib/App.tsx b/packages/ccip-react-components/lib/App.tsx new file mode 100644 index 0000000..9a69fb7 --- /dev/null +++ b/packages/ccip-react-components/lib/App.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { forwardRef } from 'react'; +import { useTheme } from '@/hooks/useTheme'; +import { Default } from '@/AppDefault'; +import { Providers } from '@/AppProviders'; +import { Drawer } from '@/components/Drawer'; +import { Card } from '@/components/ui/card'; +import { cn } from '@/utils'; +import { ConfigProps, TDrawer } from '@/types'; + +export const App = forwardRef( + ({ config, drawer, tokensList }, ref) => { + if (config?.variant === 'drawer') { + return ( + + + + + + + + ); + } + + return ( + + + + + + ); + } +); + +const Container = ({ children }: { children: React.ReactNode }) => { + const { style, variant } = useTheme(); + + return ( + + {children} + + ); +}; diff --git a/packages/ccip-react-components/lib/AppContext.tsx b/packages/ccip-react-components/lib/AppContext.tsx new file mode 100644 index 0000000..5fa7012 --- /dev/null +++ b/packages/ccip-react-components/lib/AppContext.tsx @@ -0,0 +1,140 @@ +import { DEFAULT_CONFIG, LINK_CONTRACTS } from '@/utils/config'; +import { Config, ConfigProps, Token } from '@/types'; +import { + createContext, + PropsWithChildren, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { Address } from 'viem'; +import { useAccount, useBalance } from 'wagmi'; + +export const Context = createContext<{ + config?: Config; + tokensList: Token[]; + transferHash?: Address; + messageId?: Address; + sourceChainId?: number; + destinationChainId?: number; + feeTokenSymbol?: string; + feeTokenAddress?: Address; + feeAmount?: bigint; + setTransferHash: (txHash: Address | undefined) => void; + setMessageId: (message: Address | undefined) => void; + setSourceChainId: (chainId: number | undefined) => void; + setDestinationChainId: (chainId: number | undefined) => void; + setFeeTokenSymbol: () => void; + setFeeAmount: (f: bigint) => void; + feeTokenBalance?: bigint; + isConnectOpen?: boolean; + setIsConnectOpen: (open: boolean) => void; +}>({ + tokensList: [], + setTransferHash: () => null, + setMessageId: () => null, + setSourceChainId: () => null, + setDestinationChainId: () => null, + setFeeTokenSymbol: () => null, + setFeeAmount: () => null, + setIsConnectOpen: () => null, +}); + +export const ContextProvider = ({ + config: configProp, + tokensList, + children, +}: PropsWithChildren) => { + const [transferHash, setTransferHash] = useState
(); + const [messageId, setMessageId] = useState
(); + const [sourceChainId, setSourceChainId] = useState(); + const [destinationChainId, setDestinationChainId] = useState(); + const [feeTokenSymbol, setFeeTokenSymbol] = useState(); + const [feeTokenAddress, setFeeTokenAddress] = useState
(); + const [feeAmount, setFeeAmount] = useState(); + const [feeTokenBalance, setFeeTokenBalance] = useState(); + const [isConnectOpen, setIsConnectOpen] = useState(false); + + const config = useMemo( + () => ({ + theme: { + palette: { + ...DEFAULT_CONFIG.theme?.palette, + ...configProp?.theme?.palette, + }, + shape: { ...DEFAULT_CONFIG.theme?.shape, ...configProp?.theme?.shape }, + }, + fromChain: configProp?.fromChain, + toChain: configProp?.toChain, + token: configProp?.token, + chains: configProp?.chains, + variant: configProp?.variant, + walletConfig: configProp?.walletConfig, + }), + [configProp] + ); + + const { chain, chainId, address } = useAccount(); + const { data: feeTokenBalanceResult } = useBalance({ + address, + token: feeTokenAddress, + }); + + useEffect(() => { + if (chain?.nativeCurrency.symbol) { + setFeeTokenSymbol(chain?.nativeCurrency.symbol); + } + }, [chain?.nativeCurrency.symbol]); + + useEffect(() => { + if (chainId) { + feeTokenSymbol === 'LINK' + ? setFeeTokenAddress(LINK_CONTRACTS[chainId]) + : setFeeTokenAddress(undefined); + } + }, [chainId, feeTokenSymbol]); + + useEffect(() => { + if (feeTokenBalanceResult?.value) { + setFeeTokenBalance(feeTokenBalanceResult?.value); + } + }, [feeTokenBalanceResult?.value]); + + const setFeeTokenHandler = useCallback(() => { + if (chain?.nativeCurrency.symbol) { + feeTokenSymbol === chain?.nativeCurrency.symbol + ? setFeeTokenSymbol('LINK') + : setFeeTokenSymbol(chain?.nativeCurrency.symbol); + } + }, [feeTokenSymbol, chain?.nativeCurrency.symbol]); + + return ( + setTransferHash(tx), + messageId, + setMessageId: (msg: Address | undefined) => setMessageId(msg), + sourceChainId, + setSourceChainId: (chainId: number | undefined) => + setSourceChainId(chainId), + destinationChainId, + setDestinationChainId: (chainId: number | undefined) => + setDestinationChainId(chainId), + feeTokenSymbol: feeTokenSymbol, + setFeeTokenSymbol: setFeeTokenHandler, + feeTokenAddress, + feeAmount: feeAmount, + setFeeAmount: (f: bigint | undefined) => setFeeAmount(f), + feeTokenBalance, + isConnectOpen, + setIsConnectOpen: (o: boolean) => setIsConnectOpen(o), + }} + > + {children} + + ); +}; diff --git a/packages/ccip-react-components/lib/AppDefault.test.tsx b/packages/ccip-react-components/lib/AppDefault.test.tsx new file mode 100644 index 0000000..553040c --- /dev/null +++ b/packages/ccip-react-components/lib/AppDefault.test.tsx @@ -0,0 +1,41 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import { Default } from './AppDefault'; +import { describe, expect, test } from 'vitest'; +import { Context } from './AppContext'; +import { WagmiProvider } from 'wagmi'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { config as wagmiConfig } from '@/utils/config'; + +const queryClient = new QueryClient(); + +describe('AppDefault', () => { + test('render transfer status page', () => { + render( + + + null, + setMessageId: () => null, + setSourceChainId: () => null, + setDestinationChainId: () => null, + setFeeTokenSymbol: () => null, + setFeeAmount: () => null, + setIsConnectOpen: () => null, + messageId: + '0xea62953e1710d2d369fb896adb3dc009048cd1f4467c87a6295d1722555b2a74', + }} + > + + + + + ); + waitFor(() => { + expect(screen.getByText('Detecting...')).toBeNull(); + }); + const statusHeading = screen.getAllByRole('heading', { level: 4 })[0]; + expect(statusHeading).toHaveTextContent('Transfer status'); + }); +}); diff --git a/packages/ccip-react-components/lib/AppDefault.tsx b/packages/ccip-react-components/lib/AppDefault.tsx new file mode 100644 index 0000000..46d158d --- /dev/null +++ b/packages/ccip-react-components/lib/AppDefault.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { useAppContext } from '@/hooks/useAppContext'; +import { BridgeForm } from '@/pages/BridgeForm'; +import { TxProgress } from '@/pages/TxProgress'; +import { ConnectWallet, ChooseWallet } from '@/components/ConnectWallet'; +import { CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button, buttonVariants } from '@/components/ui/button'; +import { ChainlinkSVG } from '@/components/svg/chainlink'; +import { cn } from '@/utils'; + +export const Default = () => { + const { messageId, isConnectOpen } = useAppContext(); + + if (isConnectOpen) return ; + + if (messageId) { + return ( + <> +
+ +