diff --git a/public/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg b/public/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg new file mode 100644 index 00000000000..78a3009e9b7 Binary files /dev/null and b/public/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg differ diff --git a/public/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg b/public/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg new file mode 100644 index 00000000000..b1dc10f8058 Binary files /dev/null and b/public/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg differ diff --git a/src/components/CCIP/Chain/Chain.astro b/src/components/CCIP/Chain/Chain.astro index b135ef7b58f..fd726a209a2 100644 --- a/src/components/CCIP/Chain/Chain.astro +++ b/src/components/CCIP/Chain/Chain.astro @@ -127,7 +127,7 @@ const chainStructuredData = generateChainStructuredData(

Tokens ({allTokens.length})

{ - network.chainType !== "solana" && ( + network.chainType !== "solana" && network.chainType !== "aptos" && (
Router
-
+
@@ -195,7 +203,7 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain {network.armProxy ? (
) : ( @@ -249,7 +257,10 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain {network.tokenAdminRegistry ? (
) : ( @@ -277,7 +288,7 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain {network.registryModule ? (
) : ( @@ -288,6 +299,69 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain )} + {network.chainType === "aptos" && ( + <> +
+
+ Token admin registry + +
+
+ {network.tokenAdminRegistry ? ( +
+ ) : ( + "n/a" + )} +
+
+ + {network.mcms && ( +
+
+ MCMS + +
+
+
+
+
+ )} + + )} + {network.chainType === "solana" && (
@@ -309,7 +383,7 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain {network.feeQuoter ? (
) : ( diff --git a/src/components/CCIP/ChainHero/LaneDetailsHero.tsx b/src/components/CCIP/ChainHero/LaneDetailsHero.tsx index b5a2af357b0..59113f19942 100644 --- a/src/components/CCIP/ChainHero/LaneDetailsHero.tsx +++ b/src/components/CCIP/ChainHero/LaneDetailsHero.tsx @@ -135,7 +135,11 @@ function LaneDetailsHero({ {/* Display address information based on lane type */} {inOutbound === LaneFilter.Inbound ? ( - + ) : ( : undefined} > - + )} diff --git a/src/components/CCIP/ChainHero/TokenDetailsHero.tsx b/src/components/CCIP/ChainHero/TokenDetailsHero.tsx index 438cb3094c3..94ce250d01f 100644 --- a/src/components/CCIP/ChainHero/TokenDetailsHero.tsx +++ b/src/components/CCIP/ChainHero/TokenDetailsHero.tsx @@ -3,13 +3,14 @@ import { getExplorerAddressUrl, fallbackTokenIconUrl } from "~/features/utils/in import { PoolType } from "~/config/data/ccip/types.ts" import { tokenPoolDisplay } from "~/config/data/ccip/utils.ts" import "./ChainHero.css" -import { ExplorerInfo } from "~/config/types.ts" +import { ExplorerInfo, ChainType } from "~/config/types.ts" interface TokenDetailsHeroProps { network: { name: string logo: string explorer: ExplorerInfo + chainType?: ChainType } token: { id: string @@ -59,7 +60,7 @@ function TokenDetailsHero({ network, token }: TokenDetailsHeroProps) {
@@ -73,7 +74,7 @@ function TokenDetailsHero({ network, token }: TokenDetailsHeroProps) {
diff --git a/src/components/CCIP/Drawer/TokenDrawer.tsx b/src/components/CCIP/Drawer/TokenDrawer.tsx index 77863a767fd..9a92344971e 100644 --- a/src/components/CCIP/Drawer/TokenDrawer.tsx +++ b/src/components/CCIP/Drawer/TokenDrawer.tsx @@ -131,6 +131,7 @@ function TokenDrawer({ name: network.name, logo: network.logo, explorer: network.explorer, + chainType: network.chainType, }} />
diff --git a/src/components/CCIP/Tables/ChainTable.tsx b/src/components/CCIP/Tables/ChainTable.tsx index de82ce27a11..69e6396f27b 100644 --- a/src/components/CCIP/Tables/ChainTable.tsx +++ b/src/components/CCIP/Tables/ChainTable.tsx @@ -33,6 +33,7 @@ interface TableProps { } key: string directory: SupportedChain + chainType?: ChainType }[] explorer: ExplorerInfo } @@ -156,7 +157,10 @@ function ChainTable({ lanes, explorer, sourceNetwork, environment }: TableProps)
diff --git a/src/components/VersionSelector/base/VersionSelector.astro b/src/components/VersionSelector/base/VersionSelector.astro index 8476a2267a1..dc0caf8fcf0 100644 --- a/src/components/VersionSelector/base/VersionSelector.astro +++ b/src/components/VersionSelector/base/VersionSelector.astro @@ -17,13 +17,13 @@ const { product, currentPath, enableEnhanced = true } = Astro.props // Support multiple path patterns for version detection const standardPathMatch = currentPath.match(new RegExp(`/${product}/api-reference/(v[^/]+)`)) -const extendedPathMatch = currentPath.match(new RegExp(`/${product}/api-reference/(?:evm|svm)/(v[^/]+)`)) +const extendedPathMatch = currentPath.match(new RegExp(`/${product}/api-reference/(?:evm|svm|aptos)/(v[^/]+)`)) let pathVersion = extendedPathMatch?.[1] || standardPathMatch?.[1] // Extract VM type from the path (for CCIP) let vmType: string | undefined = undefined if (extendedPathMatch) { - const vmTypeMatch = currentPath.match(new RegExp(`/${product}/api-reference/(evm|svm)/`)) + const vmTypeMatch = currentPath.match(new RegExp(`/${product}/api-reference/(evm|svm|aptos)/`)) vmType = vmTypeMatch?.[1] } diff --git a/src/components/VersionSelector/base/VersionSelectorClient.tsx b/src/components/VersionSelector/base/VersionSelectorClient.tsx index f627c6fceb5..40931bd97d0 100644 --- a/src/components/VersionSelector/base/VersionSelectorClient.tsx +++ b/src/components/VersionSelector/base/VersionSelectorClient.tsx @@ -104,7 +104,7 @@ export const VersionSelectorClient = ({ // 2. Extended: /{product}/api-reference/{vm_type}/v{version}/{page} const standardPathMatch = currentPath.match(new RegExp(`/${config.product.name}/api-reference/v[^/]+/(.+?)/?$`)) const extendedPathMatch = currentPath.match( - new RegExp(`/${config.product.name}/api-reference/(?:evm|svm)/v[^/]+/(.+?)/?$`) + new RegExp(`/${config.product.name}/api-reference/(?:evm|svm|aptos)/v[^/]+/(.+?)/?$`) ) const pagePath = extendedPathMatch?.[1] || standardPathMatch?.[1] diff --git a/src/components/VersionSelector/base/VersionSelectorHead.astro b/src/components/VersionSelector/base/VersionSelectorHead.astro index 385c503062d..911dde57a15 100644 --- a/src/components/VersionSelector/base/VersionSelectorHead.astro +++ b/src/components/VersionSelector/base/VersionSelectorHead.astro @@ -33,7 +33,7 @@ const { // Extract page path to check availability // Support both path patterns const standardPageMatch = currentPath.match(new RegExp(`/${product}/api-reference/v[^/]+/(.+?)/?$`)) -const extendedPageMatch = currentPath.match(new RegExp(`/${product}/api-reference/(?:evm|svm)/v[^/]+/(.+?)/?$`)) +const extendedPageMatch = currentPath.match(new RegExp(`/${product}/api-reference/(?:evm|svm|aptos)/v[^/]+/(.+?)/?$`)) const pagePath = extendedPageMatch?.[1] || standardPageMatch?.[1] || null const productAvailability = PAGE_AVAILABILITY[product] diff --git a/src/config/data/ccip/data.ts b/src/config/data/ccip/data.ts index b369af8c9ba..3e26eb36eb2 100644 --- a/src/config/data/ccip/data.ts +++ b/src/config/data/ccip/data.ts @@ -449,6 +449,7 @@ export const getAllNetworks = ({ filter }: { filter: Environment }): Network[] = armProxy: chains[chain].armProxy, feeQuoter: chainType === "solana" ? chains[chain]?.feeQuoter : undefined, rmnPermeable: chains[chain]?.rmnPermeable, + mcms: chainType === "aptos" ? chains[chain]?.mcms?.address : undefined, }) } diff --git a/src/config/data/ccip/types.ts b/src/config/data/ccip/types.ts index d637e7f33a0..40a666748f6 100644 --- a/src/config/data/ccip/types.ts +++ b/src/config/data/ccip/types.ts @@ -77,6 +77,9 @@ export type ChainConfig = { symbol: string logo: string } + mcms?: { + address: string + } } export type ChainsConfig = { @@ -194,6 +197,7 @@ export interface Network { routerExplorerUrl: string feeQuoter?: string rmnPermeable: boolean + mcms?: string } export type DecomConfig = { diff --git a/src/config/data/ccip/v1_2_0/mainnet/chains.json b/src/config/data/ccip/v1_2_0/mainnet/chains.json index 8662a097db6..bb92375fcaa 100644 --- a/src/config/data/ccip/v1_2_0/mainnet/chains.json +++ b/src/config/data/ccip/v1_2_0/mainnet/chains.json @@ -39,6 +39,26 @@ "version": "1.5.0" } }, + "aptos-mainnet": { + "armProxy": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "RMNRemote 1.6.0" + }, + "chainSelector": "4741433654826277614", + "feeTokens": ["APT", "LINK"], + "mcms": { + "address": "0x2d3254b5c860a75d8d5095d44388ae8e9c23a234a85abfcff6264d298fbef6cc" + }, + "rmnPermeable": false, + "router": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "Router 1.6.0" + }, + "tokenAdminRegistry": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "TokenAdminRegistry 1.6.0" + } + }, "avalanche-mainnet": { "armProxy": { "address": "0xcBD48A8eB077381c3c4Eb36b402d7283aB2b11Bc", diff --git a/src/config/data/ccip/v1_2_0/mainnet/lanes.json b/src/config/data/ccip/v1_2_0/mainnet/lanes.json index c1a42d8a874..409b7f0950b 100644 --- a/src/config/data/ccip/v1_2_0/mainnet/lanes.json +++ b/src/config/data/ccip/v1_2_0/mainnet/lanes.json @@ -63,6 +63,80 @@ "rmnPermeable": true } }, + "aptos-mainnet": { + "bsc-mainnet": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + }, + "ethereum-mainnet-arbitrum-1": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + }, + "ethereum-mainnet-base-1": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + }, + "ethereum-mainnet-optimism-1": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + }, + "mainnet": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + }, + "sonic-mainnet": { + "offRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0x20f808de3375db34d17cc946ec6b43fc26962f6afa125182dc903359756caf6b", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true + } + }, "avalanche-mainnet": { "bitcoin-mainnet-bitlayer-1": { "offRamp": { @@ -2930,6 +3004,18 @@ } }, "bsc-mainnet": { + "aptos-mainnet": { + "offRamp": { + "address": "0xA27056438FfA1f286AB197488808692F0db93F8B", + "version": "1.6.0" + }, + "onRamp": { + "address": "0xf09AFe78d3c7d359b334d7cB88995751F7eC5E13", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0xc69a550470bEbC5c3Be98A4C3dD26C6AdD90C64b", @@ -5840,6 +5926,18 @@ }, "rmnPermeable": true }, + "aptos-mainnet": { + "offRamp": { + "address": "0xee85aEfb15b9489563A6a29891ebe0750AA1A7Ae", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x76a443768A5e3B8d1AED0105FC250877841Deb40", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0x95095007d5Cc3E7517A1A03c9e228adA5D0bc376", @@ -8840,6 +8938,18 @@ }, "rmnPermeable": true }, + "aptos-mainnet": { + "offRamp": { + "address": "0xf09AFe78d3c7d359b334d7cB88995751F7eC5E13", + "version": "1.6.0" + }, + "onRamp": { + "address": "0xee85aEfb15b9489563A6a29891ebe0750AA1A7Ae", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0x61C3f6d72c80A3D1790b213c4cB58c3d4aaFccDF", @@ -14002,6 +14112,18 @@ } }, "ethereum-mainnet-optimism-1": { + "aptos-mainnet": { + "offRamp": { + "address": "0xee85aEfb15b9489563A6a29891ebe0750AA1A7Ae", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x76a443768A5e3B8d1AED0105FC250877841Deb40", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0xF8E38B4503418659F791F2135c4912F85BFB7988", @@ -16746,6 +16868,18 @@ }, "rmnPermeable": true }, + "aptos-mainnet": { + "offRamp": { + "address": "0x26d3681DfC9E4c8C79cfbf461adec8A21d5d73C5", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x913814782144864e523C3FdB78E3ca25D2c2aeCa", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0xd98E80C79a15E4dbaF4C40B6cCDF690fe619BFBb", @@ -26816,6 +26950,18 @@ } }, "sonic-mainnet": { + "aptos-mainnet": { + "offRamp": { + "address": "0xee85aEfb15b9489563A6a29891ebe0750AA1A7Ae", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x76a443768A5e3B8d1AED0105FC250877841Deb40", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true + }, "avalanche-mainnet": { "offRamp": { "address": "0x9076066423bC9a078D323eD2Aa652d324b77Be83", diff --git a/src/config/data/ccip/v1_2_0/mainnet/tokens.json b/src/config/data/ccip/v1_2_0/mainnet/tokens.json index 79167b492a6..d344572ac04 100644 --- a/src/config/data/ccip/v1_2_0/mainnet/tokens.json +++ b/src/config/data/ccip/v1_2_0/mainnet/tokens.json @@ -97,6 +97,16 @@ "tokenAddress": "0x7894b3088d069E70895EFfA4e8f7D2c243Fd04C1" } }, + "APT": { + "aptos-mainnet": { + "allowListEnabled": false, + "decimals": 8, + "name": "Aptos Coin", + "poolType": "feeTokenOnly", + "symbol": "APT", + "tokenAddress": "0x000000000000000000000000000000000000000000000000000000000000000A" + } + }, "APU": { "ethereum-mainnet-arbitrum-1": { "allowListEnabled": false, @@ -1784,6 +1794,14 @@ "symbol": "LINK", "tokenAddress": "0xf575731b78981B86d34321d875A3D25a48479be6" }, + "aptos-mainnet": { + "allowListEnabled": false, + "decimals": 8, + "name": "ChainLink Token", + "poolType": "feeTokenOnly", + "symbol": "LINK", + "tokenAddress": "0x8c764993820ea735719f1ff7f1a0f80c022b18e7b5daefa35adf60a3a6556566" + }, "avalanche-mainnet": { "allowListEnabled": false, "decimals": 18, diff --git a/src/config/data/ccip/v1_2_0/testnet/chains.json b/src/config/data/ccip/v1_2_0/testnet/chains.json index ee57bc057ea..4dbc7c87844 100644 --- a/src/config/data/ccip/v1_2_0/testnet/chains.json +++ b/src/config/data/ccip/v1_2_0/testnet/chains.json @@ -67,6 +67,26 @@ "version": "1.5.0" } }, + "aptos-testnet": { + "armProxy": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "RMNRemote 1.6.0" + }, + "chainSelector": "743186221051783445", + "feeTokens": ["APT", "LINK"], + "mcms": { + "address": "0xbdf1b9aacb4e21bf6f255105831df0172e911d4748e488196fde10d2e2a4e32d" + }, + "rmnPermeable": false, + "router": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "Router 1.6.0" + }, + "tokenAdminRegistry": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "TokenAdminRegistry 1.6.0" + } + }, "avalanche-fuji-testnet": { "armProxy": { "address": "0xAc8CFc3762a979628334a0E4C1026244498E821b", diff --git a/src/config/data/ccip/v1_2_0/testnet/lanes.json b/src/config/data/ccip/v1_2_0/testnet/lanes.json index 1e7966f13c6..a0b9fcdedb7 100644 --- a/src/config/data/ccip/v1_2_0/testnet/lanes.json +++ b/src/config/data/ccip/v1_2_0/testnet/lanes.json @@ -73,6 +73,176 @@ } } }, + "aptos-testnet": { + "bsc-testnet": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, + "ethereum-testnet-sepolia": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, + "ethereum-testnet-sepolia-arbitrum-1": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, + "ethereum-testnet-sepolia-base-1": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, + "ethereum-testnet-sepolia-optimism-1": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, + "sonic-testnet-blaze": { + "offRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "version": "OffRamp 1.6.0" + }, + "onRamp": { + "address": "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45", + "enforceOutOfOrder": true, + "version": "OnRamp 1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + } + }, "avalanche-fuji-testnet": { "bitcoin-testnet-bitlayer-1": { "offRamp": { @@ -740,6 +910,34 @@ } }, "bsc-testnet": { + "aptos-testnet": { + "offRamp": { + "address": "0xF4EbCC2c077d3939434C7Ab0572660c5A45e4df5", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x28A025d34c830BF212f5D2357C8DcAB32dD92A20", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "avalanche-fuji-testnet": { "offRamp": { "address": "0x95b66acfaaDF122f4EccE52C0aD4Fd997DD1150C", @@ -1498,6 +1696,34 @@ } } }, + "aptos-testnet": { + "offRamp": { + "address": "0x0820f975ce90EE5c508657F0C58b71D1fcc85cE0", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x23a5084Fa78104F3DF11C63Ae59fcac4f6AD9DeE", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "avalanche-fuji-testnet": { "offRamp": { "address": "0x1DEBa99dC8e2A77832461BD386d83D9FCb133137", @@ -3166,6 +3392,34 @@ } }, "ethereum-testnet-sepolia-arbitrum-1": { + "aptos-testnet": { + "offRamp": { + "address": "0xF4EbCC2c077d3939434C7Ab0572660c5A45e4df5", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x28A025d34c830BF212f5D2357C8DcAB32dD92A20", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "avalanche-fuji-testnet": { "offRamp": { "address": "0x7245a5947E2F32B66aF74F4dAF91718ea19afaDf", @@ -3602,6 +3856,34 @@ } }, "ethereum-testnet-sepolia-base-1": { + "aptos-testnet": { + "offRamp": { + "address": "0xF4EbCC2c077d3939434C7Ab0572660c5A45e4df5", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x28A025d34c830BF212f5D2357C8DcAB32dD92A20", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "avalanche-fuji-testnet": { "offRamp": { "address": "0x3Ab3a3d35cAC95FfcFCcc127eF01eA8D87b0A64e", @@ -4522,6 +4804,34 @@ } }, "ethereum-testnet-sepolia-optimism-1": { + "aptos-testnet": { + "offRamp": { + "address": "0x30D197C6F5bE050D5525dD94d01760FaCdB67e7C", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x8F5bED5F7601025b12A97b01584220C12e343986", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "avalanche-fuji-testnet": { "offRamp": { "address": "0xCb2266c2118b1f30D15CBeB3a885531ABaA1b556", @@ -6484,6 +6794,34 @@ } }, "sonic-testnet-blaze": { + "aptos-testnet": { + "offRamp": { + "address": "0xF094E1dB26Ce8C76C9fF0bD0566Bb8EEfF1b76dd", + "version": "1.6.0" + }, + "onRamp": { + "address": "0x384C8843411f725e800E625d5d1B659256D629dF", + "enforceOutOfOrder": true, + "version": "1.6.0" + }, + "rmnPermeable": true, + "supportedTokens": { + "CCIP-BnM": { + "rateLimiterConfig": { + "in": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + }, + "out": { + "capacity": "0", + "isEnabled": false, + "rate": "0" + } + } + } + } + }, "bsc-testnet": { "offRamp": { "address": "0x761A8119AD4fFc2468f9B6AED52DACc2C18bA85f", diff --git a/src/config/data/ccip/v1_2_0/testnet/tokens.json b/src/config/data/ccip/v1_2_0/testnet/tokens.json index 8f484079b35..7c50a897d40 100644 --- a/src/config/data/ccip/v1_2_0/testnet/tokens.json +++ b/src/config/data/ccip/v1_2_0/testnet/tokens.json @@ -1,4 +1,14 @@ { + "APT": { + "aptos-testnet": { + "allowListEnabled": false, + "decimals": 8, + "name": "Aptos Coin", + "poolType": "feeTokenOnly", + "symbol": "APT", + "tokenAddress": "0x000000000000000000000000000000000000000000000000000000000000000A" + } + }, "CCIP-BnM": { "abstract-testnet": { "allowListEnabled": false, @@ -18,6 +28,15 @@ "symbol": "CCIP-BnM", "tokenAddress": "0xF48cae4B1F4EB3a1682600D4F3aFA166db5B162E" }, + "aptos-testnet": { + "allowListEnabled": false, + "decimals": 8, + "name": "CCIP-BnM", + "poolAddress": "0x65ad4cb3142cab5100a4eeed34e2005cbb1fcae42fc688e3c96b0c33ae16e6b9", + "poolType": "burnMint", + "symbol": "CCIP-BnM", + "tokenAddress": "0xa680c9935c7ea489676fa0e01f1ff8a97fadf0cb35e1e06ba1ba32ecd882fc9a" + }, "avalanche-fuji-testnet": { "allowListEnabled": false, "decimals": 18, @@ -595,6 +614,14 @@ "symbol": "LINK", "tokenAddress": "0xa787B3E0471b718bBfEaA59B502fd0C4EBd7b74E" }, + "aptos-testnet": { + "allowListEnabled": false, + "decimals": 8, + "name": "ChainLink Token", + "poolType": "feeTokenOnly", + "symbol": "LINK", + "tokenAddress": "0x3d5d565c271d6b9c52f1a963f2b7bddad3453b0de2ace5e254b8db6549cc335e" + }, "avalanche-fuji-testnet": { "allowListEnabled": false, "decimals": 18, diff --git a/src/config/data/chain-to-technology.json b/src/config/data/chain-to-technology.json index 606b19a6d7c..7c722422691 100644 --- a/src/config/data/chain-to-technology.json +++ b/src/config/data/chain-to-technology.json @@ -138,5 +138,7 @@ "NEO_X_MAINNET": "NEO_X", "NEO_X_TESTNET": "NEO_X", "KATANA_TATARA": "POLYGON_KATANA", - "KATANA_MAINNET": "POLYGON_KATANA" + "KATANA_MAINNET": "POLYGON_KATANA", + "APTOS_MAINNET": "APTOS", + "APTOS_TESTNET": "APTOS" } diff --git a/src/config/data/chains.json b/src/config/data/chains.json index 9848f24be1b..e0a2639c2cf 100644 --- a/src/config/data/chains.json +++ b/src/config/data/chains.json @@ -1736,6 +1736,43 @@ } } }, + "APTOS": { + "title": "Aptos", + "icon": "/assets/chains/aptos.svg", + "chainType": "aptos", + "chains": { + "APTOS_MAINNET": { + "chainId": 1, + "title": "Aptos Mainnet", + "explorer": { + "baseUrl": "https://explorer.aptoslabs.com", + "queryParameters": { + "network": "mainnet" + } + }, + "nativeCurrency": { + "name": "Aptos", + "symbol": "APT", + "decimals": 8 + } + }, + "APTOS_TESTNET": { + "chainId": 2, + "title": "Aptos Testnet", + "explorer": { + "baseUrl": "https://explorer.aptoslabs.com", + "queryParameters": { + "network": "testnet" + } + }, + "nativeCurrency": { + "name": "Aptos", + "symbol": "APT", + "decimals": 8 + } + } + } + }, "SOLANA": { "title": "Solana", "icon": "/assets/chains/solana.svg", diff --git a/src/config/redirects/ccip.ts b/src/config/redirects/ccip.ts index 42e84d9118a..d47663b30a7 100644 --- a/src/config/redirects/ccip.ts +++ b/src/config/redirects/ccip.ts @@ -14,6 +14,7 @@ const EVM_API_FILES = [ ] const SVM_API_FILES = ["events", "router", "errors", "messages"] +const APTOS_API_FILES = ["events", "router", "errors", "messages"] export const ccipRedirects = { ...EVM_API_FILES.reduce( @@ -38,4 +39,15 @@ export const ccipRedirects = { }, {} as Record ), + ...APTOS_API_FILES.reduce( + (redirects, file) => { + // Only create one redirect without trailing slash + redirects[`/ccip/api-reference/aptos/${file}`] = { + status: 301, + destination: `/ccip/api-reference/aptos/v1.6.0/${file}`, + } + return redirects + }, + {} as Record + ), } diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index a4d1cce5647..5e65d4a8600 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -9,6 +9,7 @@ import evmCcipV150Contents from "./sidebar/ccip/api-reference/evm/v1_5_0.json" w import evmCcipV151Contents from "./sidebar/ccip/api-reference/evm/v1_5_1.json" with { type: "json" } import evmCcipV160Contents from "./sidebar/ccip/api-reference/evm/v1_6_0.json" with { type: "json" } import svmCcipV160Contents from "./sidebar/ccip/api-reference/svm/v1_6_0.json" with { type: "json" } +import aptosCcipV160Contents from "./sidebar/ccip/api-reference/aptos/v1_6_0.json" with { type: "json" } import chainlinkLocalV021Contents from "./sidebar/chainlink-local/api-reference/v0_2_1.json" with { type: "json" } import chainlinkLocalV022Contents from "./sidebar/chainlink-local/api-reference/v0_2_2.json" with { type: "json" } import chainlinkLocalV023Contents from "./sidebar/chainlink-local/api-reference/v0_2_3.json" with { type: "json" } @@ -1285,6 +1286,10 @@ export const SIDEBAR: Partial> = { title: "SVM", url: "ccip/service-limits/svm", }, + { + title: "Aptos", + url: "ccip/service-limits/aptos", + }, { title: "Network Specific", url: "ccip/service-limits/network-specific-limits", @@ -1364,6 +1369,24 @@ export const SIDEBAR: Partial> = { }, ], }, + { + title: "Aptos", + url: "ccip/concepts/architecture/onchain/aptos", + children: [ + { + title: "Overview", + url: "ccip/concepts/architecture/onchain/aptos/overview", + }, + { + title: "Components", + url: "ccip/concepts/architecture/onchain/aptos/components", + }, + { + title: "Upgradability", + url: "ccip/concepts/architecture/onchain/aptos/upgradability", + }, + ], + }, ], }, { @@ -1460,6 +1483,10 @@ export const SIDEBAR: Partial> = { title: "SVM", url: "ccip/concepts/best-practices/svm", }, + { + title: "Aptos", + url: "ccip/concepts/best-practices/aptos", + }, ], }, ], @@ -1632,6 +1659,64 @@ export const SIDEBAR: Partial> = { }, ], }, + { + title: "Aptos", + url: "ccip/tutorials/aptos", + children: [ + { + title: "Implement CCIP Receiver", + url: "ccip/tutorials/aptos/receivers", + }, + { + title: "Aptos as Source", + url: "ccip/tutorials/aptos/source", + children: [ + { + title: "Build CCIP Messages", + url: "ccip/tutorials/aptos/source/build-messages", + }, + { + title: "Prerequisites", + url: "ccip/tutorials/aptos/source/prerequisites", + }, + { + title: "Token Transfers", + url: "ccip/tutorials/aptos/source/token-transfers", + }, + ], + }, + { + title: "Aptos as Destination", + url: "ccip/tutorials/aptos/destination", + children: [ + { + title: "Build CCIP Messages", + url: "ccip/tutorials/aptos/destination/build-messages", + }, + { + title: "Prerequisites", + url: "ccip/tutorials/aptos/destination/prerequisites", + }, + { + title: "Token Transfers", + url: "ccip/tutorials/aptos/destination/token-transfers", + }, + { + title: "Arbitrary Messaging", + url: "ccip/tutorials/aptos/destination/arbitrary-messaging", + }, + { + title: "Programmatic Token Transfers", + url: "ccip/tutorials/aptos/destination/programmatic-token-transfers", + }, + ], + }, + { + title: "Cross-Chain Token (CCT) standard", + url: "ccip/tutorials/aptos/cross-chain-tokens", + }, + ], + }, ], }, { @@ -1695,6 +1780,18 @@ export const SIDEBAR: Partial> = { }, ], }, + { + title: "Aptos", + url: "ccip/api-reference/aptos", + children: [ + { + title: "v1.6.0 (Latest)", + url: "ccip/api-reference/aptos/v1.6.0", + isCollapsible: true, + children: aptosCcipV160Contents, + }, + ], + }, ], }, { diff --git a/src/config/sidebar/ccip/api-reference/aptos/v1_6_0.json b/src/config/sidebar/ccip/api-reference/aptos/v1_6_0.json new file mode 100644 index 00000000000..fad48fc9ba6 --- /dev/null +++ b/src/config/sidebar/ccip/api-reference/aptos/v1_6_0.json @@ -0,0 +1,26 @@ +[ + { + "title": "Messages", + "url": "ccip/api-reference/aptos/v1.6.0/messages" + }, + { + "title": "Router", + "url": "ccip/api-reference/aptos/v1.6.0/router" + }, + { + "title": "Receiver", + "url": "ccip/api-reference/aptos/v1.6.0/receiver" + }, + { + "title": "Client", + "url": "ccip/api-reference/aptos/v1.6.0/client" + }, + { + "title": "Errors", + "url": "ccip/api-reference/aptos/v1.6.0/errors" + }, + { + "title": "Events", + "url": "ccip/api-reference/aptos/v1.6.0/events" + } +] diff --git a/src/config/types.ts b/src/config/types.ts index d6010235cc0..ab396e17b30 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -68,7 +68,7 @@ export type SupportedTechnology = | "NEO_X" | "POLYGON_KATANA" -export type ChainType = "evm" | "solana" | "aptos" +export type ChainType = "evm" | "solana" | "aptos" | "sui" export type ChainFamily = "evm" | "mvm" | "svm" @@ -213,6 +213,8 @@ export type SupportedChain = | "KATANA_TATARA" | "KATANA_MAINNET" | "BOTANIX_MAINNET" + | "APTOS_MAINNET" + | "APTOS_TESTNET" export type ExplorerInfo = { baseUrl: string diff --git a/src/config/versions/index.ts b/src/config/versions/index.ts index dbf208ec2a5..957ae9259c2 100644 --- a/src/config/versions/index.ts +++ b/src/config/versions/index.ts @@ -14,6 +14,7 @@ export interface VersionConfig { export interface VMVersionConfig { evm: VersionConfig svm: VersionConfig + aptos: VersionConfig } // Registry of all product versions @@ -37,6 +38,13 @@ export const VERSIONS = { "v1.6.0": "2025-05-19T00:00:00Z", // 19 May 2025 }, }, + aptos: { + LATEST: "v1.6.0", + ALL: ["v1.6.0"] as const, + RELEASE_DATES: { + "v1.6.0": "2025-06-30T00:00:00Z", // 30 June 2025 + }, + }, // Default for backward compatibility get LATEST() { return this.evm.LATEST diff --git a/src/config/web3Providers.ts b/src/config/web3Providers.ts index d7098877389..45994b945ca 100644 --- a/src/config/web3Providers.ts +++ b/src/config/web3Providers.ts @@ -145,6 +145,8 @@ export const chainToProvider: Record Provider> = { KATANA_TATARA: () => new JsonRpcProvider("https://rpc.tatara.katanarpc.com"), KATANA_MAINNET: () => new JsonRpcProvider("https://rpc.katanarpc.com"), BOTANIX_MAINNET: () => new JsonRpcProvider("https://rpc.botanix.org"), + APTOS_MAINNET: () => new JsonRpcProvider("https://fullnode.mainnet.aptoslabs.com/v1"), + APTOS_TESTNET: () => new JsonRpcProvider("https://fullnode.testnet.aptoslabs.com/v1"), } export const getRpcUrlForChain = (chain: SupportedChain): string => { diff --git a/src/content/ccip/api-reference/aptos/index.mdx b/src/content/ccip/api-reference/aptos/index.mdx new file mode 100644 index 00000000000..281f781599b --- /dev/null +++ b/src/content/ccip/api-reference/aptos/index.mdx @@ -0,0 +1,32 @@ +--- +section: ccip +date: Last Modified +title: "Chainlink CCIP API Reference Documentation (Aptos)" +metadata: + description: "Complete API reference documentation for Chainlink Cross-Chain Interoperability Protocol (CCIP) on the Aptos blockchain. Includes message structures, router functionality, events, and error handling." +isIndex: true +--- + +import { Aside } from "@components" + +## Available Versions + +### Latest Release + +- **[CCIP v1.6.0](/ccip/api-reference/aptos/v1.6.0)** (Current Version) + - Initial release for the Aptos blockchain + - Cross-chain messaging from Aptos to EVM-based chains and EVM-based chains to Aptos + - Token transfers using Aptos Fungible Assets + - Support for native APT fee payments + - Configurable gas limits and out-of-order execution + +## Documentation Structure + +Each version includes detailed documentation for: + +- Message Structures and Extra Args +- Router Functions +- Receiver Functions +- Client Helper Functions +- Events +- Error Handling diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/client.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/client.mdx new file mode 100644 index 00000000000..c1f4c421e51 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/client.mdx @@ -0,0 +1,70 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Client API Reference" +metadata: + description: "API documentation for the CCIP client helper module on the Aptos blockchain, focusing on extra args encoding." +--- + +import { Aside, ClickToZoom } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Client + +The `ccip::client` module provides helper `#[view]` functions that can be called on-chain to aid in constructing CCIP messages. The primary use case is encoding the `extra_args` parameter for different destination chain families. + +### `encode_generic_extra_args_v2` + +This function encodes the extra arguments required when sending a CCIP message from Aptos to an **EVM-based chain (like Ethereum) or another Aptos module**. It takes a gas limit and a boolean flag, BCS-encodes them, and prepends the standard `GENERIC_EXTRA_ARGS_V2_TAG` (`0x181dcf10`). + +```rust +#[view] +public fun encode_generic_extra_args_v2( + gas_limit: u256, + allow_out_of_order_execution: bool +): vector +``` + +#### Parameters + +| Name | Type | Description | +| ------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------- | +| `gas_limit` | `u256` | The gas limit to allocate for the execution of the message on the destination chain. | +| `allow_out_of_order_execution` | `bool` | A flag indicating if the message can be processed out of order. It is recommended to set this to `true`. | + +#### Returns + +| Name | Type | Description | +| ----------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `encoded_vector` | `vector` | A byte vector containing the 4-byte tag followed by the BCS-encoded parameters, ready to be used in `ccip_send`. | + +### `encode_svm_extra_args_v1` + +This function encodes the more complex extra arguments required when sending a CCIP message from Aptos to an **SVM-based chain (like Solana)**. It constructs a byte vector by prepending the `SVM_EXTRA_ARGS_V1_TAG` (`0x1f3b3aba`) to the BCS-encoded parameters, which include details specific to the SVM account model. + +```rust +#[view] +public fun encode_svm_extra_args_v1( + compute_units: u32, + account_is_writable_bitmap: u64, + allow_out_of_order_execution: bool, + token_receiver: vector, + accounts: vector> +): vector +``` + +#### Parameters + +| Name | Type | Description | +| ------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `compute_units` | `u32` | The number of compute units to allocate for the message's execution on the destination SVM chain. | +| `account_is_writable_bitmap` | `u64` | A bitmask indicating which of the provided `accounts` are writable. Bit `i` corresponds to `accounts[i]`. | +| `allow_out_of_order_execution` | `bool` | A flag indicating if the message can be processed out of order. | +| `token_receiver` | `vector` | The 32-byte public key of the account on the SVM chain that will receive any transferred tokens. | +| `accounts` | `vector>` | A vector of 32-byte public keys representing the accounts required by the receiver program on the destination SVM chain. | + +#### Returns + +| Name | Type | Description | +| ----------------------------- | ------------------------- | -------------------------------------------------------------------------------------------- | +| `encoded_vector` | `vector` | A byte vector containing the 4-byte tag followed by the BCS-encoded SVM-specific parameters. | diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/errors.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/errors.mdx new file mode 100644 index 00000000000..35404ca75bd --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/errors.mdx @@ -0,0 +1,105 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Errors API Reference" +metadata: + description: "API documentation for CCIP errors on the Aptos blockchain, with functions specified." +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Errors + +### router + +Errors that can occur in the main `router` module during a `ccip_send` call. + +#### `ccip_send_with_message_id` + +| Error Code | Symbol | Description | +| ------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| **2** | `E_UNSUPPORTED_DESTINATION_CHAIN` | The provided `dest_chain_selector` does not correspond to a configured and supported destination chain in the router's state. | +| **3** | `E_UNSUPPORTED_ON_RAMP_VERSION` | The on-ramp version for the specified destination chain is not supported by the current router implementation. The call was aborted. | + +### onramp + +Errors originating from the `onramp` module, which handles the core logic for sending messages and tokens. + +#### `get_fee_internal` + +| Error Code | Symbol | Description | +| ------------------- | ------------------------------ | ------------------------------------------------------------- | +| **12** | `E_CURSED_BY_RMN` | The transfer is blocked by the Risk Management Network (RMN). | + +#### `resolve_fungible_asset` + +| Error Code | Symbol | Description | +| ------------------- | ------------------------------ | ------------------------------------------------------------------------- | +| **13** | `E_INVALID_TOKEN` | The provided token address is not a valid fungible asset metadata object. | + +#### `resolve_fungible_store` + +| Error Code | Symbol | Description | +| ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| **14** | `E_INVALID_TOKEN_STORE` | The provided token store address (or the primary store if `0x0` was passed) is not a valid fungible store object for the given token. | + +#### `ccip_send` + +| Error Code | Symbol | Description | +| ------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| **4** | `E_UNKNOWN_DEST_CHAIN_SELECTOR` | The destination chain configuration for the given `dest_chain_selector` does not exist. | +| **6** | `E_SENDER_NOT_ALLOWED` | The allow-list is enabled for the destination chain, and the caller's address is not in the list of permitted senders. | +| **10** | `E_UNSUPPORTED_TOKEN` | The token being transferred is not registered with the token admin registry and has no associated token pool. | +| **15** | `E_UNEXPECTED_WITHDRAW_AMOUNT` | The amount of tokens withdrawn from the user's account did not match the expected amount. | +| **16** | `E_UNEXPECTED_FUNGIBLE_ASSET` | The fungible asset withdrawn from the user's account does not match the expected asset metadata. | +| **18** | `E_MUST_BE_CALLED_BY_ROUTER` | An internal consistency check failed; the on-ramp can only be called by its designated router. | +| **19** | `E_TOKEN_AMOUNT_MISMATCH` | The number of items in `token_addresses` does not match the number of items in `token_store_addresses`. | +| **20** | `E_CANNOT_SEND_ZERO_TOKENS` | An attempt was made to send a token amount of zero, which is not allowed. | + +### fee_quoter + +Errors related to fee calculation and processing of message arguments. + +#### `get_token_receiver` + +| Error Code | Symbol | Description | +| ------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| **11** | `E_UNKNOWN_CHAIN_FAMILY_SELECTOR` | The destination chain's family (e.g., EVM, SVM) is unknown or not supported, preventing correct processing of `extra_args`. | + +#### `process_message_args` + +| Error Code | Symbol | Description | +| ------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| **24** | `E_MESSAGE_FEE_TOO_HIGH` | The calculated fee in Juels exceeds the maximum allowed fee per message configured in the FeeQuoter. | +| **25** | `E_SOURCE_TOKEN_DATA_TOO_LARGE` | The return data from a token pool's `lockOrBurn` function is larger than the allowed size. | + +### token_admin_registry + +Errors that occur when interacting with the token registry to manage token pool states. + +#### `start_lock_or_burn` + +| Error Code | Symbol | Description | +| ------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| **3** | `E_INVALID_TOKEN_POOL` | The token pool address is not a valid, registered `TokenPoolRegistration` object. | +| **9** | `E_NOT_IN_IDLE_STATE` | The token pool is not in an idle state and cannot start a new `lockOrBurn` operation because it is already busy. | +| **12** | `E_NON_EMPTY_LOCK_OR_BURN_INPUT` | Internal state inconsistency: The `lock_or_burn_input` storage was expected to be empty but was not. | +| **13** | `E_NON_EMPTY_LOCK_OR_BURN_OUTPUT` | Internal state inconsistency: The `lock_or_burn_output` storage was expected to be empty but was not. | +| **14** | `E_NON_EMPTY_RELEASE_OR_MINT_INPUT` | Internal state inconsistency: The `release_or_mint_input` storage was expected to be empty but was not. | +| **15** | `E_NON_EMPTY_RELEASE_OR_MINT_OUTPUT` | Internal state inconsistency: The `release_or_mint_output` storage was expected to be empty but was not. | + +#### `finish_lock_or_burn` + +| Error Code | Symbol | Description | +| ------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| **10** | `E_NOT_IN_LOCK_OR_BURN_STATE` | An attempt to finish a `lockOrBurn` operation failed because the token pool was not in the correct `LOCK_OR_BURN` state. | +| **17** | `E_MISSING_LOCK_OR_BURN_OUTPUT` | The `lockOrBurn` operation could not be completed because the expected output data from the token pool was missing. | + +### token_admin_dispatcher + +This module dispatches calls to the appropriate token pool. + +#### `dispatch_lock_or_burn` + +The errors this function can revert with originate from the `token_admin_registry` module during the `start_lock_or_burn` and `finish_lock_or_burn` calls. Refer to the `token_admin_registry` table for a list of possible errors. diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/events.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/events.mdx new file mode 100644 index 00000000000..e43d455c538 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/events.mdx @@ -0,0 +1,61 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Events API Reference" +metadata: + description: "API documentation for CCIP events on the Aptos blockchain." +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Events + +This document details the events emitted by the core CCIP modules on Aptos. + +### onramp + +#### `ccip_send` + +When the `ccip_send` entry function completes successfully, the `onramp` module emits a single event, `CCIPMessageSent`. This event signifies that a new cross-chain message has been initiated and sent from Aptos. + +```rust +#[event] +struct CCIPMessageSent has store, drop { + dest_chain_selector: u64, + sequence_number: u64, + message: Aptos2AnyRampMessage +} +``` + +| Field | Type | Description | +| ---------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `dest_chain_selector` | `u64` | The **chain selector** for the destination chain. This is the unique identifier for the target chain within the CCIP network (e.g., `16015286601757825753` for Ethereum Sepolia). | +| `sequence_number` | `u64` | A unique, monotonically increasing number for each message sent to a specific destination chain. This helps in ordering messages and is unique per destination. | +| `message` | `Aptos2AnyRampMessage` | The complete cross-chain message payload. This struct contains detailed information, including the sender's Aptos address, the receiver's address on the destination chain, the arbitrary `data` payload, token transfer details, and fee information. | +| | + +### offramp + +#### `execute_single_report` + +After successfully processing an incoming message from a source chain, the `offramp` module emits an `ExecutionStateChanged` event to record the outcome of the message execution on Aptos. + +```rust +#[event] +struct ExecutionStateChanged has store, drop { + source_chain_selector: u64, + sequence_number: u64, + message_id: vector, + message_hash: vector, + state: u8 +} +``` + +| Field | Type | Description | +| ------------------------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `source_chain_selector` | `u64` | The **chain selector** identifying the chain where the message originated. | +| `sequence_number` | `u64` | The sequence number of the message from the source chain. This corresponds to the `sequence_number` in the `CCIPMessageSent` event that was emitted on the source chain. | +| `message_id` | `vector` | The unique, global identifier for the cross-chain message. This ID is consistent across both the source and destination chains and is the primary key for tracking a CCIP transaction. | +| `message_hash` | `vector` | The hash of the message leaf in the Merkle tree. This value is used internally for verifying the message against a committed Merkle root, ensuring its authenticity and integrity. | +| `state` | `u8` | The outcome of the execution. Since Aptos transactions are atomic (either fully succeed or fail), a successfully processed CCIP message will always result in an `EXECUTION_STATE_SUCCESS`. | diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/index.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/index.mdx new file mode 100644 index 00000000000..32554c82448 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/index.mdx @@ -0,0 +1,30 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos API Reference" +metadata: + description: "API reference documentation for Chainlink CCIP v1.6.0 on the Aptos blockchain. Includes message structures, router functionality, events, and error handling for cross-chain interoperability." +isIndex: true +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + + + +## API References + +### Core Components + +- [Messages](/ccip/api-reference/aptos/v1.6.0/messages) - Message structures and extra args for cross-chain messaging +- [Router](/ccip/api-reference/aptos/v1.6.0/router) - Functions for sending messages through CCIP on Aptos +- [Receiver](/ccip/api-reference/aptos/v1.6.0/receiver) - Functions for receiving and processing CCIP messages on Aptos +- [Client](/ccip/api-reference/aptos/v1.6.0/client) - Helper functions for interacting with CCIP on Aptos +- [Events](/ccip/api-reference/aptos/v1.6.0/events) - Event emissions for tracking cross-chain messages + +### Error Handling + +- [Errors](/ccip/api-reference/aptos/v1.6.0/errors) - Comprehensive list of CCIP error codes for the Aptos blockchain diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/messages.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/messages.mdx new file mode 100644 index 00000000000..19e7f2c85d6 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/messages.mdx @@ -0,0 +1,163 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Messages API Reference" +metadata: + description: "API documentation for CCIP message structures and extra args on the Aptos blockchain." +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Messages + +This section details the data structures used for sending and receiving CCIP messages on Aptos. + +### Any2AptosTokenAmount + +The `Any2AptosTokenAmount` struct represents a specific token and amount being delivered to the Aptos chain as part of a cross-chain message. + +```rust +// As defined in ccip::client +struct Any2AptosTokenAmount has store, drop, copy { + token: address, + amount: u64 +} +``` + +**Parameters** + +| Field | Type | Description | +| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- | +| `token` | `address` | The address of the token on Aptos. | +| `amount` | `u64` | The number of tokens transferred, represented in the token's smallest unit (e.g., Octas for APT-based tokens). | + +**Where it's used**: + +- As part of the `dest_token_amounts` vector in the `Any2AptosMessage` struct when receiving a message on Aptos. + +### Aptos2AnyMessage + +(Conceptual) + +This isn't a formal struct but represents the parameters passed to the `onramp::ccip_send` entry function to initiate a cross-chain message from Aptos. + +**Parameters** + +| Field | Type | Description | +| ------------------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `receiver` | `vector` | The destination address in its native byte format (e.g., 20 bytes for EVM addresses, 32 for Aptos). | +| `data` | `vector` | The arbitrary data payload to be processed by the receiver on the destination chain. | +| `token_addresses` | `vector
` | A vector of Aptos token type addresses to transfer. | +| `token_amounts` | `vector` | A vector of amounts for each corresponding token, in the token's smallest denomination. | +| `token_store_addresses` | `vector
` | A vector of Fungible Asset store addresses from which tokens are withdrawn. Use `0x0` to default to the primary store of the sender's account. | +| `fee_token` | `address` | The type address of the token used to pay CCIP fees. Use `0xa` if paying with native APT. | +| `fee_token_store` | `address` | The Fungible Asset store address for the fee token. Use `0x0` to default to the primary store. | +| `extra_args` | `vector` | Serialized byte vector containing additional arguments for the destination chain, such as gas limits. | + +### Any2AptosMessage + +```rust +// As defined in ccip::client +struct Any2AptosMessage has store, drop, copy { + message_id: vector, + source_chain_selector: u64, + sender: vector, + data: vector, + dest_token_amounts: vector +} +``` + +**Parameters** + +| Field | Type | Description | +| ------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `message_id` | `vector` | The unique 32-byte identifier for this cross-chain message. | +| `source_chain_selector` | `u64` | The unique identifier of the blockchain where the message originated. | +| `sender` | `vector` | The sender's address on the source blockchain, in its native byte format. | +| `data` | `vector` | The arbitrary data payload sent from the source chain for your module to process. | +| `dest_token_amounts` | `vector` | A list of tokens and their amounts that were bridged and are being delivered to the receiver module on Aptos. | + +### Aptos2AnyRampMessage + +```rust +// As defined in the OnRamp module +struct Aptos2AnyRampMessage has store, drop, copy { + header: RampMessageHeader, + sender: address, + data: vector, + receiver: vector, + extra_args: vector, + fee_token: address, + fee_token_amount: u64, + fee_value_juels: u256, + token_amounts: vector +} +``` + +**Parameters** + +| Field | Type | Description | +| ------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `header` | `RampMessageHeader` | Metadata about the message: `message_id`, `source_chain_selector`, `dest_chain_selector`, `sequence_number`, and `nonce`. | +| `sender` | `address` | The sender's 32-byte Aptos account address on the source chain. | +| `data` | `vector` | The arbitrary payload data. | +| `receiver` | `vector` | The destination address in its native byte format. | +| `extra_args` | `vector` | The serialized "extra arguments" relevant to the destination, such as gas limits. | +| `fee_token` | `address` | The type address of the token used to pay CCIP fees. | +| `fee_token_amount` | `u64` | The amount of the `fee_token` (in its native denomination) that was charged. | +| `fee_value_juels` | `u256` | The total fee value converted to LINK Juels (1e18 decimals) for standardized value reporting. | +| `token_amounts` | `vector` | A list of tokens locked or burned on Aptos as part of the message. | + +## Extra Args + +### Tags + +| Constant | Value (Hex) | Purpose | +| ------------------------------------ | ------------------------- | ----------------------------------------------------------------------- | +| `EVM_EXTRA_ARGS_V2_TAG` | `0x181dcf10` | Denotes `GenericExtraArgsV2` for Aptos → EVM or Aptos → Aptos messages. | +| `SVM_EXTRA_ARGS_V1_TAG` | `0x1f3b3aba` | Denotes `SVMExtraArgsV1` for Aptos → SVM messages. | + +--- + +### GenericExtraArgsV2 + +```rust +// Conceptual representation of data to be encoded +struct GenericExtraArgsV2 { + gas_limit: u256, + allow_out_of_order_execution: bool, +} +``` + +**Parameters** + +| Field | Type | Description | +| ------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `gas_limit` | `u256` | The gas limit for calling the receiver on the destination chain. For EVM, this is the gas for `ccipReceive`. For Aptos, it's the gas for your `ccip_receive` function. | +| `allow_out_of_order_execution` | `bool` | If `true`, the message can be processed by the CCIP network in a different order than it was sent. It is recommended to set this to `true`. | + +--- + +### SVMExtraArgsV1 + +```rust +// Conceptual representation of data to be encoded +struct SVMExtraArgsV1 { + compute_units: u32, + account_is_writable_bitmap: u64, + allow_out_of_order_execution: bool, + token_receiver: [u8; 32], + accounts: vector>, +} +``` + +**Parameters** + +| Field | Type | Description | +| ------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `compute_units` | `u32` | The number of compute units allocated to the SVM program. | +| `account_is_writable_bitmap` | `u64` | Bitmap indicating which accounts are writable during program execution. | +| `allow_out_of_order_execution` | `bool` | If `true`, allows the message to be processed out of order by the CCIP network. | +| `token_receiver` | `[u8; 32]` | A 32-byte Solana address that will receive any bridged tokens. If input is shorter than 32 bytes, it will be zero-padded on the left. | +| `accounts` | `vector>` | A list of serialized SVM accounts passed to the program. | diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/receiver.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/receiver.mdx new file mode 100644 index 00000000000..0bd495b6922 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/receiver.mdx @@ -0,0 +1,142 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Receiver API Reference" +metadata: + description: "API documentation for implementing CCIP receivers on the Aptos blockchain." +--- + +import { Aside, ClickToZoom } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Receiver + +Below is a complete API reference for the `ccip_receive` function that must be implemented by any Aptos Move module wishing to receive CCIP messages. + +### `ccip_receive` + +This function is the required entry point for a module to be a valid CCIP receiver. It is not a standard `entry` function called directly by users. Instead, it is triggered as a secure callback by the CCIP `Receiver Dispatcher` through the `dispatchable_fungible_asset` standard. + +```rust +// As implemented in the ccip_message_receiver example +public fun ccip_receive( + _metadata: Object +): Option acquires YourModuleState +``` + +#### Parameters + +| Name | Type | Description | +| ------------------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `_metadata` | `Object` | A generic object provided by the `dispatchable_fungible_asset` callback mechanism. It is not directly used, but is a required part of the function signature for the callback to work. | + +#### Returns + +| Name | Type | Description | +| ------------------------ | --------------------------- | ----------------------------------------------------------------------------------------- | +| `(unnamed)` | `Option` | The function must return `option::none()` to conform to the dispatchable asset interface. | + +#### Execution Flow (The Secure Callback Pattern) + +1. The CCIP **OffRamp** calls the **Receiver Dispatcher**. +2. The **Receiver Dispatcher** securely stores the incoming `Any2AptosMessage` payload in the **Receiver Registry**. +3. The **Receiver Dispatcher** then triggers a `derived_supply` call on a dummy fungible asset that was registered for your receiver module. +4. This `derived_supply` call invokes your module's `ccip_receive` function as the registered callback. +5. Your `ccip_receive` implementation **must** then immediately call `receiver_registry::get_receiver_input` to securely fetch the message payload that was stored for it. + +#### Implementation Requirements + +1. **Module Registration**: Your module must be registered as a valid receiver by calling `receiver_registry::register_receiver`. This is typically done once during your module's initialization and requires defining a unique, empty `ProofType` struct. + +2. **Account Type**: + + - If your module will handle tokens, it **must** be deployed to a **Resource Account**. + - If your module is data-only, it can be deployed to a regular user account or a code object account. + +3. **Security Validations**: Your `ccip_receive` function is responsible for application-level security checks, such as verifying the `source_chain_selector` and `sender` from the fetched message against an allowlist. + +--- + +## Example + +Below is a minimal implementation of a `ccip_receive` function, based on the `ccip_message_receiver` module as given in the [Aptos Starter Kit](https://github.com/smartcontractkit/aptos-starter-kit): + +```rust +// In your custom receiver module +// 1. Define a unique, empty proof struct +struct CCIPReceiverProof has drop {} + +// 2. Implement the ccip_receive function with the correct signature +public fun ccip_receive( + _metadata: Object +): Option acquires CCIPReceiverState { + // 3. Load state and create a signer for the module's resource account + let state = borrow_global_mut(@receiver); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + // 4. Fetch the message payload from the registry using your proof type + let message = receiver_registry::get_receiver_input( + @receiver, CCIPReceiverProof {} + ); + + // 5. Process the message... + let data = client::get_data(&message); + if (data.length() != 0) { + // Your logic here + } + + option::none() +} +``` + +--- + +## Token Handling + +When your receiver module is the destination for a token transfer, the CCIP Off-Ramp automatically deposits the assets into your module account's primary fungible store before your `ccip_receive` function is called. + +### Using Received Tokens + +To access and manage these tokens (e.g., to forward them to another user), your module must use the `SignerCapability` stored in its resource. This capability allows the module to generate a `signer` for itself and authorize outgoing transfers. + +```rust +// Example logic inside ccip_receive to forward tokens +// Assumes `CCIPReceiverState` resource holds the `signer_cap` + +public fun forward_tokens( + state_signer: &signer, // The signer generated from the SignerCapability + token_address: address, + amount: u64, + final_recipient: address +) { + let fa_metadata = object::address_to_object(token_address); + + // Get the store for the receiver module itself + let receiver_store = primary_fungible_store::ensure_primary_store_exists( + signer::address_of(state_signer), + fa_metadata + ); + + // Get the store for the final recipient + let final_recipient_store = primary_fungible_store::ensure_primary_store_exists( + final_recipient, + fa_metadata + ); + + // Authorize the transfer using the module's own signer + fungible_asset::transfer( + state_signer, + receiver_store, + final_recipient_store, + amount + ); +} +``` + + + + diff --git a/src/content/ccip/api-reference/aptos/v1.6.0/router.mdx b/src/content/ccip/api-reference/aptos/v1.6.0/router.mdx new file mode 100644 index 00000000000..caa80212e42 --- /dev/null +++ b/src/content/ccip/api-reference/aptos/v1.6.0/router.mdx @@ -0,0 +1,79 @@ +--- +section: ccip +date: Last Modified +title: "CCIP v1.6.0 Aptos Router API Reference" +metadata: + description: "API documentation for the CCIP router module on the Aptos blockchain." +--- + +import { Aside, ClickToZoom } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Router + +Below is a complete API reference for the primary user-facing functions in the Aptos CCIP `router` module. Unlike SVM-based chains, Aptos does not require a complex list of accounts to be passed in a `Context`. Instead, all necessary information is passed directly as arguments to the entry functions. + +### `ccip_send` + +This entry function is located in the `ccip_router::router` module and serves as the primary entry point for sending a cross-chain message. After performing initial checks, such as validating the destination chain and its supported OnRamp version, it forwards the call to the underlying `ccip_send` function in the appropriate `ccip_onramp::onramp` module to process the request. + +```rust +// As defined in ccip_router::router +public entry fun ccip_send( + caller: &signer, + dest_chain_selector: u64, + receiver: vector, + data: vector, + token_addresses: vector
, + token_amounts: vector, + token_store_addresses: vector
, + fee_token: address, + fee_token_store: address, + extra_args: vector +) +``` + +#### Parameters + +| Name | Type | Description | +| ------------------------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `caller` | `&signer` | The account signing the transaction and initiating the CCIP message. This account pays for the transaction fees and provides the tokens. | +| `dest_chain_selector` | `u64` | The unique CCIP blockchain identifier of the destination blockchain. | +| `receiver` | `vector` | The destination address in its native byte format. For details, see the [Building Aptos to EVM Messages guide](/ccip/tutorials/aptos/source/build-messages#receiver). | +| `data` | `vector` | The arbitrary data payload to be processed by the receiver on the destination chain. | +| `token_addresses` | `vector
` | A vector of Aptos token type addresses to transfer. This should be an empty vector if no tokens are being sent. | +| `token_amounts` | `vector` | A vector of amounts for each corresponding token in `token_addresses`, specified in the token's smallest denomination. | +| `token_store_addresses` | `vector
` | A vector of Fungible Asset store addresses from which tokens are withdrawn. Use `0x0` to default to the primary store of the `caller`'s account for each corresponding token. | +| `fee_token` | `address` | The type address of the token used to pay CCIP fees. Use `0xa` if paying with native APT. | +| `fee_token_store` | `address` | The Fungible Asset store address for the fee token. Use `0x0` to default to the primary store. | +| `extra_args` | `vector` | A serialized byte vector containing additional arguments for the destination chain, such as gas limits. See the [Aptos Messages API Reference](/ccip/api-reference/aptos/v1.6.0/messages#extra-args) for encoding details. | + +### `get_fee` + +This `#[view]` function is located in the `ccip_router::router` module. It allows you to preview the fee for a CCIP message before sending it. After validating the destination chain, it forwards the call to the corresponding `get_fee` function in the `ccip_onramp::onramp` module to get an accurate quote. This is essential for determining the fee amount to provide in your `ccip_send` call. + +```rust +// As defined in ccip_router::router +#[view] +public fun get_fee( + dest_chain_selector: u64, + receiver: vector, + data: vector, + token_addresses: vector
, + token_amounts: vector, + token_store_addresses: vector
, + fee_token: address, + fee_token_store: address, + extra_args: vector +): u64 +``` + +#### Parameters + +The parameters for `get_fee` are identical to those of `ccip_send`, excluding the `caller` signer. You should pass the exact same arguments that you intend to use for your `ccip_send` call to get an accurate fee quote. + +#### Returns + +| Name | Type | Description | +| -------------------------- | ------------------- | ----------------------------------------------------------------------------- | +| `fee_amount` | `u64` | The calculated fee in the smallest denomination of the specified `fee_token`. | diff --git a/src/content/ccip/api-reference/index.mdx b/src/content/ccip/api-reference/index.mdx index 19f3b7d8ec7..bd26148dba8 100644 --- a/src/content/ccip/api-reference/index.mdx +++ b/src/content/ccip/api-reference/index.mdx @@ -3,12 +3,12 @@ section: ccip date: Last Modified title: "CCIP API Reference" metadata: - description: "Complete API reference documentation for Chainlink CCIP. Access developer resources for both EVM and Solana." + description: "Complete API reference documentation for Chainlink Cross-Chain Interoperability Protocol (CCIP). Access developer resources for EVM, Solana, and Aptos blockchains." isIndex: true --- Chainlink Cross-Chain Interoperability Protocol (CCIP) provides secure cross-chain messaging and token transfers between blockchain networks. - **[EVM-based Blockchains](/ccip/api-reference/evm)**: Complete API reference for CCIP on Ethereum Virtual Machine (EVM) compatible blockchains. - - **[Solana](/ccip/api-reference/svm)**: Complete API reference for CCIP on Solana. +- **[Aptos Blockchain](/ccip/api-reference/aptos)**: Complete API reference for CCIP on the Aptos blockchain. diff --git a/src/content/ccip/api-reference/svm/index.mdx b/src/content/ccip/api-reference/svm/index.mdx index 436cb26ca32..eefa2be4a35 100644 --- a/src/content/ccip/api-reference/svm/index.mdx +++ b/src/content/ccip/api-reference/svm/index.mdx @@ -26,5 +26,6 @@ Each version includes detailed documentation for: - Message Structures and Extra Args - Router Instructions +- Receiver Instructions - Events - Error Handling diff --git a/src/content/ccip/api-reference/svm/v1.6.0/index.mdx b/src/content/ccip/api-reference/svm/v1.6.0/index.mdx index 28279e4db7b..4edaf5f3c40 100644 --- a/src/content/ccip/api-reference/svm/v1.6.0/index.mdx +++ b/src/content/ccip/api-reference/svm/v1.6.0/index.mdx @@ -21,6 +21,7 @@ import CcipCommon from "@features/ccip/CcipCommon.astro" - [Messages](/ccip/api-reference/svm/v1.6.0/messages) - Message structures and extra args for cross-chain messaging - [Router](/ccip/api-reference/svm/v1.6.0/router) - Instructions for sending messages through CCIP on Solana +- [Receiver](/ccip/api-reference/svm/v1.6.0/receiver) - Instructions for receiving and processing CCIP messages on Solana - [Events](/ccip/api-reference/svm/v1.6.0/events) - Event emissions for tracking cross-chain messages ### Error Handling diff --git a/src/content/ccip/api-reference/svm/v1.6.0/messages.mdx b/src/content/ccip/api-reference/svm/v1.6.0/messages.mdx index 4060fbb81b0..0781c33b570 100644 --- a/src/content/ccip/api-reference/svm/v1.6.0/messages.mdx +++ b/src/content/ccip/api-reference/svm/v1.6.0/messages.mdx @@ -51,7 +51,7 @@ You **prepend** a 4-byte "tag" inside your message, identifying which extra args | `EVM_EXTRA_ARGS_V2_TAG` | `0x181dcf10` | Denotes `EVMExtraArgsV2` (SVM → EVM extra args) | | `SVM_EXTRA_ARGS_V1_TAG` | `0x1f3b3aba` | Denotes `SVMExtraArgsV1` (SVM → SVM extra args) | -### `EVMExtraArgsV2` (SVM → EVM) +### `EVMExtraArgsV2` When sending a message **from SVM-based blockchains (e.g., Solana) to EVM-based blockchains (e.g., Ethereum)**, you can supply extra parameters such as a per-message `gas_limit`. This is particularly relevant for an EVM-based destination that requires enough gas to call the receiver contract. @@ -92,7 +92,7 @@ fn create_evm_extra_args(gas_limit: u128, ooo: bool) -> Vec { } ``` -### `SVMExtraArgsV1` (SVM → SVM) +### `SVMExtraArgsV1` If your destination is another SVM-based blockchain, the extra args can define `compute_units`, an accounts list, and whether you allow out-of-order execution, among other fields. diff --git a/src/content/ccip/concepts/architecture/index.mdx b/src/content/ccip/concepts/architecture/index.mdx index cf07efbd260..7b271641134 100644 --- a/src/content/ccip/concepts/architecture/index.mdx +++ b/src/content/ccip/concepts/architecture/index.mdx @@ -3,7 +3,7 @@ section: ccip date: Last Modified title: "CCIP Architecture" metadata: - description: "Learn the core architecture of Chainlink CCIP. Explore key concepts, on‑chain components, and off‑chain systems for cross‑chain communication." + description: "Learn about the core architecture of Chainlink CCIP. Explore key concepts, onchain components (EVM/Solana/Aptos), and offchain systems for cross-chain communication." image: "/images/ccip/concepts/architecture/ccip-offchain-architecture.jpg" excerpt: "ccip architecture, cross‑chain infrastructure, on‑chain components, off‑chain systems, decentralized oracle networks, risk management network, EVM smart contracts, Solana programs, router, onramp, offramp" datePublished: "2025-05-19T11:22:47Z" @@ -15,5 +15,5 @@ This section explains the core architecture of the Cross-Chain Interoperability - **[Overview](/ccip/concepts/architecture/overview)**: Get a high-level summary of the CCIP architecture. - **[Key Concepts](/ccip/concepts/architecture/key-concepts)**: Understand the essential terms and components within the CCIP ecosystem. -- **[Onchain Components](/ccip/concepts/architecture/onchain)**: Explore the on‑chain components, including EVM smart contracts and Solana programs, that operate directly on blockchains. +- **[Onchain Components](/ccip/concepts/architecture/onchain)**: Explore the on‑chain components, including EVM smart contracts and Solana programs, and Aptos modules, that operate directly on blockchains. - **[Offchain Components](/ccip/concepts/architecture/offchain)**: Discover the offchain systems, like the Risk Management Network and Decentralized Oracle Network, that support CCIP operations. diff --git a/src/content/ccip/concepts/architecture/key-concepts.mdx b/src/content/ccip/concepts/architecture/key-concepts.mdx index f7c7890338f..73442377fca 100644 --- a/src/content/ccip/concepts/architecture/key-concepts.mdx +++ b/src/content/ccip/concepts/architecture/key-concepts.mdx @@ -21,11 +21,11 @@ Before diving into Chainlink CCIP, ensure you understand these fundamental block - **Accounts / Addresses**: Many blockchains distinguish between user-controlled addresses (called Externally Owned Accounts (EOAs) on EVM blockchains) and onchain program addresses (often called contract accounts in EVM ecosystems). Even if the name or the underlying account model differs, the principle is that a user's private key or authority controls one address (EOA) while the other is controlled by onchain code or logic (contract account). -- **Smart Contracts**: On EVM-based blockchains, these are pieces of logic (often written in languages like Solidity) that get compiled into bytecode and deployed onchain. Other blockchains—such as SVM-based (e.g., Solana) or MVM-based (e.g., Sui or Aptos) chains—may refer to these as "modules," "programs," or simply "onchain code." The basic idea is the same: executable code that lives on the blockchain and enforces rules without centralized control. +- **Smart Contracts**: On EVM-based blockchains, these are pieces of logic (often written in languages like Solidity) that get compiled into bytecode and deployed onchain. Other blockchains—such as SVM-based (e.g., Solana), **Aptos**, or **Sui**—may refer to these as "modules," "programs," or simply "onchain code." The basic idea is the same: executable code that lives on the blockchain and enforces rules without centralized control. - **Decentralized Applications (dApps)**: [dApps](https://ethereum.org/en/developers/docs/dapps/) are applications that use onchain logic to manage data and transactions. -- **Token Standards**: Many chains use standards (like [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on EVM-based blockchains) or equivalents (e.g., [SPL tokens on Solana](https://spl.solana.com/token)) to represent fungible tokens. +- **Token Standards**: Many chains use standards (like [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on EVM-based blockchains) or equivalents (e.g., [SPL tokens on Solana](https://spl.solana.com/token), the [Fungible Asset standard on Aptos](https://aptos.dev/en/build/smart-contracts/fungible-asset), or the [Coin standard on Sui](https://docs.sui.io/standards/coin)) to represent fungible tokens. - **Merkle Trees**: A data structure that summarizes and verifies large information sets with small cryptographic proofs. To learn more, try this [tutorial](https://ethereum.org/en/developers/tutorials/merkle-proofs-for-offline-data-integrity/). @@ -37,7 +37,9 @@ When you see references to "smart contracts" or "onchain programs," they are exe - **SVM (Solana Virtual Machine)**: Solana uses the Sealevel parallelization engine to execute onchain programs compiled to Berkeley Packet Filter (BPF) bytecode. This parallel processing approach can handle multiple simultaneous transactions. Read this [page](https://solana.com/news/sealevel---parallel-processing-thousands-of-smart-contracts) to learn more. -- **MVM (Move Virtual Machine)**: Aptos, Sui, and other Move-based chains use the Move language and runtime. Move is designed to emphasize resource safety, access control, and predictable execution. Read this [page](https://aptos.dev/en/network/blockchain/move) to learn more. +- **Aptos**: The Aptos blockchain uses the Move language and its own implementation of the Move Virtual Machine (MoveVM). The MoveVM is designed for safety and verification, and Aptos processes transactions in parallel using its Block-STM execution engine. Read this [page](https://aptos.dev/en/network/blockchain/move) to learn more. + +- **Sui**: The Sui blockchain uses a variant of the Move language. Its architecture is object-centric and designed for high-throughput, parallel execution of transactions, enabling horizontal scaling. Read this [page](https://docs.sui.io/concepts/sui-move-concepts) to learn more. ## Cross-Chain dApps diff --git a/src/content/ccip/concepts/architecture/onchain/aptos/components.mdx b/src/content/ccip/concepts/architecture/onchain/aptos/components.mdx new file mode 100644 index 00000000000..c03fd2902c4 --- /dev/null +++ b/src/content/ccip/concepts/architecture/onchain/aptos/components.mdx @@ -0,0 +1,202 @@ +--- +section: ccip +date: Last Modified +title: "Onchain Architecture - Components (Aptos)" +metadata: + description: "Explore the onchain components of CCIP architecture on Aptos: Sender/Receiver, Router, OffRamp, FeeQuoter, Tokens, Token Pools, and RMN module details." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "ccip aptos components, architecture, router module, offramp module, fee quoter, token pools, rmn module, token admin dispatcher, token admin registry, receiver dispatcher, receiver registry" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + difficulty: "advanced" + estimatedTime: "35 minutes" +--- + +import { Aside } from "@components" + +This section provides more detail on the Onchain components for Aptos. + +## Sender/Receiver + +**CCIP supports the following as senders and receivers:** + +- A user-controlled account – A wallet/user account controlled by a private key (also called EOA on EVM blockchains). +- An onchain program – Smart contract logic (called "programs" on Solana, "modules" on Aptos, "smart contracts" on EVM chains). + +**CCIP messages can be sent in any of the following combinations (terminology adapts to the destination blockchain):** + +- **User Account → User Account / EOA / Wallet** + + - **Supported Message Type**: Token-only transfers. + - **Reason**: The recipient is a simple user account that does not have executable code. While a data payload can be sent in the CCIP message, the recipient account has no way to act upon or process that data. + +- **User Account → Module / Smart Contract / Program** + + - **Supported Message Type**: Token transfers, arbitrary data, and programmable token transfers (data + tokens). + - **Reason**: The receiving module is designed with a `ccip_receive` function that can be programmed to handle incoming tokens, process arbitrary data, or both simultaneously according to your application's logic. + +- **Module / Smart Contract / Program → User Account / EOA / Wallet** + + - **Supported Message Type**: Token-only transfers. + - **Reason**: Similar to the first case, the destination is a simple user account that cannot execute logic to process an incoming data payload. + +- **Module / Smart Contract / Program → Module / Smart Contract / Program** + - **Supported Message Type**: Token transfers, arbitrary data, and programmable token transfers (data + tokens). + - **Reason**: Both the sender and receiver are programmable entities, allowing for any combination of token and data to be sent and processed according to custom application logic. + +**A CCIP Message can include:** + +- An arbitrary bytes payload. +- A token transfer. +- A programmable token transfer (data + tokens). + +**Sender Responsibilities:** + +- Prepare the arguments for the `ccip_send` function. +- Retrieve a fee estimate by calling the `router::get_fee` view function. +- Call the `router::ccip_send` entry function to send the message. The sender's signature on this transaction authorizes the withdrawal of any tokens and fees. + +**Receiver Considerations:** + +- **Data Processing:** If the CCIP Message contains a data payload or a programmable token transfer, the receiver must be a Move module that implements a `ccip_receive` entry function. +- **Registration:** The receiver module must be registered with the `Receiver Registry` to be able to receive messages from the CCIP OffRamp. +- **Security Validation:** The receiver module should validate that any call to its `ccip_receive` function originates from an authorized CCIP OffRamp address. + +## Router + +The `ccip_router::router` module serves as the single, user-facing interface for sending all outbound CCIP messages from the Aptos blockchain. As a minimal module, its primary role is to act as a stable entry point that directs traffic to the appropriate `OnRamp`. + +The Router exposes two primary functions for the sender: + +- `get_fee`: A view function that retrieves the CCIP fee for a given message by forwarding the request to the `OnRamp`. +- `ccip_send`: The entry function that initiates a cross-chain message. It performs initial validations and then forwards the call to the correct `OnRamp` module for processing. + +## OnRamp + +The `ccip_onramp::onramp` module is an internal CCIP module that handles the core logic for processing outbound messages on the source chain. + +When the Router forwards a `ccip_send` request, the OnRamp performs the following actions: + +- **Validations** + + - Ensures that the destination chain is not cursed. + - Verifies that the sender is on the allowlist if one is enabled for the destination lane. + +- **Fee Collection** + + - Withdraws the pre-calculated fee amount from the sender's specified `fee_token_store`, defaulting to the user's primary store if `0x0` is provided. + +- **Token Handling** + + - If the message involves token transfers, it initiates a secure callback pattern by calling the `Token Admin Dispatcher`. + - The dispatcher uses the `Token Admin Registry` to store the message context (sender, receiver, etc.) before invoking the correct `Token Pool` to `lock_or_burn` the assets. + +- **Nonce Management** + + - For ordered messages, it calls the `Nonce Manager` to retrieve and increment the sender's nonce for that destination. + +- **Event Emission** + - Generates a unique `messageId` and emits a `CCIPMessageSent` event containing the complete, sequenced message details. + +## Nonce Manager + +The `ccip::nonce_manager` module enables optional strict message ordering. It tracks outbound nonces on a per-sender and per-destination-chain basis. + +When out-of-order execution is disabled for a message, the `OnRamp` module uses the `Nonce Manager` to assign an incrementing nonce, which is then verified by the `OffRamp` on the destination chain. + +## OffRamp + +The `ccip_offramp::offramp` module is an internal CCIP module that operates on the destination chain. It is the primary module that the offchain DONs interact with to deliver messages to Aptos. + +### Commit Phase + +During the **Commit Phase**, the following steps occur: + +1. **Commit Report Submission**: + The Committing DON calls the `commit` function on the `OffRamp` with a report that includes Merkle roots and price updates. + +1. **Validation**: + + - The `OffRamp` verifies the DON's signatures. + - It verifies any "blessed" Merkle roots. + +1. **Price Updates**: + It calls the `Fee Quoter` to update its onchain token and gas price data. + +1. **Event Emission**: + At the end of the Commit Phase, the `OffRamp` emits a `CommitReportAccepted` event. + +### Execution Phase + +In the **Execution Phase**, the `OffRamp` processes messages one by one: + +1. **Merkle Proof Verification**: + The `OffRamp` verifies the message's Merkle proof against a previously committed Merkle root. + +1. **Additional Validations**: + The `OffRamp` performs final validations, including ensuring the source chain is not cursed. + +1. **Token Processing (if applicable)**: + It calls the `Token Admin Dispatcher` to initiate the `release_or_mint` process. This follows a secure callback pattern where the dispatcher and `Token Pool` interact with the `Token Admin Registry` to securely process the token transfer before depositing the assets into the receiver's primary fungible store. + +1. **Message Delivery (if applicable)**: + It calls the `Receiver Dispatcher`, which securely stores the message payload in the `Receiver Registry` and then triggers the `ccip_receive` function on the registered receiver module. The receiver module is then responsible for calling the `Receiver Registry` to fetch this payload. + +1. **Final Execution Status**: + The `OffRamp` emits a final `ExecutionStateChanged` event, indicating a `SUCCESS` state. Due to Aptos's transaction atomicity, failed executions revert entirely, leaving the message state as `UNTOUCHED` for a potential retry. + +### Permissionless Manual Execution (Fallback) + +The `OffRamp` includes a `manually_execute` function. If automated execution fails, this function can be called permissionlessly after a configured time delay to ensure message delivery. For more information, read the [manual execution](/ccip/concepts/manual-execution) page. + +## Fee Quoter + +The `ccip::fee_quoter` module is responsible for all fee-related calculations. + +- **Source Chain**: Called by the `OnRamp` to provide a precise fee estimate for a given message. +- **Destination Chain**: Called by the `OffRamp` to receive and store updated token and gas price data included in commit reports from the CCIP network. + +## Token Admin Dispatcher + +The `ccip::token_admin_dispatcher` is a security-focused module that acts as a secure entry point for all token operations. Only authorized CCIP modules (specifically, the `OnRamp` and `OffRamp`) are permitted to call this dispatcher. It then looks up the correct `Token Pool` via the `Token Admin Registry` and initiates the `lock_or_burn` or `release_or_mint` operations, preventing direct, unauthorized calls to the token pools. + +## Token Admin Registry + +The `ccip::token_admin_registry` is a central onchain module that maintains the critical mapping between a token's address and the address of its designated `Token Pool` module. It serves as the single source of truth for the CCIP network to determine how to handle a specific token for cross-chain transfers. It also manages administrative rights for token configurations. + +## Receiver Dispatcher + +The `ccip::receiver_dispatcher` is a security-focused module that ensures only the authorized `OffRamp` module can deliver a message to a registered receiver module. When a message containing data arrives, the `OffRamp` calls this dispatcher. The dispatcher first verifies that the destination module is registered in the `Receiver Registry` before safely calling the `ccip_receive` function on that end-user's module. + +## Receiver Registry + +The `ccip::receiver_registry` is a module where developers can register their custom Move modules to make them officially recognizable as valid CCIP message receivers. A module must be registered here before it can receive data or programmatic token transfers from the `OffRamp` via the `Receiver Dispatcher`. + +## Tokens and Token Pools + +### Tokens + +- Tokens are developed by token developers and exist independently of the core CCIP modules. +- Most tokens built on the Aptos [Fungible Asset](https://aptos.dev/en/build/smart-contracts/fungible-asset) standard are compatible with CCIP. For more information on compatibility, refer to the CCT documentation. + +### Token Pools + +- Token Pools are deployed by token developers and exist independently of the core CCIP modules. +- Token Pools are modules interact with tokens created using the [Fungible Asset](https://aptos.dev/en/build/smart-contracts/fungible-asset) standard. +- Token pools follow standard models: + - **Lock/Release** + - **Burn/Mint** +- Audited code for `lock_release_token_pool` and `burn_mint_token_pool` modules is available in the CCIP repository. +- For tokens requiring custom logic before burn/mint/lock/release, developers may build custom pools on top of these base modules. More details are available in the CCT documentation. + +## RMN (Risk Management Network) Remote + +The `ccip::rmn_remote` module is a critical security component deployed on every CCIP-enabled chain. Various CCIP modules (like `OnRamp`, `OffRamp`, `Token Pools`) query this component to verify the status of other chains in the network. It maintains an onchain list of "cursed" (i.e., blocklisted) chains. If a source or destination chain is cursed, CCIP transactions involving that chain are halted. + + diff --git a/src/content/ccip/concepts/architecture/onchain/aptos/index.mdx b/src/content/ccip/concepts/architecture/onchain/aptos/index.mdx new file mode 100644 index 00000000000..e4d965bdb34 --- /dev/null +++ b/src/content/ccip/concepts/architecture/onchain/aptos/index.mdx @@ -0,0 +1,20 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Onchain Architecture (Aptos)" +metadata: + description: "Explore Chainlink CCIP's onchain architecture for Aptos. Learn about Move modules, components, message lifecycle, and upgradability strategies." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "ccip aptos architecture, modules, router module, offramp module, fee quoter, token pools, rmn module, token admin dispatcher, token admin registry, receiver dispatcher, receiver registry" + datePublished: "2025-09-03" + lastModified: "2025-09-03" +isIndex: true +--- + +Chainlink CCIP's Aptos onchain architecture consists of specialized Move modules deployed on both source and destination chains. These modules work together with CCIP's offchain infrastructure to provide end-to-end cross-chain interoperability. + +- **[Overview](/ccip/concepts/architecture/onchain/aptos/overview)**: Provides a high-level introduction to CCIP's Aptos-based onchain architecture, including a component diagram, key roles, and a detailed walk-through of a message's lifecycle from source to destination chain. + +- **[Components](/ccip/concepts/architecture/onchain/aptos/components)**: Describes each architectural component in detail. + +- **[Upgradability](/ccip/concepts/architecture/onchain/aptos/upgradability)**: Explains CCIP's approach to secure system evolution. diff --git a/src/content/ccip/concepts/architecture/onchain/aptos/overview.mdx b/src/content/ccip/concepts/architecture/onchain/aptos/overview.mdx new file mode 100644 index 00000000000..8a5690941f2 --- /dev/null +++ b/src/content/ccip/concepts/architecture/onchain/aptos/overview.mdx @@ -0,0 +1,126 @@ +--- +section: ccip +date: Last Modified +title: "Onchain Architecture - Overview (Aptos)" +metadata: + description: "Get an overview of Chainlink CCIP's onchain architecture for Aptos. Learn key components, roles, and the typical lifecycle of a cross-chain message on Aptos." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "ccip aptos overview, architecture, modules, router module, offramp module, fee quoter, token pools, rmn module, token admin dispatcher, token admin registry, receiver dispatcher, receiver registry" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + difficulty: "intermediate" + estimatedTime: "20 minutes" +--- + +import { Aside, ClickToZoom } from "@components" + +## Aptos as Source Chain + +On the Aptos source blockchain, a user or another module initiates a transaction by calling an entry function in the CCIP `Router` module. The `Router` module serves as the primary entry point, performing initial validations before forwarding the call to the appropriate `OnRamp` module. The `OnRamp` module then calculates fees, interacts with a designated Token Pool to lock or burn tokens, and finally emits a `CCIPMessageSent` event. This event is observed offchain by the Committing DON, which then securely relays the message to the destination blockchain. + +## Aptos as Destination Chain + +On the Aptos destination blockchain, an onchain module called the `OffRamp` receives a `commit` from the Committing DON. This `commit` contains Merkle roots of batched messages. The `OffRamp` module verifies the OCR signatures from the DON, checks the Risk Management Network (RMN) Remote module to ensure the source chain is not cursed, and then stores the verified Merkle root onchain. Subsequently, the Executing DON submits messages for execution. Aptos, like SVM, follows a one-message-per-transaction execution pattern, which is different from the batch execution possible on EVM chains. For each message, the `OffRamp` verifies its Merkle proof against a committed root, tracks the execution state by emitting an `ExecutionStateChanged` event, and processes the payload. This processing includes calling the appropriate Token Pool to release or mint tokens and, if the message contains data, calling the `ccip_receive` entry function on a designated receiver module. + +## Key Components + + + +**Source Chain**: + + + +**Destination Chain**: + + + +| Component | Ownership | Role | +| ---------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Sender/Receiver | External (User/Module) | An end-user wallet or a custom Move module that initiates a cross-chain message on the source chain and/or receives the message on the destination chain via the `ccip_receive` function. | +| Router | CCIP | The primary, user-facing entry point for sending outbound messages from Aptos. It validates the destination chain, determines the correct `OnRamp` version to use, and forwards the `ccip_send` call. | +| OnRamp | CCIP | A module that processes outbound messages on the source chain. It validates parameters, calculates fees by calling the FeeQuoter, manages token interactions with `Token Pools` via the TokenAdminDispatcher, increments sequence numbers, and emits a `CCIPMessageSent` event that offchain DONs observe. | +| Nonce Manager | CCIP | A module that tracks outbound nonces for messages on a per-destination-chain and per-sender basis, ensuring ordered message processing where required. | +| OffRamp | CCIP | A destination chain module that receives committed message roots from the offchain DON. It verifies these roots, checks the `RMN Remote` for curses, executes messages by verifying their Merkle proofs, and dispatches token/data payloads to their final destinations. It emits an `ExecutionStateChanged` event upon completion. | +| Fee Quoter | CCIP | A module that calculates the total fee required for a cross-chain message. It uses onchain price data for tokens and destination chain gas to provide an accurate cost estimate in the user's chosen fee_token. | +| Token Admin Dispatcher | CCIP | A dispatcher module that acts as a middleman between the `OnRamp`/`OffRamp` and the TokenAdminRegistry. It ensures that only authorized CCIP modules can initiate token lock, burn, release, or mint operations. | +| Token Admin Registry | CCIP | A module that acts as a central registry and maps a given token's address to its designated Token Pool module. It also manages administrative roles for each token, such as who can update its pool configuration. | +| Receiver Dispatcher | CCIP | A dispatcher module that ensures only the authorized `OffRamp` module can call the `ccip_receive` function on a registered receiver module. It uses the ReceiverRegistry to verify the destination. | +| Receiver Registry | CCIP | A module where custom Move modules can register themselves as valid CCIP message receivers. This registration is necessary for the `OffRamp` to be able to dispatch data and tokens to them. | +| Token Pools | External (Token Developer) | Specialized modules that handle cross-chain token transfers. Used by the `Token Admin Dispatcher` for dispatch_lock_or_burn operations on the source blockchain and dispatch_release_or_mint operations on the destination blockchain. | +| Token | External (Token Developer) | A fungible asset created on Aptos. | +| RMN Remote | CCIP | A module that maintains a list of "cursed" (i.e., blocklisted) chains. Various CCIP modules query this component to ensure they are not interacting with a cursed chain before processing a message. | + +## Typical Lifecycle of a Message + +### Source Blockchain (Aptos) + +This outlines the process when initiating a CCIP transaction from the Aptos blockchain. + +1. **Preparation** + + - The Sender (a user's wallet or another module) prepares the information for a CCIP Message, including: + + - **Receiver**: A byte array representing the destination address (e.g., a 20-byte EVM address). + - **Data payload**: Arbitrary bytes to be delivered to the receiver. + - **Tokens and amounts** (if applicable). + - **Fee token**: The address of the token for paying fees (e.g., native APT or LINK). + - **Extra Arguments**: An encoded byte vector containing destination-specific parameters, like a gas limit for EVM. + + - The Sender calls the `ccip_router::router::get_fee` view function to determine the total CCIP fee required for the message. This function internally calls the `Fee Quoter` module for the cost calculation. + + - The Sender prepares to call the `ccip_router::router::ccip_send` entry function. Unlike EVM and SVM chains, a separate token approve transaction is not required. The user's signature on the `ccip_send` transaction itself authorizes the CCIP modules to withdraw the necessary tokens and fees from the sender's account. + +1. **Sending** + + - When the `ccip_send` transaction is executed: + + - The `Router` module, as the entry point, performs initial validations and forwards the call to the appropriate `OnRamp` module. + + - The `OnRamp` module executes the core logic: + + - Verifications: It ensures that the destination chain is not cursed by checking with the `RMN Remote` module. + - Fee Collection: It withdraws the required fee from the sender's fungible asset store. If the fee_token_store address is specified as 0x0, the module automatically resolves to the primary_fungible_store corresponding to the sender's account and the chosen fee_token. + - Sequence Management: It increments the sequence number for the given destination chain. If required, it also interacts with the `Nonce Manager` to handle ordered message nonces. + - Token Handling (if applicable): For each token in the message, the `OnRamp` withdraws the funds from the sender's token store and calls the `Token Admin Dispatcher`. This dispatcher uses the `Token Admin Registry` as a secure state machine: + - It first calls `start_lock_or_burn` on the registry to store the message context (sender, receiver, etc.). + - It then invokes the appropriate Token Pool's `lock_or_burn` function. + - The Token Pool module, in turn, calls back into the registry to securely `get_lock_or_burn_input` and, after processing, `set_lock_or_burn_output`. This secure callback pattern ensures token operations are only performed within a valid CCIP transaction context. + + - Event Emission: A unique messageId is generated, and the `OnRamp` emits a `CCIPMessageSent` event containing the full, sequenced message details. + +1. **Initial Offchain Processing** + + - The CCIP Committing DON monitors the Aptos blockchain for the `CCIPMessageSent` event and begins processing the message offchain to prepare it for commitment on the destination chain. + +### Destination Blockchain (Aptos) + +This outlines the process when Aptos is the receiving chain for a CCIP message. + +1. **Commit Phase** + + - The final OCR report from the Committing DON, containing Merkle roots of batched messages and any price updates, is submitted to the `OffRamp` module's `commit` function. + - The `OffRamp` verifies the DON's signatures. + - If the report includes blessed Merkle roots, the `OffRamp` verifies the RMN signatures. + - If the report contains price updates, the `OffRamp` calls the `Fee Quoter` module to update its onchain token and gas price data. + - The `OffRamp` stores the verified Merkle root and emits a `CommitReportAccepted` event, confirming the messages are ready for execution. + +1. **Secondary Offchain Processing** + + - The CCIP Executing DON monitors for the `CommitReportAccepted` event. For each message in the committed batch, the DON computes its specific Merkle proof. Each message is then submitted in a separate transaction to the Aptos blockchain. + +1. **Execution Phase** + + - When the Executing DON calls the `OffRamp` module's `execute` function, the `OffRamp` first verifies the message's Merkle proof against a stored Merkle root. It performs several validations, including checking the `RMN Remote` module and ensuring the message has not already been executed. + - If the message includes tokens, the `OffRamp` calls the `Token Admin Dispatcher`. This dispatcher follows a secure callback pattern using the `Token Admin Registry`: it initiates a `release_or_mint` operation, which invokes the correct Token Pool. The pool then calls the registry to get its input data, mints or releases the tokens, and sets the output. The released tokens are then deposited into the receiver's primary fungible store. + - If the message contains arbitrary data, the `OffRamp` calls the `Receiver Dispatcher`. The dispatcher does not pass the message data directly. Instead, it securely stores the payload in the `Receiver Registry` and then triggers the `ccip_receive` entry function of the registered receiver module. The receiver module, in turn, must call receiver_registry::`get_receiver_input` within its own execution to securely fetch the message payload it is meant to process. + - The `OffRamp` emits a final `ExecutionStateChanged` event with the outcome. Due to the atomic nature of Aptos transactions, a successfully processed message will have a state of `SUCCESS` (2). If the transaction fails for any reason, it fully reverts, and the message remains `UNTOUCHED` (0), allowing for a later retry. + - The `OffRamp` module also provides a `manually_execute` function. If automated execution by the DON fails, this function can be permissionlessly called after a configured time delay to ensure the message can still be processed. For more information, read the [manual execution](/ccip/concepts/manual-execution) page. diff --git a/src/content/ccip/concepts/architecture/onchain/aptos/upgradability.mdx b/src/content/ccip/concepts/architecture/onchain/aptos/upgradability.mdx new file mode 100644 index 00000000000..92265b8145f --- /dev/null +++ b/src/content/ccip/concepts/architecture/onchain/aptos/upgradability.mdx @@ -0,0 +1,55 @@ +--- +section: ccip +date: Last Modified +title: "Onchain Architecture - Upgradability (Aptos)" +metadata: + description: "Learn about CCIP's upgradability strategy on Aptos, covering onchain configuration, module code upgrades, the timelocked review process and the cross-chain MCMS-based governance process." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "ccip aptos upgradability module upgrades manychainmultisig timelock governance configuration changes module code upgrades upgrade authority backward compatibility secure evolution expedited approval" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + difficulty: "advanced" + estimatedTime: "10 minutes" +--- + +Chainlink's Cross-Chain Interoperability Protocol (CCIP) is designed to evolve in response to new feature requests, security considerations, and the need to support additional blockchains over time. This requires a secure upgrade process that preserves CCIP's robust security while allowing for iterative improvements. + +## What Can Be Upgraded + +On the Aptos blockchain, upgradability primarily happens in two ways: + +1. **Onchain Configuration** + + Many CCIP modules (like `onramp`, `offramp`, `fee_quoter`) expose public entry functions that allow authorized accounts to adjust operational parameters. These functions modify onchain data stored in resources without requiring a new deployment. + + Examples include: + + - Enabling support for a new destination chain. + - Updating fee parameters. + +1. **Module Code Upgrades** + + In the CCIP deployment on Aptos, the core modules (`router`, `onramp`, `offramp`, `fee_quoter`, etc.) are grouped into packages and published under a single, unified Object. + + Aptos allows for module code to be upgraded in-place. This means a code upgrade for the CCIP protocol involves publishing the new, updated module bytecode to the existing Object address. + + Because the Object address remains unchanged: + + - External modules and off-chain clients that interact with CCIP do not need to update their stored addresses. + - They seamlessly begin interacting with the new code after the upgrade is published. + + This approach ensures that bug fixes and new features can be rolled out atomically and consistently while maintaining a stable on-chain address for the protocol. + +## Implementation Process + +All critical onchain configuration changes to CCIP on Aptos are governed by a secure, cross-chain process using the **ManyChainMultiSig (MCMS)** system, which functions similarly across all CCIP-supported chains. + +The onchain mechanism for this on Aptos is the `mcms_entrypoint` function found within the core CCIP modules. This function is designed to be called by the `mcms_registry`, allowing the multi-chain governance process to execute proposals. + +Any proposal must follow one of two paths: + +1. **Time-locked Review**: The proposal is submitted onchain and enters a mandatory review period. During this window, node operators securing CCIP can inspect the proposed change and veto it if necessary. If no veto occurs, the proposal becomes executable after the delay expires. + +1. **Expedited Approval**: For time-sensitive situations, a proposal can be passed via an expedited path if it receives explicit approval from a quorum of independent signers. + +Once a proposal is approved through either path, it can be executed, and the `mcms_entrypoint` on the target Aptos module is called with the specified changes. This entire process is publicly verifiable, ensuring transparency for all onchain upgrades. diff --git a/src/content/ccip/concepts/architecture/onchain/index.mdx b/src/content/ccip/concepts/architecture/onchain/index.mdx index bde7ec57eb6..1a029ae9efb 100644 --- a/src/content/ccip/concepts/architecture/onchain/index.mdx +++ b/src/content/ccip/concepts/architecture/onchain/index.mdx @@ -3,11 +3,11 @@ section: ccip date: Last Modified title: "Onchain Architecture" metadata: - description: "Learn about Chainlink CCIP's on‑chain architecture. Explore components specific to EVM-compatible chains and Solana." + description: "Learn about Chainlink CCIP's on‑chain architecture. Explore components specific to EVM-compatible chains, Solana, and Aptos." image: "/images/ccip/concepts/architecture/onchain-evm-architecture.jpg" - excerpt: "ccip on‑chain architecture, evm smart contracts, solana programs, router, onramp, offramp, commit store, token pools, ccip receiver, cross‑chain components, blockchain interoperability" + excerpt: "ccip on‑chain architecture, evm smart contracts, solana programs, aptos modules, router, onramp, offramp, commit store, token pools, ccip receiver, cross‑chain components, blockchain interoperability" datePublished: "2025-05-19T11:22:47Z" - lastModified: "2025-06-09T15:41:42Z" + lastModified: "2025-09-03" isIndex: true --- @@ -15,3 +15,4 @@ This section details the onchain components of the CCIP architecture, covering b - **[EVM Architecture](/ccip/concepts/architecture/onchain/evm)**: Learn about the onchain components specific to EVM environments. - **[SVM Architecture](/ccip/concepts/architecture/onchain/svm)**: Explore the onchain programs and components specific for SVM environments. +- **[Aptos Architecture](/ccip/concepts/architecture/onchain/aptos)**: Understand the onchain components and message structures for Aptos environments. diff --git a/src/content/ccip/concepts/best-practices/aptos.mdx b/src/content/ccip/concepts/best-practices/aptos.mdx new file mode 100644 index 00000000000..a846556c5e0 --- /dev/null +++ b/src/content/ccip/concepts/best-practices/aptos.mdx @@ -0,0 +1,208 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Best Practices (Aptos)" +metadata: + description: "Best practices for building secure and reliable cross-chain dApps using CCIP on Aptos. Includes destination/source verification, message handling, gas configuration, monitoring, multisig guidance, and Aptos-specific considerations for building secure cross-chain applications." + image: "/images/ccip/ccip-hl-v1.6.gif" + excerpt: "ccip aptos best practices, sender verification, message handling, extraArgs usage, gas configuration, receiver module deployment, multisig security, monitoring, and auditing guidance, ccip receive extra args" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + difficulty: "intermediate" + estimatedTime: "35 minutes" +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + + + + + +Before you deploy your cross-chain dApps to mainnet, make sure that your dApps follow the best practices in this document. You are responsible for thoroughly reviewing your code and applying best practices to ensure that your cross-chain dApps are secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. + +## Verify destination chain + +Before calling the `router::ccip_send` entry function, your application should verify that the destination chain is supported. Sending messages to unsupported chains will fail and waste transaction fees. + +**Example**: You can programmatically check for support by calling the `onramp::is_chain_supported` view function. Here is a TypeScript example: + +```ts +import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk" + +async function isDestinationChainSupported( + aptos: Aptos, + onRampAddress: string, + destinationChainSelector: string +): Promise { + const result = await aptos.view({ + payload: { + function: `${onRampAddress}::onramp::is_chain_supported`, + functionArguments: [destinationChainSelector], + }, + }) + return result[0] as boolean +} +``` + +## Verify source chain + +When implementing the `ccip_receive` entry function in your custom module, you should verify the `source_chain_selector` from the incoming `Any2AptosMessage`. This ensures your module only accepts messages from blockchains you trust. + +```rust +use ccip::client; +fun ccip_receive( + _proof: ProofType, +) { + let message = receiver_registry::get_receiver_input(module_address, _proof); + let source_chain = client::get_source_chain_selector(&message); + + // Your allowlist logic + assert!(is_allowed_source_chain(source_chain), E_UNTRUSTED_SOURCE_CHAIN); + + // ... rest of your logic +} +``` + +## Verify sender + +Your `ccip_receive` implementation should also validate the sender address in the `Any2AptosMessage` if your application logic depends on messages coming from specific source addresses. + +**Note**: This verification may not be necessary for all use cases, such as an application that accepts messages from any sender. + +```rust +// Inside your ccip_receive function +let sender_bytes = client::get_sender(&message); +assert!(is_trusted_sender(sender_bytes), E_UNTRUSTED_SENDER); +``` + +## Using `extraArgs` + + + +The `extra_args` parameter provides chain-specific configuration for cross-chain messaging. It controls execution parameters on the destination chain, including resource allocation and message ordering guarantees. + + + +### Setting `gasLimit` + +When sending a message from Aptos to an EVM chain, the `gasLimit` in `extraArgs` specifies the gas for the `ccipReceive` execution on the destination. + +- To transfer tokens directly to an EVM wallet (EOA), set the `gasLimit` to `0` because no contract execution is required. +- To call a receiver contract on EVM, you must estimate the required gas and set an appropriate `gasLimit`. Unused gas is not refunded. +- When sending a message from Aptos to another Aptos module, the `gasLimit` can be used to allocate a specific amount of gas for your `ccip_receive` function's execution. + +### Message Ordering (`allowOutOfOrderExecution` flag) + +- When sending a message **from EVM to Aptos**, the `extraArgs` on the EVM side contains an `allowOutOfOrderExecution: bool` parameter. + +- When sending a message **from Aptos**, the `extra_args` contains an `allow_out_of_order_execution: bool` parameter. + + + +## Decoupling CCIP Message Reception and Business Logic + +As a best practice, separate the logic for receiving a CCIP message from your core application logic. Your `ccip_receive` function should be lightweight, focusing on: + +- Verifying the caller and payload. +- Storing the message data in an onchain resource. +- Emitting an event. + +A separate function can then be called by a user or another process to consume the stored data and execute the main business logic. This pattern provides more control and allows for "escape hatches" to manage situations where the business logic encounters issues. + +## Key Concepts for Aptos Receivers + +When implementing a custom receiver module on Aptos to interact with CCIP, there are several key architectural patterns and constraints to understand. + +### Why must `ccip_receive` fetch its own data payload? + +The `ccip_receive` function in your module acts as a secure callback, triggered by the CCIP Off-Ramp, but it does not receive the message payload directly in its function arguments. Instead, the receiver module is responsible for actively retrieving the payload. + +- **Mechanism**: When a message arrives, the CCIP protocol temporarily stores the payload (the `Any2AptosMessage` struct) within the `ReceiverRegistry` module. Your `ccip_receive` function must then call `receiver_registry::get_receiver_input` to securely fetch this data within the same transaction. + +- **Rationale**: This design pattern ensures security. It confirms that only the correctly registered module at the designated receiver address can access the message payload, and only during the context of a valid CCIP execution initiated by the Off-Ramp. This prevents unauthorized access to message data. + +### When must a receiver module be deployed under a Resource Account? + +The choice between a **resource account** and a **user account** / **code object account** for deploying your receiver module depends entirely on whether the module will ever need to programmatically control assets. + +- **Modules That Handle Tokens**: If your module will receive tokens via CCIP and later needs to transfer them, it **must** be deployed under a **resource account**. This is because a resource account allows the module to generate a signer for its own address onchain, which is required to authorize the withdrawal or transfer of those assets. Without this signer capability, any tokens the module receives would be locked. + +- **Data-Only Modules**: Conversely, if your receiver module is designed **only to process arbitrary data** — for example, to update its own internal state or trigger an event—and will never hold or transfer assets, it can be deployed under a regular **user account** or **code object account** (the code object account is recommended). In this scenario, the module doesn't need to sign for any transactions on its own behalf, so the signer capability of a resource account is not necessary. + +### Why must each `ccip_receive` module be deployed under a unique account? + +The CCIP `ReceiverRegistry` is designed to map a single account address to a single, unique `ccip_receive` function. + +- **Constraint**: When a CCIP message arrives, it targets a specific receiver address. The protocol requires a deterministic way to find and invoke the correct function. Registering multiple `ccip_receive` functions at the same address would create an ambiguity that the protocol cannot resolve. + +- **Recommended Design Pattern**: While you are limited to one registered entry point per account, this does not limit your application's complexity. The recommended approach is to use your single `ccip_receive` function as a **dispatcher**. + - Your application can encode additional routing information inside the data payload of the CCIP message (e.g., using a function name or an action ID). + - Your single `ccip_receive` function then parses this data and calls the appropriate internal functions within your module to handle different logic paths. + +This pattern maintains a single, secure entry point for CCIP while allowing for flexible and sophisticated application logic. + +## Evaluate the security and reliability of the networks that you use + +Although CCIP has been thoroughly reviewed and audited, inherent risks might still exist based on your use case, the blockchain networks where you deploy your programs, and the network conditions on those blockchains. + +## Review and audit your code + +Before securing value with programs that implement CCIP interfaces and routers, ensure that your code is secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. + +## Soak test your dApps + +Be aware of the [Service Limits and Rate Limits for Supported Networks](/ccip/directory). Before you provide access to end users or secure value, soak test your cross-chain dApps. Ensure that your dApps can operate within these limits and operate correctly during usage spikes or unfavorable network conditions. + +## Monitor your dApps + +When you build applications that depend on CCIP, include monitoring and safeguards to protect against the negative impact of extreme market events, possible malicious activity on your dApp, potential delays, and outages. + +Create your own monitoring alerts based on deviations from normal activity. This will notify you when potential issues occur so you can respond to them. + +## Multi-Signature Authorities + +Multi-signature authorities enhance security by requiring multiple signatures to authorize transactions. + +### Threshold configuration + +Set an optimal threshold for signers based on the trust level of participants and the required security. + +### Role-based access control + +Assign roles with specific permissions to different signers, limiting access to critical operations to trusted individuals. + +### Hardware wallet integration + +Use hardware wallets for signers to safeguard private keys from online vulnerabilities. Ensure that these devices are secure and regularly updated. + +### Regular audits and updates + +Conduct periodic audits of signer access and authority settings. Update the multisig setup as necessary, especially when personnel changes occur. + +### Emergency recovery plans + +Implement procedures for recovering from lost keys or compromised accounts, such as a predefined recovery multisig or recovery key holders. + +### Transaction review process + +Establish a standard process for reviewing and approving transactions, which can include a waiting period for large transfers to mitigate risks. + +### Documentation and training + +Maintain thorough documentation of multisig operations and provide training for all signers to ensure familiarity with processes and security protocols. diff --git a/src/content/ccip/concepts/best-practices/index.mdx b/src/content/ccip/concepts/best-practices/index.mdx index 4be56aac84a..11ad0ab0c3d 100644 --- a/src/content/ccip/concepts/best-practices/index.mdx +++ b/src/content/ccip/concepts/best-practices/index.mdx @@ -15,3 +15,4 @@ This section outlines recommended practices for using Chainlink CCIP effectively - **[EVM Best Practices](/ccip/concepts/best-practices/evm)**: Recommended guidelines for interacting with CCIP on EVM-compatible chains. - **[SVM Best Practices](/ccip/concepts/best-practices/svm)**: Recommended guidelines for interacting with CCIP on SVM-based chains like Solana. +- **[Aptos Best Practices](/ccip/concepts/best-practices/aptos)**: Recommended guidelines for interacting with CCIP on Aptos chain. diff --git a/src/content/ccip/service-limits/aptos.mdx b/src/content/ccip/service-limits/aptos.mdx new file mode 100644 index 00000000000..01d49e476d2 --- /dev/null +++ b/src/content/ccip/service-limits/aptos.mdx @@ -0,0 +1,44 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Service Limits (Aptos)" +metadata: + description: "Explore Chainlink CCIP service limits for the Aptos blockchain, including message size, gas limits, token transfers, and execution parameters for EVM-Aptos lanes." + excerpt: "Chainlink CCIP, Aptos service limits, Aptos constraints, Message size limits, Token transfers, Cross-chain execution, Aptos parameters, EVM to Aptos, Aptos to EVM" + datePublished: "2025-09-03" + lastModified: "2025-09-03" +--- + +import { Aside } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + + + +## EVM to Aptos + +These limits apply to messages sent from an EVM-compatible chain to the Aptos blockchain. + +| Item | Description | Limit | +| :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :------------- | +| Maximum message data length | The total payload for the message, including user data and token transfer information. | 30,000 bytes | +| Message execution gas limit | The user-specified `gasLimit` in `extraArgs` for the execution of the `ccip_receive` function on Aptos. | 100,000 gas | +| Maximum number of tokens | The maximum number of distinct tokens that can be transferred in a single transaction. | 1 | +| Smart Execution time window | The maximum duration that CCIP will attempt to automatically execute a message on Aptos before manual execution is required. | 8 hours | +| Token pool execution gas limit | The default gas overhead allocated for a standard token transfer operation on Aptos when a message is received from an EVM chain. | 36 gas | +| Out of Order execution | The `allowOutOfOrderExecution` parameter in the `extraArgs` of a CCIP message from an EVM chain. | Must be `true` | + +## Aptos to EVM + +These limits apply to messages sent from the Aptos blockchain to an EVM-compatible chain. + +| Item | Description | Limit | +| :----------------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------------- | +| Maximum message data length | The `data` payload sent within the [CCIP message](/ccip/api-reference/aptos/v1.6.0/messages#aptos2anymessage). | 30,000 bytes | +| Message execution gas limit | The user-specified `gasLimit` in `extraArgs` for the execution of the `ccipReceive` function on the destination EVM chain. | 100,000 gas | +| Maximum number of tokens | The maximum number of distinct tokens that can be transferred in a single transaction. | 1 | +| Smart Execution time window | The maximum duration for the execution of a CCIP message on the destination EVM chain. | 8 hours | +| Token pool execution gas limit | The default gas overhead for executing token pool logic on the destination EVM chain for a standard token transfer. | 36 gas | +| Out of Order execution | The `allow_out_of_order_execution` parameter in the `extraArgs` of a CCIP message from Aptos. | Must be `true` | diff --git a/src/content/ccip/service-limits/index.mdx b/src/content/ccip/service-limits/index.mdx index cb3d5033111..3799b7131df 100644 --- a/src/content/ccip/service-limits/index.mdx +++ b/src/content/ccip/service-limits/index.mdx @@ -4,9 +4,9 @@ date: Last Modified title: "CCIP Service Limits" metadata: description: "Complete guide to Chainlink CCIP operational limits across all blockchain architectures. Covers EVM and Solana service constraints, network-specific limitations, message size limits, execution resources, and token transfer restrictions." - excerpt: "Chainlink CCIP, Service limits, EVM constraints, Solana constraints, Gas limits, Message size limits, Token transfer limits, Cross‑chain limitations, Network constraints, CCIP parameters" + excerpt: "Chainlink CCIP, Service limits, EVM constraints, Solana constraints, Aptos constraints, Gas limits, Message size limits, Token transfer limits, Cross-chain limitations, Network constraints, CCIP parameters" datePublished: "2025-06-27" - lastModified: "2025-06-27" + lastModified: "2025-09-03" isIndex: true --- @@ -14,6 +14,7 @@ This section outlines the operational limits for Chainlink CCIP across different - **[EVM Service Limits](/ccip/service-limits/evm)**: Service limits for Ethereum and other EVM-compatible blockchains. - **[Solana Service Limits](/ccip/service-limits/svm)**: Service limits for Solana. +- **[Aptos Service Limits](/ccip/service-limits/aptos)**: Service limits for Aptos. - **[Network-Specific Limits](/ccip/service-limits/network-specific-limits)**: Documented network-specific limitations (all blockchain families). Understanding these limits is essential for building reliable cross-chain applications that operate within CCIP's intended parameters. diff --git a/src/content/ccip/tutorials/aptos/cross-chain-tokens/index.mdx b/src/content/ccip/tutorials/aptos/cross-chain-tokens/index.mdx new file mode 100644 index 00000000000..a0de0b3a22d --- /dev/null +++ b/src/content/ccip/tutorials/aptos/cross-chain-tokens/index.mdx @@ -0,0 +1,19 @@ +--- +section: ccip +date: Last Modified +title: "Cross-Chain Token (CCT) Tutorials" +isIndex: true +metadata: + description: "Make tokens CCIP-compatible with the Cross-Chain Token (CCT) standard on Aptos." + image: "/images/ccip/CCIP_enabled_tokens_flowchart.jpg" + excerpt: "CCT, Aptos, cross-chain tokens, CCIP, Aptos FA, Fungible Asset, tutorials" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "10 minutes" + difficulty: "advanced" +--- + +import { Aside, ClickToZoom } from "@components" +import CcipCommon from "@features/ccip/CcipCommon.astro" + + diff --git a/src/content/ccip/tutorials/aptos/destination/arbitrary-messaging.mdx b/src/content/ccip/tutorials/aptos/destination/arbitrary-messaging.mdx new file mode 100644 index 00000000000..3871d412f52 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/arbitrary-messaging.mdx @@ -0,0 +1,238 @@ +--- +section: ccip +date: Last Modified +title: "Arbitrary Messaging: EVM to Aptos" +isIndex: false +metadata: + description: "Learn how to send arbitrary data messages from an EVM chain to an Aptos module using Chainlink's Cross-Chain Interoperability Protocol (CCIP). This step-by-step tutorial guides you through configuring and executing data-only cross-chain messages." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "arbitrary data, messaging, EVM→Aptos, LINK fees, logs" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "25 minutes" + difficulty: "advanced" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +This tutorial demonstrates how to send arbitrary data from an Ethereum Virtual Machine (EVM) chain to a Move module on the Aptos blockchain using Chainlink's Cross-Chain Interoperability Protocol (CCIP). You will learn how to configure a CCIP message that triggers a module execution on the destination chain. + + + +## Introduction + +This tutorial shows you how to send a data-only message from the Ethereum Sepolia testnet to a receiver module on the Aptos testnet. + +## What You will Build + +In this tutorial, you will: + +- Publish a CCIP receiver module to the Aptos Testnet. +- Configure a CCIP message for arbitrary data messaging. +- Send data from Ethereum Sepolia to your Aptos module. +- Pay for CCIP transaction fees using LINK tokens. +- Verify that the data was received and processed by the module on Aptos. + +## Understanding Arbitrary Messaging to Aptos + +This tutorial focuses on arbitrary messaging from EVM chains to Aptos Move modules. For detailed information about CCIP message structure and parameters, refer to the [guide on building CCIP messages from EVM to Aptos](/ccip/tutorials/aptos/destination/build-messages). + +### Key Points Specific to Arbitrary Messaging + +- **Module Execution**: The message is sent to a specific module on Aptos, triggering the execution of its `ccip_receive` function. +- **Mandatory Settings**: + - The `receiver` field of the CCIP message must be the account address of your destination Aptos module. + - The `tokenAmounts` array must be empty (`[]`). + - The `extraArgs` field should be encoded with a `gasLimit` and a `allowOutOfOrderExecution` flag. + - The `gasLimit` must be a tested value and sufficient for the execution of the `ccip_receive` function of the receiver module (i.e., the destination Aptos module). + - The `allowOutOfOrderExecution` flag must be set to `true` when Aptos is the destination chain. + +### Key Differences from Token Transfers + + + Purpose + Configuration + Destination + Verification + + **Token Transfers**: + - Simply moves assets between chains. + - No custom logic execution on the destination. + + **Arbitrary Messaging**: + - Sends a data payload to be processed by a module. + - Triggers module execution on the destination. + - Can update the module's on-chain state or perform complex logic. + + + + **Token Transfers**: + - `receiver` is the end-user's Aptos account address. + - `data` is empty (`0x`). + + **Arbitrary Messaging**: + - `receiver` is the Aptos module's account address. + - `data` contains the encoded payload. + - `tokenAmounts` is an empty array. + + + + **Token Transfers**: + - Tokens arrive in a user's primary fungible store. + + **Arbitrary Messaging**: + - Data arrives at a specific Move module. + - The module executes logic to process the data. + + + + **Token Transfers**: + - Check the token balance in the user's account. + - Use the Aptos Explorer to verify balances. + + **Arbitrary Messaging**: + - Verify the module's state has changed. + - Query the module's events or view functions to retrieve the data. + + + + +### The `ccip_message_receiver` Module + +This tutorial uses the `ccip_message_receiver` module from the `aptos-starter-kit` as the destination. + + + +The `ccip_message_receiver` is a simple module designed to receive arbitrary data. When its `ccip_receive` function is called, it decodes the data as a string and emits a `ReceivedMessage` event, providing a clear on-chain record that the message was processed. + +## Implementing Arbitrary Messaging + +In this section, you'll first publish the receiver module to Aptos and then use a script to send a message to it. + +### Publish the Receiver Module + +Before you can send a message, the destination module must exist on the Aptos Testnet. The starter kit provides a script to create a **Resource Account** and publish the `ccip_message_receiver` module to it. + +Run the following command: + +```bash +npx ts-node scripts/deploy/aptos/createResourceAccountAndPublishReceiver.ts +``` + +This command will output the address of the newly created resource account. **Copy this address**, as you will need it for the next step. + +### Configure and Send the Message + +The `evm2aptos/ccipSendMsgRouter.ts` script handles the configuration and sending of the CCIP message. The core of the script builds the message payload: + +```typescript +// From scripts/evm2aptos/ccipSendMsgRouter.ts +const ccipMessage = buildCCIPMessage( + recipient, // The address of your deployed Aptos receiver module + hexlify(toUtf8Bytes("Hello Aptos from EVM")), // Your data, hex-encoded + networkConfig.sepolia.linkTokenAddress, // Fee token + encodeExtraArgsV2(0n, true) // gasLimit is 0, allowOutOfOrderExecution is true +) +``` + +## Running the Arbitrary Messaging Script + +### Execute the Script + +Run the script from your terminal. You will need to provide the `--aptosReceiver` address you copied from the deployment step. This example sends from **Ethereum Sepolia**. + +```bash +npx ts-node scripts/evm2aptos/ccipSendMsgRouter.ts --sourceChain sepolia --feeToken link --aptosReceiver --msgString "Hello Aptos from EVM" +``` + +### Expected Output + +The script will output the progress of the transaction, including approvals and fee calculations, and finish by providing the transaction hash and the CCIP Message ID. + +```text +Base Fee (in LINK JUELS): ... +Fee with 20% buffer (in LINK JUELS): ... +Current Allowance of LINK token: 0 +Approval tx sent: 0x... +Approval transaction confirmed in block ... after 3 confirmations. +Router contract approved to spend ... of LINK token from your account. +Proceeding with the message transfer... +Transaction sent: 0x... +Waiting for transaction confirmation... +Transaction confirmed in block 8803642 after 3 confirmations. +✅ Transaction successful: https://sepolia.etherscan.io/tx/0x... +🆔 CCIP Message ID: 0x... +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x... +``` + +## Verification: Retrieving the Message + +After sending the message, you can verify its delivery and processing on Aptos. + +### Check Message Execution + +#### Use the CCIP Explorer to check the message status + +Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle. + +```text +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/ +``` + +#### Programmatically check the message status + +After you receive a CCIP Message ID, you can programmatically check if the CCIP message has been successfully executed on the Aptos network. This is done by querying the [`ExecutionStateChanged`](/ccip/api-reference/aptos/v1.6.0/events#execute_single_report) event emitted by the CCIP OffRamp module. The `evm2aptos/checkMsgExecutionStateOnAptos.ts` script is designed for this purpose. + +After 15-20 minutes, run the script using the CCIP Message ID you received from the previous step. + + + +**Command**: + +```bash +npx ts-node scripts/evm2aptos/checkMsgExecutionStateOnAptos.ts --msgId +``` + +_Replace `` with the actual CCIP Message ID from the log output._ + +**Output**: +When the message has been successfully delivered, you will see the following output: + +```text +Execution state for CCIP message is SUCCESS +``` + +### Query the Receiver Module + +Once the message is successfully executed, you can verify that your receiver module processed it. The `getLatestMessageOnAptos.ts` script queries the `ReceivedMessage` event that your module emitted. + +Run the verification script, passing the address of your receiver module: + +```bash +npx ts-node scripts/evm2aptos/getLatestMessageOnAptos.ts --aptosReceiver +``` + +### Expected Verification Output + +When you run the verification script, you should see the decoded message that was stored by your module, confirming the end-to-end flow was successful. + +```text +Latest message received on Aptos at : Hello Aptos from EVM +``` + +You can also manually verify this by finding the `offramp::execute` transaction for your module's address in the [Aptos Explorer](https://explorer.aptoslabs.com/?network=testnet) and checking the `Events` tab. + + diff --git a/src/content/ccip/tutorials/aptos/destination/build-messages.mdx b/src/content/ccip/tutorials/aptos/destination/build-messages.mdx new file mode 100644 index 00000000000..9070ed78d2b --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/build-messages.mdx @@ -0,0 +1,304 @@ +--- +section: ccip +date: Last Modified +title: "Building CCIP Messages from EVM to Aptos" +isIndex: false +metadata: + description: "Implement CCIP messages from EVM chains to Aptos. Guide covers message structure, parameters, and implementation for token transfers, arbitrary data, and programmatic token transfers (data+tokens)." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "message structure, Aptos accounts, object, resource account, extraArgs, tokens, data, CCIP, Aptos FA, Fungible Asset" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "25 minutes" + difficulty: "advanced" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Introduction + +This guide explains how to construct CCIP Messages from Ethereum Virtual Machine (EVM) chains (e.g., Ethereum, Arbitrum, Base, etc.) to the Aptos blockchain. We'll cover the message structure, required parameters, and implementation details for different message types including token transfers, arbitrary data messaging, and programmatic token transfers (data and tokens). + + + +## CCIP Message Structure + +CCIP messages from EVM are built using the [`EVM2AnyMessage`](/ccip/api-reference/evm/v1.6.0/client#evm2anymessage) struct from the [`Client.sol`](/ccip/api-reference/evm/v1.6.0/client) library. The `EVM2AnyMessage` struct is defined as follows: + +```solidity +struct EVM2AnyMessage { + bytes receiver; + bytes data; + EVMTokenAmount[] tokenAmounts; + address feeToken; + bytes extraArgs; +} +``` + +### receiver + +- **Definition**: The 32-byte account address of the receiver on Aptos. +- **For token-only transfers**: This is the **end user's wallet address** on Aptos. +- **For arbitrary messaging** or **programmatic token transfers**: This must be the account address of your custom Aptos module that is intended to receive and process the message. + + + +### data + +- **Definition**: Contains the payload to be delivered to the destination chain. +- **For token-only transfers**: Must be empty (`0x`). +- **For arbitrary messaging** or **programmatic token transfers**: Contains the custom data the receiver module will process. +- **Encoding requirement**: Must be encoded as a hex string with a `0x` prefix. + + + +### tokenAmounts + +- **Definition**: An array of token addresses and amounts to transfer. +- **For data-only messages**: Must be an empty array (`[]`). +- **For token transfers** or **programmatic token transfers**: Each entry specifies a token address and amount. **Note**: Check the [CCIP Directory](/ccip/directory) for the list of supported tokens on each lane. + +### feeToken + +- **Definition**: Specifies which token to use for paying CCIP fees. +- **For native gas token**: Use `address(0)` (`ethers.ZeroAddress`) to specify the source chain's native gas token (e.g., ETH on Ethereum). +- **For ERC-20 tokens**: Specify the ERC-20 token address for fee payment. **Note**: Check the [CCIP Directory](/ccip/directory) for the list of supported fee tokens on your source chain. + +## extraArgs + +For Aptos-bound messages, the `extraArgs` parameter is a byte string composed of a 4-byte tag (`0x181dcf10`) prepended to the ABI-encoded tuple `(uint256 gasLimit, bool allowOutOfOrderExecution)`. This format is specified in the [`GenericExtraArgsV2`](/ccip/api-reference/evm/v1.6.0/client#genericextraargsv2) reference. + +### gasLimit + +- **Definition**: Specifies the amount of gas units to allocate for execution on Aptos. +- **Usage**: For simple token and data transfers as shown in the examples, `0` is a sufficient value. For complex programmatic token transfers involving significant computation in your receiving module, this value must be determined through testing. + +### allowOutOfOrderExecution + +- **Definition**: A boolean flag required for the message. +- **Usage**: The provided example scripts consistently use `true` for this value. It must be set to `true` when Aptos is the destination chain. + + + +## Implementation by Message Type + +### Token Transfer + +Use this configuration when sending only tokens from an EVM chain to a user's wallet on Aptos. + + + Configuration + Example + + ``` + { + destinationChainSelector: APTOS_CHAIN_SELECTOR, + receiver: userAptosAddress, // The end user's 32-byte Aptos address + tokenAmounts: [{ token: tokenAddress, amount: tokenAmount }], + feeToken: feeTokenAddress, // or address(0) for native + data: "0x", // Data must be empty + extraArgs: { + gasLimit: 0, // Gas limit is 0 for token-only transfers + allowOutOfOrderExecution: true + } + } + ``` + + + ```javascript + const aptosRecipient = "0xabf820035b3b4104284b792e2a936c86ca843279e3427144cead5e4d5999a3d0"; + const tokenAmount = ethers.parseUnits("0.001", 18); // 0.001 CCIP-BnM + + const message = { + receiver: aptosRecipient, + data: "0x", // No data for a simple token transfer + tokenAmounts: [{ + token: "0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05", // CCIP-BnM on Sepolia + amount: tokenAmount + }], + feeToken: ethers.ZeroAddress, // Pay fee in native ETH + extraArgs: encodeExtraArgsV2(0n, true) + }; + ``` + + + + + + +### Arbitrary Messaging + +Use this configuration when sending only a data payload to a custom module on Aptos. + + + Configuration + Example + + ``` + { + destinationChainSelector: APTOS_CHAIN_SELECTOR, + receiver: yourAptosModuleAddress, + tokenAmounts: [], // Empty for data-only + feeToken: feeTokenAddress, // or address(0) for native + data: customHexData, + extraArgs: { + gasLimit: determinedGasLimit, // Must be tested + allowOutOfOrderExecution: true + } + } + ``` + + + ```javascript + const yourAptosModule = "0xca843279e3427144cead5e4d5999a3d0abf820035b3b4104284b792e2a936c86"; + const messageData = ethers.hexlify(ethers.toUtf8Bytes("Hello Aptos!")); + + const message = { + receiver: yourAptosModule, + data: messageData, + tokenAmounts: [], // No tokens + feeToken: "0x779877A7B0D9E8603169DdbD7836e478b4624789", // Pay fee in LINK + extraArgs: encodeExtraArgsV2(0n, true) + }; + ``` + + + + + + +### Programmatic Token Transfer (Data and Tokens) + +Use this configuration when sending both tokens and a data payload to a custom module on Aptos. + + + Configuration + Example + + ``` + { + destinationChainSelector: APTOS_CHAIN_SELECTOR, + receiver: yourAptosModuleAddress, + tokenAmounts: [{ token: tokenAddress, amount: tokenAmount }], + feeToken: feeTokenAddress, + data: customHexData, + extraArgs: { + gasLimit: determinedGasLimit, // Must be tested + allowOutOfOrderExecution: true + } + } + ``` + + + ```javascript + const yourAptosModule = "0xca843279e3427144cead5e4d5999a3d0abf820035b3b4104284b792e2a936c86"; + const messageData = ethers.hexlify(ethers.toUtf8Bytes("Deposit for user X")); + + const message = { + receiver: yourAptosModule, + data: messageData, + tokenAmounts: [{ + token: "0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05", // CCIP-BnM on Sepolia + amount: ethers.parseUnits("1.5", 18) + }], + feeToken: "0x779877A7B0D9E8603169DdbD7836e478b4624789", // Pay fee in LINK + extraArgs: encodeExtraArgsV2(200000n, true) // Gas determined from testing + }; + ``` + + + + + + +## Related Tutorials + +To see these concepts in action with step-by-step implementation guides, check out the following tutorials: + +- [Token Transfers: EVM to Aptos](/ccip/tutorials/aptos/destination/token-transfers) - Learn how to implement token-only transfers from EVM chains to Aptos wallets. +- [Arbitrary Messaging: EVM to Aptos](/ccip/tutorials/aptos/destination/arbitrary-messaging) - Learn how to send data messages from EVM chains to Aptos modules. +- [Programmatic Token Transfers: EVM to Aptos](/ccip/tutorials/aptos/destination/programmatic-token-transfers) - Learn how to send both tokens and data in a single message to trigger module execution with token transfers on Aptos. + +These tutorials provide complete, working examples using the concepts covered in this guide. + +## Further Resources + +- [**CCIP EVM Client API Reference**](/ccip/api-reference/evm/v1.6.0/client): Complete technical details about the `EVM2AnyMessage` struct, helper functions, and message construction on EVM chains. +- **Aptos TS-SDK Docs**: For more information on building the receiving Aptos module, refer to the [official Aptos TS-SDK docs](https://aptos.dev/en/build/sdks/ts-sdk). + + diff --git a/src/content/ccip/tutorials/aptos/destination/index.mdx b/src/content/ccip/tutorials/aptos/destination/index.mdx new file mode 100644 index 00000000000..5dde7bb662a --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/index.mdx @@ -0,0 +1,33 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Tutorials: EVM to Aptos" +isIndex: true +metadata: + description: "Learn how to implement cross-chain communication from Ethereum to Aptos using Chainlink CCIP. Tutorials cover token transfers, arbitrary messaging, and programmatic token transfers with detailed implementation guides." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "EVM→Aptos, tokens, arbitrary messaging, CCIP, programs, fees, Aptos FA, Fungible Asset, ERC20, token" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "5 minutes" + difficulty: "intermediate" +--- + +import { Aside } from "@components" + +This section provides comprehensive guides and tutorials for implementing cross-chain communication from Ethereum Virtual Machine (EVM) chains to Aptos chains using Chainlink's Cross-Chain Interoperability Protocol (CCIP). + +## Getting Started + +Before implementing specific use cases, it's important to understand the fundamental concepts and message structure for EVM to Aptos communication: + +- [Building CCIP Messages from EVM to Aptos](/ccip/tutorials/aptos/destination/build-messages) - Learn the core message structure, required parameters, and implementation details for all message types. +- [Prerequisites for EVM to Aptos Tutorials](/ccip/tutorials/aptos/destination/prerequisites) - Set up your development environment with Aptos CLI tools, wallets, and token accounts. + +## Tutorials by Use Case + +Depending on your specific needs, choose the appropriate tutorial: + +- [Token Transfers](/ccip/tutorials/aptos/destination/token-transfers) - Send tokens from EVM chains to Aptos wallets without program execution. +- [Arbitrary Messaging](/ccip/tutorials/aptos/destination/arbitrary-messaging) - Send data from EVM chains to Aptos module. +- [Programmatic Token Transfers](/ccip/tutorials/aptos/destination/programmatic-token-transfers) - Send both tokens and data in a single message to trigger module execution with token transfers. diff --git a/src/content/ccip/tutorials/aptos/destination/prerequisites.mdx b/src/content/ccip/tutorials/aptos/destination/prerequisites.mdx new file mode 100644 index 00000000000..3c270275006 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/prerequisites.mdx @@ -0,0 +1,191 @@ +--- +section: ccip +date: Last Modified +title: "Prerequisites for EVM to Aptos Tutorials" +isIndex: false +metadata: + description: "Complete setup guide for EVM to Aptos cross-chain development. Install Aptos CLI, Node.js, configure wallets, and get testnet tokens for CCIP Chainlink tutorials. Step-by-step instructions for developers building cross-chain applications." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "prerequisites, Aptos, CLI, wallets, tokens, setup" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "15 minutes" + difficulty: "beginner" +--- + +import { Aside } from "@components" + +Before starting the EVM to Aptos tutorials, ensure you have: + +## Development Environment + +- **Aptos CLI**: Install the Aptos CLI by following the official [installation guide](https://aptos.dev/en/build/cli). + + + +- **Node.js v20 or higher**: You can use the [nvm package](http://nvm.sh/) to install and switch between Node.js versions. Once installed, verify the node version with: + + ```bash + node -v + ``` + + Example output: + + ```text + $ node -v + v22.15.0 + ``` + +- **Npm**: For installing and managing dependencies. +- **Git**: For cloning the repository. + +## Starter Kit Repository + +1. Clone the CCIP Aptos Starter Kit: + + ```bash + git clone https://github.com/smartcontractkit/aptos-starter-kit.git + ``` + +1. Navigate to the directory: + + ```bash + cd aptos-starter-kit + ``` + +1. Install dependencies: + + ```bash + npm install + ``` + +## Understanding Named Addresses in the Manifest File (`Move.toml`) + +The Move build system uses a manifest file, `Move.toml`, to define a package, its dependencies, and any named addresses it references. Named addresses are human-readable aliases for on-chain hexadecimal addresses, which makes the Move code cleaner, more readable, and easier to manage. + +When you compile a Move package, the compiler replaces these aliases (e.g., `@ccip_router`) with their actual address values from the `Move.toml` file. + +In the [`aptos-starter-kit`](https://github.com/smartcontractkit/aptos-starter-kit), the example modules (like `ccip_message_sender`, `ccip_message_receiver`) need to interact with the core CCIP modules already deployed on the Aptos Testnet. The `[addresses]` section in their `Move.toml` files provides the necessary references. + +**Example `Move.toml` Address Block:** + +```toml +[addresses] +ccip = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +mcms = "0xbdf1b9aacb4e21bf6f255105831df0172e911d4748e488196fde10d2e2a4e32d" +mcms_register_entrypoints = "0x0" +ccip_onramp = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +ccip_router = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +``` + +Here is a breakdown of what each named address represents: + +- **`ccip`**: This is the main **Object address** under which the core CCIP packages and their modules (like `fee_quoter`, `rmn_remote`, `token_admin_registry`, etc.) are published. + +- **`ccip_router`** & **`ccip_onramp`**: These aliases also point to the main CCIP Object address. This is because the `router` and `onramp` modules are part of the packages published under that single, unified Object. The aliases are defined for clarity and consistency in the Move code's `use` statements. + +- **`mcms`**: This is the address of the on-chain **ManyChainMultiSig (MCMS)** module, which is responsible for handling governance and administrative actions for the CCIP protocol through a secure, time-locked [implementation process](/ccip/concepts/architecture/onchain/aptos/upgradability#implementation-process). + +- **`mcms_register_entrypoints`**: This is a special value used as a **compile-time flag**. By setting it to `0x0`, the module is compiled in a way that bypasses the logic for registering with the MCMS system. This is useful for certain testing or deployment scenarios where the MCMS module is not relevant. + +By defining these addresses in the `Move.toml` file, the example modules can interact with the core CCIP protocol without hardcoding long addresses, making the code portable and easier to update for different networks. + + + +## Wallets + +- **EVM Wallet and Private Key**: To send transactions from an EVM chain (like Ethereum Sepolia), you need a wallet and its private key. + + - Set up a wallet like [MetaMask](https://metamask.io/). + - You will need to export the private key for the account you intend to send _from_. Follow the [official MetaMask guide](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/) to obtain your private key and add it to the `.env` file as shown below. + +- **Aptos Account**: You'll need an Aptos account. If you don't have one, create it with: + + ```bash + aptos init --network testnet + ``` + + This command will guide you through creating a new account for Testnet and will save the credentials in a `.aptos/config.yaml` file. This also configures your Aptos CLI to use Testnet. + + {/* prettier-ignore */} + + + Verify your current configuration with: + + ```bash + aptos config show-profiles + ``` + + This should show your `default` profile configured for Testnet, with the `network` set to `Testnet`. + + Example output: + + ```text + $ aptos config show-profiles + + { + "Result": { + "default": { + "network": "Testnet", + "has_private_key": true, + "public_key": "ed25519-pub-0x2ecdd2d7bc0cbfe2e44c219ef9a9fddc986b384f4a01fb5d821cf0dab5d2fbae", + "account": "d0e227835c33932721d54ae401cfaae753c295024fe454aa029b5e2782d2fad4", + "rest_url": "https://fullnode.testnet.aptoslabs.com" + } + } + } + ``` + + Note down the `account` value from the output and **prepend `0x` to it**. You will need to use this value as one of the environment variables in your `.env` file, as shown below. + +## Environment Configuration (`.env` file) + +The starter kit uses a `.env` file to manage sensitive information like private keys and RPC URLs. Create a new file named `.env` in the root of the `aptos-starter-kit` directory by copying the example file: + +```bash +cp .env.example .env +``` + +Next, open the `.env` file and fill in the following values: + +- `PRIVATE_KEY`: The private key of your source wallet (EOA) on Ethereum Sepolia from which you're sending CCIP-BnM tokens. You can export your private key from your MetaMask Wallet, as shown in the [Wallets](/ccip/tutorials/aptos/destination/prerequisites#wallets) section above. +- `ETHEREUM_SEPOLIA_RPC_URL`: The RPC endpoint for the Ethereum Sepolia testnet. This is required to interact with the Ethereum Sepolia network. You can obtain an RPC URL by signing up for a personal endpoint from [Alchemy](https://www.alchemy.com/), [Infura](https://www.infura.io/), or another node provider service. + +**Example `.env` file:** + +``` +PRIVATE_KEY= +ETHEREUM_SEPOLIA_RPC_URL= +``` + +## Native Tokens for Transaction Fees + +**ETH** tokens are used for Ethereum Sepolia transaction fees. For these tutorials, we will also use **ETH** to pay for CCIP fees. + +- You can use the [Chainlink Faucet](https://faucet.chain.link) to get test ETH. + +## Obtaining Testnet Tokens + +### LINK Tokens on EVM Chains + +When using LINK tokens to pay for CCIP fees, you will need LINK tokens on Ethereum Sepolia. You can use the [Chainlink Faucet](https://faucet.chain.link) to get test LINK tokens. + +### BnM Tokens on EVM Chains + +To obtain CCIP-BnM tokens on Ethereum Sepolia, you can use the [Mint tokens in the documentation](/ccip/test-tokens#mint-tokens-in-the-documentation) section to get test BnM tokens. diff --git a/src/content/ccip/tutorials/aptos/destination/programmatic-token-transfers.mdx b/src/content/ccip/tutorials/aptos/destination/programmatic-token-transfers.mdx new file mode 100644 index 00000000000..7d31fced8ce --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/programmatic-token-transfers.mdx @@ -0,0 +1,206 @@ +--- +section: ccip +date: Last Modified +title: "Programmatic Token Transfers: EVM to Aptos" +isIndex: false +metadata: + description: "Learn how to send tokens and data together from an EVM chain to an Aptos module using Chainlink CCIP. This step-by-step tutorial guides you through a programmatic token transfer where a receiver module forwards tokens based on a data payload." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "programmatic token transfer, EVM→Aptos, messaging, LINK fees, logs" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "25 minutes" + difficulty: "advanced" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +This tutorial demonstrates how to send a programmatic token transfer—a message containing both tokens and arbitrary data—from an Ethereum Virtual Machine (EVM) chain to a Move module on the Aptos blockchain using Chainlink CCIP. + + + +## Introduction + +This tutorial shows you how to send CCIP-BnM tokens from the Ethereum Sepolia testnet to a receiver module on the Aptos testnet. The message will also include a data payload containing a final recipient address. The receiver module will then execute logic to forward the received tokens to that final address. + +## What You will Build + +In this tutorial, you will: + +- Publish a CCIP receiver module to the Aptos Testnet. +- Configure a CCIP message containing both a token transfer and a data payload. +- Send the message from Ethereum Sepolia to your Aptos module. +- Pay for CCIP transaction fees using LINK or native ETH. +- Verify that the receiver module executed its logic and forwarded the tokens to the final destination. + +## Understanding Programmatic Token Transfers + +A programmatic token transfer combines the features of a token transfer and an arbitrary message. It allows you to send assets and instructions in a single, atomic cross-chain transaction. + +- **Module Execution**: The message and tokens are sent to a specific module on Aptos, triggering the execution of its `ccip_receive` function. +- **Combined Payload**: + - The `receiver` is the address of your custom Aptos module. + - The `tokenAmounts` array is populated with the tokens to transfer. + - The `data` field contains the instructions for the module. +- The `extraArgs` field should be encoded with a `gasLimit` and a `allowOutOfOrderExecution` flag. + - The `gasLimit` must be a tested value and sufficient for the execution of the `ccip_receive` function of the receiver module (i.e., the destination Aptos module). This includes performing a token transfer in this case, but may also include other logic depending on the module's implementation and the exact use case. + - The `allowOutOfOrderExecution` flag must be set to `true` when Aptos is the destination chain. + +### The `ccip_message_receiver` Module + +This tutorial uses the `ccip_message_receiver` module from the `aptos-starter-kit`. Its `ccip_receive` function contains dispatcher logic. For this tutorial, we will trigger the part of its logic that handles a message containing both tokens and data. + +- **Logic**: When the module receives both tokens and data, it interprets the `data` payload as the 32-byte address of a final recipient. It then uses its on-chain `signer` capability (derived from being deployed on a Resource Account) to transfer the tokens it just received to that final recipient address. Finally, it emits a `ForwardedTokens` event. + + + +## Implementing the Programmatic Transfer + +### Publish the Receiver Module + + + +First, the destination module must be deployed on the Aptos Testnet. Because this module will handle and transfer tokens, it must be deployed to a **Resource Account**. + +Run the following command from the starter kit: + +```bash +npx ts-node scripts/deploy/aptos/createResourceAccountAndPublishReceiver.ts +``` + +**Copy the address** of the new resource account from the output. This is your receiver module's address. + +### Configure and Send the Message + +The `evm2aptos/ccipTokenForwarder.ts` script handles the configuration and sending of the message. The core of the script builds the message payload: + +```typescript +// From scripts/evm2aptos/ccipTokenForwarder.ts +const ccipMessage = buildCCIPMessage( + recipient, // The address of your deployed Aptos receiver module + aptosAccountAddress, // The final recipient's Aptos address, sent as data + tokenAddress, // The address of the CCIP-BnM token on Sepolia + tokenAmount, // The amount of tokens to send + feeTokenAddress, // The address of the fee token (e.g., LINK) + encodeExtraArgsV2(100000n, true) // A gasLimit for the receiver's logic +) +``` + +## Running the Script + +### Execute the Script + +Run the script from your terminal. You will need to provide three key arguments: + +- `--aptosReceiver`: The address of the module you just deployed. +- `--aptosAccount`: The final destination address where the tokens should be forwarded. +- `--amount`: The number of tokens to send. + +This example sends from **Ethereum Sepolia** and pays fees in LINK: + +```bash +npx ts-node scripts/evm2aptos/ccipTokenForwarder.ts --sourceChain sepolia --feeToken link --amount 0.001 --aptosReceiver --aptosAccount +``` + +### Expected Output + +The script will output the progress of the transaction and finish by providing the transaction hash and the CCIP Message ID. + +```text +Base Fee (in LINK JUELS): ... +Fee with 20% buffer (in LINK JUELS): ... +Current Allowance of CCIP-BnM token: 0 +Approval tx sent: 0x... +Approval transaction confirmed in block 8803748 after 3 confirmations. +Router contract approved to spend 1000000000000000 of CCIP-BnM token from your account. +Current Allowance of LINK token: 0 +Approval tx sent: 0x... +Approval transaction confirmed in block ... after 3 confirmations. +Router contract approved to spend ... of LINK token from your account. +Proceeding with the token transfer... +Transaction sent: 0x... +Waiting for transaction confirmation... +Transaction confirmed in block 8803754 after 3 confirmations. +✅ Transaction successful: https://sepolia.etherscan.io/tx/0x... +🆔 CCIP Message ID: 0x... +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x... +``` + +## Verification + +Verification is a multi-step process: you must confirm the message was executed and then confirm that your module's logic produced the correct outcome. + +### Check Message Execution + +#### Use the CCIP Explorer to check the message status + +Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle. + +```text +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/ +``` + +#### Programmatically check the message status + +After you receive a CCIP Message ID, you can programmatically check if the CCIP message has been successfully executed on the Aptos network. This is done by querying the [`ExecutionStateChanged`](/ccip/api-reference/aptos/v1.6.0/events#execute_single_report) event emitted by the CCIP OffRamp module. The `evm2aptos/checkMsgExecutionStateOnAptos.ts` script is designed for this purpose. + +After 15-20 minutes, run the script using the CCIP Message ID you received from the previous step. + + + +**Command**: + +```bash +npx ts-node scripts/evm2aptos/checkMsgExecutionStateOnAptos.ts --msgId +``` + +_Replace `` with the actual CCIP Message ID from the log output._ + +**Output**: +When the message has been successfully delivered, you will see the following output: + +```text +Execution state for CCIP message is SUCCESS +``` + +### Verify the Outcome + +Once execution is successful, you need to verify that your module correctly forwarded the tokens. + +- **Check the Event**: You can manually verify that your module emitted the correct `ForwardedTokens` event by using the [Aptos Explorer](https://explorer.aptoslabs.com/?network=testnet). + + 1. Search for your receiver module's address (the one you provided with `--aptosReceiver`). + 1. In the `Transactions` tab, find the latest transaction that calls the `offramp::execute` function. + 1. Click on the transaction and navigate to the `Events` tab. + 1. You should find an event with the following structure, confirming that your module's logic was executed: + + ```text + Account Address: + Creation Number: 3 + Sequence Number: 0 + Type: ::ccip_message_receiver::ForwardedTokens + Data: { + final_recipient: "" + } + ``` + +- **Check the Final Balance**: The ultimate verification is checking the token balance of the final recipient. Search for the address you provided in the `--aptosAccount` argument on the Aptos Explorer. Under the "Tokens" tab, you should see the new balance of the CCIP-BnM token. + + diff --git a/src/content/ccip/tutorials/aptos/destination/token-transfers.mdx b/src/content/ccip/tutorials/aptos/destination/token-transfers.mdx new file mode 100644 index 00000000000..8224bb630da --- /dev/null +++ b/src/content/ccip/tutorials/aptos/destination/token-transfers.mdx @@ -0,0 +1,232 @@ +--- +section: ccip +date: Last Modified +title: "Token Transfers: EVM to Aptos" +isIndex: false +metadata: + description: "Learn how to transfer tokens from Ethereum to Aptos using Chainlink's Cross-Chain Interoperability Protocol (CCIP). This step-by-step tutorial guides you through setting up and executing a basic token transfer from an EVM chain to an Aptos wallet." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "ERC-20, ERC20, Aptos FA, Fungible Asset, token transfer, EVM→Aptos, wallet, CCIP, fees, verification" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "20 minutes" + difficulty: "intermediate" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +This tutorial demonstrates how to transfer tokens from an Ethereum Virtual Machine (EVM) chain to an Aptos wallet using Chainlink CCIP. You will learn how to build a CCIP message on an EVM chain, send it to the CCIP router, and verify the transfer on the destination chain. + + + +## Introduction + +This tutorial covers transferring tokens from Ethereum Sepolia to an Aptos wallet without any additional data payload. + +## What You will Build + +In this tutorial, you will: + +- Use a script to configure a CCIP message for a token-only transfer. +- Send CCIP-BnM test tokens from Ethereum Sepolia to an Aptos wallet. +- Pay for CCIP transaction fees using either native ETH or LINK tokens. +- Monitor and verify your cross-chain transfer. + + + +## Understanding Token Transfers to Aptos + +This tutorial focuses on token-only transfers from an EVM chain to an Aptos wallet. + +Key points specific to token-only transfers to Aptos: + +- **No Program Execution**: Tokens are transferred directly to a wallet without executing a custom module. +- **Mandatory Settings**: + - The `receiver` field in the CCIP message must be the **end user's Aptos wallet address**. + - The `data` field must be empty (`0x`). + - The `extraArgs` field should be encoded with `gasLimit: 0` and `allowOutOfOrderExecution: true`. + +## Implementing Token Transfers + +In this section, you'll execute a token transfer from Ethereum Sepolia to Aptos Testnet using the example script located at `scripts/evm2aptos/ccipSendTokenRouter.ts` in the starter kit. + +### Token Transfer Configuration + +The core of the transfer is configuring the [`EVM2AnyMessage`](/ccip/api-reference/evm/v1.6.0/client#evm2anymessage) correctly. The script handles this for you by calling a `buildCCIPMessage` function. Here's what the configuration looks like within the script: + +```typescript +// Inside the transferTokenPayNative function for example: +const ccipMessage = buildCCIPMessage( + recipient, // Your Aptos wallet address from .env + "0x", // Data is empty for a token-only transfer + tokenAddress, // CCIP-BnM token address on Ethereum Sepolia + tokenAmount, // The amount of tokens to send + ethers.ZeroAddress, // For paying fees in native ETH + encodeExtraArgsV2(0n, true) // gasLimit is 0, allowOutOfOrderExecution is true +) +``` + + + + + +### How the Script Works + +The `evm2aptos/ccipSendTokenRouter.ts` script automates the entire cross-chain transfer process. Based on your command-line arguments, it performs the following steps: + +1. **Selects Chain Configuration**: Selects the correct RPC URL and contract addresses for the Ethereum Sepolia testnet based on the `--sourceChain sepolia` argument. +1. **Builds the CCIP Message**: Configures the message with the parameters shown above. +1. **Calculates Fees**: Calls the [`getFee`](/ccip/api-reference/evm/v1.6.0/i-router-client#getfee) function on the source chain's Router to determine the required CCIP fee. +1. **Approves Tokens**: + - Approves the Router to spend the CCIP-BnM tokens you are transferring. + - If paying with LINK, it also approves the Router to spend the required amount of LINK tokens for the fee. +1. **Executes Transfer**: Calls the [`ccipSend`](/ccip/api-reference/evm/v1.6.0/i-router-client#ccipsend) function on the CCIP Router contract to initiate the cross-chain transfer, sending native tokens for the fee if specified. +1. **Returns Message ID**: Parses the transaction receipt to find and display the `CCIPMessageSent` event and its unique `messageId`. + +## Running the Token Transfer + +### Prerequisites Check + +1. Ensure you've completed the setup steps outlined in the [prerequisites](/ccip/tutorials/aptos/destination/prerequisites). +1. Make sure your `.env` file contains your `PRIVATE_KEY` (of your wallet on Ethereum Sepolia from which you're sending CCIP-BnM tokens) and `ETHEREUM_SEPOLIA_RPC_URL`. +1. Verify you have sufficient native ETH and CCIP-BnM token balances in your EVM wallet on Ethereum Sepolia network. + +### Execute the Script + +Run the following command from your terminal to transfer CCIP-BnM tokens from Ethereum Sepolia to Aptos Testnet, paying the fee in native ETH. You can change the value for the `--amount` flag to send a different number of tokens. + +```bash +npx ts-node scripts/evm2aptos/ccipSendTokenRouter.ts --sourceChain sepolia --feeToken native --amount 0.001 --aptosReceiver <0x_YOUR_APTOS_WALLET_ADDRESS> +``` + +To pay with LINK instead, change the `feeToken` argument: + +```bash +npx ts-node scripts/evm2aptos/ccipSendTokenRouter.ts --sourceChain sepolia --feeToken link --amount 0.001 --aptosReceiver <0x_YOUR_APTOS_WALLET_ADDRESS> +``` + +### Understanding the Output + +When the script executes successfully, you'll see output similar to this, showing the steps for a transfer from Ethereum Sepolia: + +``` +Base Fee (in WEI): 80690622338859 +Fee with 20% buffer (in WEI): 96828746806630 +Current Allowance of CCIP-BnM token: 0 +Approval tx sent: 0x1a1fb6362f2eac4a6a52e4974e15fa071bc9084ad99273ef8aa5b37a9bc0568d +Approval transaction confirmed in block 8775988 after 3 confirmations. +Router contract approved to spend 1000000000000000 of CCIP-BnM token from your account. +Proceeding with the token transfer... +Transaction sent: 0x06d2657c1939f5cba4b29052977355a5cee131a6ffefe80550aeb80c383b9450 +Waiting for transaction confirmation... +Transaction confirmed in block 8775991 after 3 confirmations. +✅ Transaction successful: https://sepolia.etherscan.io/tx/0x06d2657c1939f5cba4b29052977355a5cee131a6ffefe80550aeb80c383b9450 +🆔 CCIP Message ID: 0x54d2e9fb4b7e852b30fae6617a568e729b714b5d658fa85592def65901b84c56 +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x54d2e9fb4b7e852b30fae6617a568e729b714b5d658fa85592def65901b84c56 +``` + +- The output displays the base fee and the fee with a 20% buffer, both shown in the smallest denomination of the fee token (i.e., WEI for the native token). +- It checks the current allowance of the CCIP-BnM token and approves the Router contract to spend the specified amount if necessary. +- The transaction hash is displayed, which you can use to track the transfer on Ethereum Sepolia. +- The output provides the unique `CCIP Message ID`, which is essential for tracking your transfer. +- The CCIP Explorer URL allows you to monitor the message status across chains. + +## Verification and Monitoring + +After sending your token transfer, you can verify its arrival on Aptos in the following ways: + +### Check Message Execution + +#### Use the CCIP Explorer to check the message status + +Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle. + +```text +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/ +``` + +#### Programmatically check the message status + +After you receive a CCIP Message ID, you can programmatically check if the CCIP message has been successfully executed on the Aptos network. This is done by querying the [`ExecutionStateChanged`](/ccip/api-reference/aptos/v1.6.0/events#execute_single_report) event emitted by the CCIP OffRamp module. The `evm2aptos/checkMsgExecutionStateOnAptos.ts` script is designed for this purpose. + +After 15-20 minutes, run the script using the CCIP Message ID you received from the previous step. + + + +**Command**: + +```bash +npx ts-node scripts/evm2aptos/checkMsgExecutionStateOnAptos.ts --msgId +``` + +_Replace `` with the actual CCIP Message ID from the log output._ + +**Output**: +When the message has been successfully delivered, you will see the following output: + +```text +Execution state for CCIP message 0x54d2e9fb4b7e852b30fae6617a568e729b714b5d658fa85592def65901b84c56 is SUCCESS +``` + +### Verify Token Balance + +Once the script confirms a `SUCCESS` state, you can perform a final verification on a block explorer. + +- Visit the [Aptos Explorer](https://explorer.aptoslabs.com/?network=testnet). +- Search for your Aptos wallet address. +- Under the "Coins" tab, you should see the balance of the CCIP-BnM token you transferred. + + diff --git a/src/content/ccip/tutorials/aptos/index.mdx b/src/content/ccip/tutorials/aptos/index.mdx new file mode 100644 index 00000000000..d0f01ba03b3 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/index.mdx @@ -0,0 +1,60 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Tutorials (Aptos)" +metadata: + description: "CCIP tutorials for Aptos. Build receivers, send tokens and messages between Aptos and EVM." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "Aptos, CCIP, receivers, tokens, messaging, Aptos FA, Fungible Asset, modules" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "8 minutes" + difficulty: "intermediate" +isIndex: true +--- + +import { Aside } from "@components" + +Chainlink CCIP enables secure cross-chain communication between Aptos and EVM blockchains. These tutorials will help you implement cross-chain functionality in both directions. + +## Getting Started + +Before diving into specific implementations, ensure you understand the fundamentals: + +- [Prerequisites for Aptos to EVM Tutorials](/ccip/tutorials/aptos/source/prerequisites) - Set up your development environment for Aptos -> EVM CCIP development. +- [Prerequisites for EVM to Aptos Tutorials](/ccip/tutorials/aptos/destination/prerequisites) - Set up your development environment for EVM -> Aptos CCIP development. + +## Using Aptos as a Source Chain + +Send messages and tokens from Aptos to EVM chains: + +- [Aptos to EVM Guide](/ccip/tutorials/aptos/source) - Comprehensive guide for building all types of CCIP messages from Aptos to EVM chains. +- [Token Transfers](/ccip/tutorials/aptos/source/token-transfers) - Transfer tokens from Aptos to EVM chains without executing code on the destination. + +## Using Aptos as a Destination Chain + +Send messages and tokens from EVM chains to Aptos: + +- [EVM to Aptos Guide](/ccip/tutorials/aptos/destination) - Comprehensive guide for building all types of CCIP messages from EVM chains to Aptos. +- [Token Transfers](/ccip/tutorials/aptos/destination/token-transfers) - Transfer tokens from EVM chains to Aptos wallets. + {/* - [Arbitrary Messaging](/ccip/tutorials/aptos/destination/arbitrary-messaging) - Send data from EVM chains to execute programs on Aptos */} + +## Architecture Reference + +Key Differences from EVM: + +- **Account Model:** Aptos uses an account-based model where code (modules) and data (resources) are stored directly within accounts. +- **Resource Model:** The Move language on Aptos uses a resource model that provides strong ownership and access control, ensuring resources cannot be duplicated or accidentally destroyed. +- **Explicit Access:** Modules define functions that can explicitly access and modify the resources stored within an account. +- **Fungible Assets:** Tokens are typically handled via the Fungible Asset standard, where assets are stored as resources within an owner's account. + +Message Types: + +- **Token Transfers:** Send tokens across chains without program execution. +- **Arbitrary Messaging:** Send data to trigger program execution on the destination chain. +- **Programmatic Token Transfers:** Send both tokens and data in a single message to trigger program execution with token transfers. + + diff --git a/src/content/ccip/tutorials/aptos/receivers.mdx b/src/content/ccip/tutorials/aptos/receivers.mdx new file mode 100644 index 00000000000..5aa87ecef4b --- /dev/null +++ b/src/content/ccip/tutorials/aptos/receivers.mdx @@ -0,0 +1,262 @@ +--- +section: ccip +date: Last Modified +title: "Implementing CCIP Receivers" +isIndex: false +metadata: + description: "Build secure Aptos CCIP receivers. Learn to implement ccip_receive, handle cross-chain messages/tokens, understand security patterns & module deployment." + image: "/images/ccip/concepts/architecture/ccip-aptos-destination-chain.jpg" + excerpt: "CCIP receivers, Aptos modules, Aptos resource account, security, accounts, patterns, Aptos FA, Fungible Asset, ERC-20, ERC20, token, CCIP" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "25 minutes" + difficulty: "advanced" +--- + +import { Aside } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + + + +# Implementing CCIP Receivers for Aptos + +This reference guide explains the key components and security patterns required for building Aptos Move modules that can receive cross-chain messages via Chainlink's Cross-Chain Interoperability Protocol (CCIP). + +## Introduction + +A CCIP Receiver is an Aptos Move module that contains a `ccip_receive` entry function and is registered with the CCIP `ReceiverRegistry`. This allows it to process incoming cross-chain messages, handling both arbitrary data payloads and/or token transfers, serving as the on-chain destination endpoint for CCIP messages. + +## Security Architecture + +To build a secure CCIP receiver, you need to understand how your module interacts with the core CCIP on-chain components. The security model relies on a chain of trust originating from the authorized CCIP Off-Ramp module. + + + Architectural Components + Security Model + + + + The on-chain architecture consists of: + + 1. **Receiver Module** (your custom Move module) + - Implements the `ccip_receive` entry function. + - Is registered with the `ReceiverRegistry`. + - Should validate that messages come from an authorized Off-Ramp. + + 2. **CCIP Off-Ramp Module** + - The only module authorized to deliver messages to your receiver. + - Calls your module via the secure `ReceiverDispatcher`. + + 3. **CCIP Receiver Registry & Dispatcher** + - The `ReceiverRegistry` maintains a list of valid receiver modules. + - The `ReceiverDispatcher` ensures only the Off-Ramp can trigger a `ccip_receive` call on a registered module. + + + + + + The security model is layered, combining guarantees provided by the CCIP protocol with validations you must implement. + + **Protocol Security Guarantees (Handled *before* your function is called):** + + 1. **Caller Validation**: The `Receiver Dispatcher` module cryptographically verifies that the caller is a legitimate and authorized CCIP Off-Ramp. This is the most critical check and prevents any unauthorized module from invoking your receiver. + + **Developer Responsibilities (Implemented *within* your `ccip_receive` function):** + + 1. **Source Chain Validation**: You should check the `source_chain_selector` from the message against an allowlist of blockchains you trust. + 2. **Sender Validation (Optional)**: If your use case requires it, you should check the `sender` address from the message against an allowlist of trusted source accounts. + + + + +## Core Components of a CCIP Receiver + +A complete CCIP Receiver implementation on Aptos contains several key components. + +### Message Structure + +Your module must be prepared to handle the `Any2AptosMessage` struct, which it fetches from the `ReceiverRegistry`. + +```rust +// From the ccip::client module +struct Any2AptosMessage has store, drop, copy { + message_id: vector, + source_chain_selector: u64, + sender: vector, + data: vector, + dest_token_amounts: vector +} + +struct Any2AptosTokenAmount has store, drop, copy { + token: address, + amount: u64 +} +``` + +These structures contain: + +- `message_id`: A unique identifier for the message +- `source_chain_selector`: The chain ID of the source chain +- `sender`: The address of the sender on the source chain +- `data`: The arbitrary data payload +- `dest_token_amounts`: An array of tokens and amounts being transferred + +### Module State (Resources) + +Your module will likely need to store state in on-chain resources. If your module will handle tokens, it's critical to store the `SignerCapability` from its resource account. You also need handles to emit custom events. + +```rust +// Example of a state resource from the ccip_message_receiver +struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, +} +``` + +### The `ccip_receive` Entry Function + +This is the core function that implements the CCIP receiver interface. It acts as a secure callback, triggered by the **Receiver Dispatcher** (which is itself called by the CCIP Off-Ramp), and should be designed as a dispatcher to handle different types of incoming messages. + +```rust +// A skeleton ccip_receive function +public entry fun ccip_receive( + _receiver_account: &signer, // The signer of the receiver module's account + _proof: ProofType, // A proof object required for the callback mechanism +) acquires State { + // 1. Fetch the message payload securely from the registry + let message = receiver_registry::get_receiver_input(signer::address_of(_receiver_account), _proof); + + // 2. Perform security checks (source chain, sender, etc.) + let source_chain = client::get_source_chain_selector(&message); + assert!(source_chain == 12345, E_UNAUTHORIZED_CHAIN); // Example check + + // 3. Process the message data + let received_data = client::get_data(&message); + if (!received_data.is_empty()) { + // Your custom data processing logic here + let state = borrow_global_mut(signer::address_of(_receiver_account)); + state.latest_message = received_data; + } + + // 4. Process token transfers (if applicable) + // Note: Tokens are automatically deposited into the receiver's primary store. + // This logic would be for forwarding or utilizing them. + let tokens = client::get_dest_token_amounts(&message); + if (!tokens.is_empty()) { + // Your custom token handling logic here + } + + // 5. Emit an event for tracking + event::emit(MessageReceived { + message_id: client::get_message_id(&message), + // ... + }); +} +``` + +### Administrative Functions + +A robust receiver module should include permissioned entry functions for maintenance and security, such as a function to withdraw tokens that may have been sent to it accidentally or for administrative purposes. + +```rust +// Example of a permissioned withdrawal function +public entry fun withdraw_token( + sender: &signer, + recipient: address, + token_address: address, +) acquires CCIPReceiverState { + // Only allow the original deployer to call this function + assert!(signer::address_of(sender) == @deployer, E_UNAUTHORIZED); + + let state = borrow_global_mut(@receiver); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + // ... logic to transfer the full balance of a token to the recipient + fungible_asset::transfer( + &state_signer, + // ... + ); +} +``` + +## Publishing Your Receiver Module + +How you publish your module is critical and depends on its purpose. + +- **For Data-Only Modules**: If your module only processes data and will never hold assets, you can publish it to a regular user account or code object account (the code object account is recommended). + +- **For Token-Handling Modules**: If your module will receive tokens, it **must** be deployed to a **Resource Account**. This gives the module an on-chain `signer` capability, which is required to authorize the transfer of tokens _out_ of its own account. The `createResourceAccountAndPublishReceiver.ts` script in the starter kit handles this process. + +--- + +## Security Considerations + +Building a secure CCIP Receiver on Aptos requires careful attention to several key validation patterns. + +### Caller Validation (Protocol Guarantee) + +The most critical security check—verifying that the call originates from a legitimate CCIP Off-Ramp—is a **protocol-level guarantee**. This check is performed by the `Receiver Dispatcher` module before your `ccip_receive` function is ever called. It asserts that the caller's signer address is on an allowlist of authorized Off-Ramps, preventing unauthorized modules from sending fake messages to your receiver. Your module's security is built on this foundational guarantee. + +### Source Chain and Sender Validation + +Your application is responsible for validating the content of the message payload. Inside your `ccip_receive` function, after fetching the `Any2AptosMessage`, you should implement checks against the `source_chain_selector` and `sender` fields if your use case requires it. + +```rust +// Inside your ccip_receive function: +let message = receiver_registry::get_receiver_input(...); + +// Get the source chain and sender from the message +let source_chain = client::get_source_chain_selector(&message); +let sender_bytes = client::get_sender(&message); + +// Verify against your module's allowlists +assert!(is_allowed_source_chain(source_chain), E_UNTRUSTED_SOURCE_CHAIN); +assert!(is_allowed_sender(sender_bytes), E_UNTRUSTED_SENDER); +``` + +### Message Deduplication + +To prevent replay attacks where the same message could be executed multiple times, your receiver should track processed message IDs. + +- **Mechanism**: Store recently processed `message_id`s in an on-chain resource (e.g., a `Table` or a `vector`). +- **Implementation**: In your `ccip_receive` function, check if the incoming `message_id` already exists in your store. If it does, abort the transaction. If it doesn't, process the message and then add its ID to the store. + +### Asset Management and Account Types + +A critical security consideration on Aptos is the account type used for your receiver module. + +- To manage (e.g., withdraw or forward) tokens received via CCIP, the receiver module **must** be deployed to a **Resource Account**. +- This provides the necessary on-chain `SignerCapability` for the module to authorize outgoing transactions for the assets it holds. +- Deploying a token-handling module to a standard user or object account will result in **permanently locked funds**, as the module will have no authority to sign for transfers. + +--- + +## Key Implementation Concepts + +### Secure Payload Fetching + +As shown in the example, your `ccip_receive` function does not get the message payload in its arguments. It **must** call `receiver_registry::get_receiver_input` to securely fetch the `Any2AptosMessage`. This security pattern ensures that only your registered module can access the payload during a valid CCIP execution. + +### Token Handling for Receivers + +When tokens are sent to your module, the CCIP Off-Ramp automatically deposits them into your module account's primary fungible store _before_ your `ccip_receive` function is called. To do anything with these tokens (like forward them to another user), your module must be able to sign for the transfer. This is why deploying to a **Resource Account** and using its `SignerCapability` is mandatory for any module that acts as a custodian of assets. + +### The Dispatcher Pattern + +The `ReceiverRegistry` maps one account address to one `ccip_receive` function. To handle multiple types of actions, use the **dispatcher pattern** within your `ccip_receive` function. By inspecting the `data` and `token_amounts` fields, you can route the logic to different internal functions, allowing a single, secure entry point to manage many functionalities. + +## Example Implementation + +For a complete reference implementation of a CCIP Receiver on Aptos, you can examine the `ccip_message_receiver` module within the **[Aptos Starter Kit](https://github.com/smartcontractkit/aptos-starter-kit)**. This example demonstrates many of the security patterns and best practices covered in this guide and serves as an excellent starting point for your own implementation. + + diff --git a/src/content/ccip/tutorials/aptos/source/build-messages.mdx b/src/content/ccip/tutorials/aptos/source/build-messages.mdx new file mode 100644 index 00000000000..ec92786b523 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/source/build-messages.mdx @@ -0,0 +1,389 @@ +--- +section: ccip +date: Last Modified +title: "Building CCIP Messages from Aptos to EVM" +isIndex: false +metadata: + description: "Comprehensive guide for implementing Cross-Chain Interoperability Protocol (CCIP) messages from Aptos to EVM chains. Learn how to structure messages, manage parameters, and implement token transfers, arbitrary data messaging, and programmatic token transfers." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "Aptos→EVM, message build, address conversion, extraArgs, CCIP, Aptos FA, Fungible Asset" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "30 minutes" + difficulty: "advanced" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +## Introduction + +This guide explains how to construct CCIP Messages from the Aptos blockchain to EVM chains (e.g., Ethereum, Arbitrum, Base, etc.). We'll cover the message structure by examining the [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send) entry function from the [`ccip_router::router` module](/ccip/api-reference/aptos/v1.6.0/router), its required parameters, and the implementation details for different message types including token transfers, arbitrary data messaging, and programmatic token transfers (data and tokens). + + + +## CCIP Message Structure on Aptos + +CCIP messages from Aptos are initiated by calling the [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send) entry function in the CCIP Router Move module. This function serves as the single entry point for all cross-chain messages, and internally routes the request to the correct On-Ramp contract version based on the destination chain. See the [CCIP Router API Reference](/ccip/api-reference/aptos/v1.6.0/router) for complete details. + +As defined in the `ccip_router::router` module, the `ccip_send` function has the following signature: + +```rust +public entry fun ccip_send( + caller: &signer, + dest_chain_selector: u64, + receiver: vector, + data: vector, + token_addresses: vector
, + token_amounts: vector, + token_store_addresses: vector
, + fee_token: address, + fee_token_store: address, + extra_args: vector +) +``` + +### receiver + +- **Definition**: The address of the contract or wallet on the destination EVM chain that will receive the message. +- **Formatting**: EVM addresses are 20 bytes long, but Aptos requires this parameter to be a 32-byte array. You must left-pad the 20-byte EVM address with 12 zero-bytes to create a valid 32-byte array. + + + +### data + +- **Definition**: The payload that will be executed by the receiving contract on the destination chain. +- **Usage**: + - **For token-only transfers**: This must be an empty `vector`. + - **For arbitrary messaging** or **programmatic token transfers**: This contains the function selectors and arguments for the receiver contract, typically ABI-encoded. +- **Encoding**: The receiver contract on the destination EVM chain must be able to decode this data. Standard EVM ABI-encoding is the recommended approach. + + + +### token_addresses + +A vector of Aptos token type addresses to be transferred. + +### token_amounts + +A vector of amounts to transfer for each corresponding token, in the token's smallest denomination. + +### token_store_addresses + +A vector of addresses representing the Fungible Asset store from which the tokens will be withdrawn. You could just use `0x0` as the token_store_address, because when using `0x0`, it would retrieve the `primary_store_address` (using `primary_fungible_store::primary_store_address`) of the token corresponding to the sender's account. + +For data-only messages, the `token_addresses`, `token_amounts`, and `token_store_addresses` vectors must all be empty. + +### fee_token + +The address of the token to be used for paying CCIP fees. This can be native APT (`0xa`) or a supported token like LINK. + +### fee_token_store + +The address of the Fungible Asset store from which the fee will be paid. You can use `0x0` here as well, which will resolve to the primary store for the fee token in the sender's account. + +### extra_args + +For messages going to an EVM chain, the `extra_args` parameter is a `vector` that must be encoded according to a specific format for compatibility. While there is no literal `GenericExtraArgsV2` struct in the Aptos modules, the byte vector must be encoded to match the format that EVM-family chains expect. + +This format consists of a 4-byte tag (`0x181dcf10`) followed by the BCS-encoded parameters: + +- **`gas_limit`**: (`u256`) The gas limit for the execution of the transaction on the destination EVM chain. +- **`allow_out_of_order_execution`**: (`bool`) A flag that must always be set to `true` for Aptos-to-EVM messages. + + + +The [`ccip::client`](/ccip/api-reference/aptos/v1.6.0/client) module provides a helper view function, [`encode_generic_extra_args_v2`](/ccip/api-reference/aptos/v1.6.0/client#encode_generic_extra_args_v2), to perform this encoding on-chain. Off-chain scripts replicate this logic to construct the byte vector correctly. + + + +## Estimating Fees + +Before sending a transaction, you must calculate the required fee. The [`ccip_router::router`](/ccip/api-reference/aptos/v1.6.0/router) module provides a [`get_fee`](/ccip/api-reference/aptos/v1.6.0/router#get_fee) view function for this purpose. It takes the exact same arguments as [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send), allowing you to get a precise fee quote for your intended message. + +```typescript +import { Aptos } from "@aptos-labs/ts-sdk" + +async function getCcipFee(aptos: Aptos, messagePayload: any) { + const fee = await aptos.view({ + payload: { + function: `${ccipRouterModuleAddr}::router::get_fee`, + functionArguments: messagePayload.functionArguments, // Reuse the same arguments as ccip_send + }, + }) + return fee[0] +} + +// You would call this before submitting your ccip_send transaction. +const feeAmount = await getCcipFee(aptosClient, transactionPayload) +console.log(`Required CCIP Fee: ${feeAmount}`) +``` + +## Implementation by Message Type + +### Token Transfer + +Use this configuration when sending only tokens from Aptos to an EVM chain: + + + +```typescript +import { MoveVector } from "@aptos-labs/ts-sdk" + +const transactionPayload = { + function: `${ccipRouterModuleAddr}::router::ccip_send`, + functionArguments: [ + destinationChainSelector, // e.g., "16015286601757825753" for Ethereum Sepolia + MoveVector.U8(evmAddressToAptos(receiverAddress)), // Padded 32-byte receiver + MoveVector.U8([]), // Empty data vector + [ccipBnmTokenAddress], // Vector of token addresses + MoveVector.U64([10000000n]), // Vector of token amounts + [tokenStoreAddress], // Vector of token store addresses + feeTokenAddress, // e.g., LINK or native APT token address + feeTokenStore, // Fee token store address + encodeGenericExtraArgsV2(0n, true), // extraArgs with gasLimit = 0 + ], +} +``` + +### Arbitrary Messaging + +Use this configuration when sending only data to EVM chains. + + + +```typescript +import { MoveVector, Hex } from "@aptos-labs/ts-sdk" +import { ethers } from "ethers" + +// ABI-encode the data for the receiver contract +const encodedData = ethers.AbiCoder.defaultAbiCoder().encode(["string"], ["Hello World"]) +const dataBytes = Hex.hexInputToUint8Array(encodedData) + +const transactionPayload = { + function: `${ccipRouterModuleAddr}::router::ccip_send`, + functionArguments: [ + destinationChainSelector, + MoveVector.U8(evmAddressToAptos(receiverContractAddress)), + MoveVector.U8(dataBytes), // The encoded data payload + [], // Empty token addresses vector + MoveVector.U64([]), // Empty token amounts vector + [], // Empty token store addresses vector + feeTokenAddress, + feeTokenStore, + encodeGenericExtraArgsV2(200000n, true), // extraArgs with appropriate gasLimit + ], +} +``` + +### Programmatic Token Transfer (Data and Tokens) + +Use this configuration when sending both tokens and data in a single message: + + + +```typescript +import { MoveVector, Hex } from "@aptos-labs/ts-sdk" +import { ethers } from "ethers" + +// ABI-encode the data for the receiver contract +const encodedData = ethers.AbiCoder.defaultAbiCoder().encode(["string"], ["Tokens attached"]) +const dataBytes = Hex.hexInputToUint8Array(encodedData) + +const transactionPayload = { + function: `${ccipRouterModuleAddr}::router::ccip_send`, + functionArguments: [ + destinationChainSelector, + MoveVector.U8(evmAddressToAptos(receiverContractAddress)), + MoveVector.U8(dataBytes), // The encoded data payload + [ccipBnmTokenAddress], // Vector of token addresses + MoveVector.U64([10000000n]), // Vector of token amounts + [tokenStoreAddress], // Vector of token store addresses + feeTokenAddress, + feeTokenStore, + encodeGenericExtraArgsV2(200000n, true), // extraArgs with appropriate gasLimit + ], +} +``` + +## Tracking Messages with Transaction Events + +After a successful `ccip_send` transaction, the CCIP Router module emits an event containing the unique identifier for the cross-chain message. On Aptos, this event is emitted by the On-Ramp module (e.g., [`CCIPMessageSent`](/ccip/api-reference/aptos/v1.6.0/events#ccip_send)) and can be found in the `Events` tab of the executed transaction on the [Aptos Explorer](https://explorer.aptoslabs.com/?network=testnet). + +```json +// Example of a CCIPMessageSent event from an Aptos transaction receipt +{ + "Type": "0xe9dbf...ac0dd3::onramp::CCIPMessageSent", + "Data": { + "dest_chain_selector": "16015286601757825753", + "message": { + "header": { + "message_id": "0x06859...2afefea", + "nonce": "0", + "sequence_number": "14", + "source_chain_selector": "743186221051783445" + }, + "receiver": "0x00000...bb14ca", + "sender": "0xd0e22...d2fad4", + "token_amounts": [ + { + "amount": "10000000", + "dest_token_address": "0x00000...fe82a05" + } + ], + "fee_token": "0x8c208...fa3542", + "data": "0x", + "extra_args": "0x181dc...00000..." + }, + "sequence_number": "14" + } +} +``` + +The `message_id` is the critical piece of information that links the source Aptos transaction to the destination EVM transaction. + +## Further Resources + +- [**CCIP Router API Reference**](/ccip/api-reference/aptos/v1.6.0/router): Complete technical details about the router's functions, parameters, and view functions like [`get_fee`](/ccip/api-reference/aptos/v1.6.0/router#get_fee). +- [**CCIP Messages API Reference**](/ccip/api-reference/aptos/v1.6.0/messages): Comprehensive documentation of all CCIP message and event structures for Aptos. +- **Aptos TS-SDK Docs**: For more information on building transactions and interacting with the Aptos blockchain, refer to the [official Aptos TS-SDK docs](https://aptos.dev/en/build/sdks/ts-sdk). + + diff --git a/src/content/ccip/tutorials/aptos/source/index.mdx b/src/content/ccip/tutorials/aptos/source/index.mdx new file mode 100644 index 00000000000..cb32a50b75f --- /dev/null +++ b/src/content/ccip/tutorials/aptos/source/index.mdx @@ -0,0 +1,31 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Tutorials: Aptos to EVM" +isIndex: true +metadata: + description: "Learn how to implement cross-chain communication from Aptos to Ethereum using Chainlink CCIP. Tutorials cover environment setup, token transfers, and message building with detailed implementation guides." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "Aptos→EVM, CCIP, messages, tokens, setup, concepts, Aptos FA, Fungible Asset, ERC20, token" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "5 minutes" + difficulty: "intermediate" +--- + +import { Aside } from "@components" + +This section provides comprehensive guides and tutorials for implementing cross-chain communication from Aptos chains to Ethereum Virtual Machine (EVM) chains using Chainlink's Cross-Chain Interoperability Protocol (CCIP). + +## Getting Started + +Before implementing specific use cases, it's important to set up your environment and understand the fundamental concepts: + +- [Building CCIP Messages from Aptos to EVM](/ccip/tutorials/aptos/source/build-messages) - Learn the core message structure, required parameters, and implementation details for all message types. +- [Prerequisites for Aptos to EVM Tutorials](/ccip/tutorials/aptos/source/prerequisites) - Set up your development environment with Aptos CLI tools, wallets, and token accounts. + +## Tutorials by Use Case + +Depending on your specific needs, choose the appropriate tutorial: + +- [Token Transfers](/ccip/tutorials/aptos/source/token-transfers) - Send tokens from Aptos to EVM chains without executing code on the destination. diff --git a/src/content/ccip/tutorials/aptos/source/prerequisites.mdx b/src/content/ccip/tutorials/aptos/source/prerequisites.mdx new file mode 100644 index 00000000000..d479f74fa83 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/source/prerequisites.mdx @@ -0,0 +1,223 @@ +--- +section: ccip +date: Last Modified +title: "Prerequisites for Aptos to EVM Tutorials" +isIndex: false +metadata: + description: "Complete setup guide for Aptos to EVM cross-chain development. Install Aptos CLI, Node.js, configure wallets, and get testnet tokens for CCIP Chainlink tutorials. Step-by-step instructions for developers building cross-chain applications." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "prerequisites, Aptos, CLI, wallets, tokens, setup" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "15 minutes" + difficulty: "beginner" +--- + +import { Aside } from "@components" + +Before starting the Aptos to EVM tutorials, ensure you have: + +## Development Environment + +- **Aptos CLI**: Install the Aptos CLI by following the official [installation guide](https://aptos.dev/en/build/cli). + + + +- **Node.js v20 or higher**: You can use the [nvm package](http://nvm.sh/) to install and switch between Node.js versions. Once installed, verify the node version with: + + ```bash + node -v + ``` + + Example output: + + ```text + $ node -v + v22.15.0 + ``` + +- **Npm**: For installing and managing dependencies. +- **Git**: For cloning the repository. + +## Starter Kit Repository + +1. Clone the CCIP Aptos Starter Kit: + + ```bash + git clone https://github.com/smartcontractkit/aptos-starter-kit.git + ``` + +1. Navigate to the directory: + + ```bash + cd aptos-starter-kit + ``` + +1. Install dependencies: + + ```bash + npm install + ``` + +## Understanding Named Addresses in the Manifest File (`Move.toml`) + +The Move build system uses a manifest file, `Move.toml`, to define a package, its dependencies, and any named addresses it references. Named addresses are human-readable aliases for on-chain hexadecimal addresses, which makes the Move code cleaner, more readable, and easier to manage. + +When you compile a Move package, the compiler replaces these aliases (e.g., `@ccip_router`) with their actual address values from the `Move.toml` file. + +In the [`aptos-starter-kit`](https://github.com/smartcontractkit/aptos-starter-kit), the example modules (like `ccip_message_sender`, `ccip_message_receiver`) need to interact with the core CCIP modules already deployed on the Aptos Testnet. The `[addresses]` section in their `Move.toml` files provides the necessary references. + +**Example `Move.toml` Address Block:** + +```toml +[addresses] +ccip = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +mcms = "0xbdf1b9aacb4e21bf6f255105831df0172e911d4748e488196fde10d2e2a4e32d" +mcms_register_entrypoints = "0x0" +ccip_onramp = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +ccip_router = "0xc748085bd02022a9696dfa2058774f92a07401208bbd34cfd0c6d0ac0287ee45" +``` + +Here is a breakdown of what each named address represents: + +- **`ccip`**: This is the main **Object address** under which the core CCIP packages and their modules (like `fee_quoter`, `rmn_remote`, `token_admin_registry`, etc.) are published. + +- **`ccip_router`** & **`ccip_onramp`**: These aliases also point to the main CCIP Object address. This is because the `router` and `onramp` modules are part of the packages published under that single, unified Object. The aliases are defined for clarity and consistency in the Move code's `use` statements. + +- **`mcms`**: This is the address of the on-chain **ManyChainMultiSig (MCMS)** module, which is responsible for handling governance and administrative actions for the CCIP protocol through a secure, time-locked [implementation process](/ccip/concepts/architecture/onchain/aptos/upgradability#implementation-process). + +- **`mcms_register_entrypoints`**: This is a special value used as a **compile-time flag**. By setting it to `0x0`, the module is compiled in a way that bypasses the logic for registering with the MCMS system. This is useful for certain testing or deployment scenarios where the MCMS module is not relevant. + +By defining these addresses in the `Move.toml` file, the example modules can interact with the core CCIP protocol without hardcoding long addresses, making the code portable and easier to update for different networks. + + + +## Wallets + +- **Aptos Account**: You'll need an Aptos account. If you don't have one, create it with: + + ```bash + aptos init --network testnet + ``` + + This command will guide you through creating a new account for Testnet and will save the credentials in a `.aptos/config.yaml` file. This also configures your Aptos CLI to use Testnet. + + {/* prettier-ignore */} + + + Verify your current configuration with: + + ```bash + aptos config show-profiles + ``` + + This should show your `default` profile configured for Testnet, with the `network` set to `Testnet`. + + Example output: + + ```text + $ aptos config show-profiles + + { + "Result": { + "default": { + "network": "Testnet", + "has_private_key": true, + "public_key": "ed25519-pub-0x2ecdd2d7bc0cbfe2e44c219ef9a9fddc986b384f4a01fb5d821cf0dab5d2fbae", + "account": "d0e227835c33932721d54ae401cfaae753c295024fe454aa029b5e2782d2fad4", + "rest_url": "https://fullnode.testnet.aptoslabs.com" + } + } + } + ``` + +- **EVM Wallet Address**: You'll need an EVM-compatible wallet address to receive tokens on the destination chain (Ethereum Sepolia). You only need the address itself, not the private key, as you are only sending _to_ this address. + +## Environment Configuration (`.env` file) + +The starter kit uses a `.env` file to manage sensitive information like private keys and RPC URLs. Create a new file named `.env` in the root of the `aptos-starter-kit` directory by copying the example file: + +```bash +cp .env.example .env +``` + +Next, open the `.env` file and fill in the following values: + +- `PRIVATE_KEY_HEX`: The private key of your source wallet (EOA) on Aptos Testnet from which you're sending CCIP-BnM tokens. You can find this in the `.aptos/config.yaml` file created by the `aptos init --network testnet` command, as shown in the [Wallets](/ccip/tutorials/aptos/source/prerequisites#wallets) section above. +- `ETHEREUM_SEPOLIA_RPC_URL`: The RPC endpoint for the Ethereum Sepolia testnet. This is required by the verification script (`aptos2evm/checkMsgExecutionStateOnAptos.ts`) to read the `ExecutionStateChanged` event from the CCIP OffRamp on Ethereum Sepolia. You can obtain an RPC URL by signing up for a personal endpoint from [Alchemy](https://www.alchemy.com/), [Infura](https://www.infura.io/), or another node provider service. + +**Example `.env` file:** + +``` +PRIVATE_KEY_HEX= +ETHEREUM_SEPOLIA_RPC_URL= +``` + +## Native Tokens for Transaction Fees + +**APT** tokens are used for Aptos transaction fees. For these tutorials, we will also use **APT** to pay for CCIP fees. + +- Obtain **APT** on Testnet using the faucet: + + You can use the official [Aptos Testnet Faucet](https://aptos.dev/en/network/faucet) to get **APT** tokens. Simply enter your Aptos account address and click on **Mint** to request tokens. + +- Check your account balance: + + ```bash + aptos account balance + ``` + + Example output: + + ```text + $ aptos account balance + + { + "Result": [ + { + "asset_type": "coin", + "coin_type": "0x1::aptos_coin::AptosCoin", + "balance": 100000000 + } + ] + } + ``` + +## Obtaining Testnet Tokens + +### BnM Tokens on Aptos + +To complete the cross-chain token transfer examples, you'll need BnM tokens. To obtain CCIP-BnM tokens on Aptos Testnet, run the `faucets/aptos/dripCCIPBnMToken.ts` script included in the starter kit. + +Run the following command in your terminal: + +```bash +npx ts-node scripts/faucets/aptos/dripCCIPBnMToken.ts --to +``` + + + +The output looks like this: + +```text +1 CCIP-BnM token is minted to 0xd0e227835c33932721d54ae401cfaae753c295024fe454aa029b5e2782d2fad4 successfully. +Please check the transaction at https://explorer.aptoslabs.com/txn/0x7e5d5bae918c97cb086c72023012da2a8a230c91155f8ff697eb29001f606e55?network=testnet +``` diff --git a/src/content/ccip/tutorials/aptos/source/token-transfers.mdx b/src/content/ccip/tutorials/aptos/source/token-transfers.mdx new file mode 100644 index 00000000000..35d2a901ad0 --- /dev/null +++ b/src/content/ccip/tutorials/aptos/source/token-transfers.mdx @@ -0,0 +1,227 @@ +--- +section: ccip +date: Last Modified +title: "Token Transfers: Aptos to EVM" +isIndex: false +metadata: + description: "Learn how to transfer tokens from Aptos to EVM using Chainlink's Cross-Chain Interoperability Protocol (CCIP). This step-by-step tutorial guides you through setting up and executing a basic token transfer from a Aptos wallet to an Ethereum address." + image: "/images/ccip/concepts/architecture/ccip-aptos-source-chain.jpg" + excerpt: "Aptos FA, Fungible Asset, ERC-20, ERC20, token transfer, Aptos→EVM, APT fees, CCIP, verification" + datePublished: "2025-09-03" + lastModified: "2025-09-03" + estimatedTime: "20 minutes" + difficulty: "intermediate" +--- + +import { Aside, ClickToZoom } from "@components" +import { Tabs } from "@components/Tabs" +import CcipCommon from "@features/ccip/CcipCommon.astro" + +This tutorial demonstrates how to transfer tokens from the Aptos blockchain to an Ethereum Virtual Machine (EVM) chain using Chainlink CCIP. You will learn how to build a CCIP message on Aptos, send it using a script, and verify the transfer on the destination chain. + + + +## Introduction + +This tutorial covers transferring tokens from Aptos Testnet to Ethereum Sepolia without any additional data payload or program execution. When you transfer tokens using CCIP: + +1. Tokens are burned or locked in pools on the source chain (Aptos). +1. Equivalent tokens are minted or released from the destination pool (Ethereum Sepolia). +1. The process is managed by CCIP. + +## What You Will Build + +In this tutorial, you will: + +- Use a script to configure a CCIP message for a token-only transfer. +- Send CCIP-BnM test tokens from Aptos Testnet to an Ethereum Sepolia address. +- Pay for CCIP transaction fees using either native APT or LINK tokens. +- Monitor and verify your cross-chain transfer. + + + +## Understanding Token Transfers from Aptos to EVM + +This tutorial focuses on token-only transfers from your wallet on Aptos Testnet to an address on Ethereum Sepolia. Key points specific to token-only transfers: + +- **Burn and Mint**: In this tutorial, CCIP-BnM tokens are burned on Aptos Testnet, and equivalent CCIP-BnM tokens are minted on Ethereum Sepolia. +- **Fee Payment**: Transaction fees are paid on Aptos using either native APT or LINK tokens. +- **Message Construction**: A script constructs and sends the transaction payload by calling the [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send) entry function on the Aptos CCIP Router module. + +## CCIP Message Configuration + +The most important part of implementing a token transfer is correctly configuring the arguments for the [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send) function. The `scripts/aptos2evm/ccipSendTokenRouter.ts` script handles this. Here's a look at how the arguments are prepared within the script: + +```typescript +// Inside the script, the transaction payload is constructed like this: +const transaction = await aptos.transaction.build.simple({ + sender: account.accountAddress, + data: { + function: `${ccipRouterModuleAddr}::router::ccip_send`, // CCIP Router address and function + functionArguments: [ + destChainSelector, // Chain selector for Ethereum Sepolia + MoveVector.U8(Hex.hexInputToUint8Array(paddedReceiverArray)), // Your EVM receiver address + MoveVector.U8([]), // Empty data for token-only transfer + [ccipBnMTokenAddr], // The BnM token address on Aptos + MoveVector.U64([TOKEN_AMOUNT_TO_SEND]), // The amount of BnM tokens + [TOKEN_STORE_ADDR], // The token store, e.g., '0x0' for the primary store + feeToken, // The fee token address (APT or LINK) + feeTokenStore, // The fee token store address + extraArgs, // Encoded extra arguments with gas limit + ], + }, +}) +``` + +### Critical Configuration Settings + +When setting up your CCIP message for a token transfer, these parameters are crucial: + +#### Required Parameters + +- **`data`**: **MUST** be an empty vector (`[]`) for token-only transfers. +- **`extraArgs`**: Must be properly encoded based on your destination chain type. + +#### EVM Destination Configuration + +For EVM destinations (covered in this tutorial): + +- Set `gasLimit` to `0` +- Set `allowOutOfOrderExecution` to `true` +- The script handles encoding by calling `encodeGenericExtraArgsV2(0, true)` + +#### SVM Destination Configuration + +The `ccipSendTokenRouter.ts` script in this tutorial is designed specifically for EVM destinations and **will not work** for SVM destinations without modifications. + +For SVM destinations such as Solana, you will need to adapt the script's `extraArgs` encoding logic. + +Refer to the following resources for more information: + +- [Building CCIP messages from EVM to SVM](/ccip/tutorials/svm/destination/build-messages) +- [EVM to SVM token transfers tutorial](/ccip/tutorials/svm/destination/token-transfers) + +## How the Script Works + +The `aptos2evm/ccipSendTokenRouter.ts` script handles the interaction with the CCIP Router module on your behalf. Here's what happens behind the scenes: + +1. **Context Initialization**: The script initializes the Aptos client and loads your private key from the `.env` file. +1. **Argument Parsing**: It reads your command-line arguments (`--destChain`, `--feeToken`, `--amount`, `--evmReceiver`) to determine the destination, fee payment method, amount to send, and EVM receiver address. +1. **Parameter Preparation**: It formats the EVM receiver address and token amount correctly for the Aptos blockchain. +1. **Transaction Building**: It constructs the [`ccip_send`](/ccip/api-reference/aptos/v1.6.0/router#ccip_send) transaction with all necessary arguments, including the destination selector, receiver, token details, and encoded extra args. +1. **Simulation and Execution**: + - It simulates the transaction to prevent failures. + - It signs and sends the transaction to the Aptos Testnet. + - It waits for the transaction to be confirmed and prints the transaction hash. + +## Running the Token Transfer + +### Prerequisites Check + +Before running the script: + +1. Ensure you've completed the setup steps outlined in the [prerequisites](/ccip/tutorials/aptos/source/prerequisites). +1. Make sure your `.env` file contains your `PRIVATE_KEY_HEX` (of your wallet on Aptos Testnet from which you're sending CCIP-BnM tokens) and `ETHEREUM_SEPOLIA_RPC_URL`. + + + +1. Verify you have sufficient APT and CCIP-BnM token balances in your Aptos wallet. + +### Execute the Script + +Run the following command from your terminal to transfer CCIP-BnM tokens from Aptos Testnet to Ethereum Sepolia, paying the fee in native APT. You can change the value for the `--amount` flag to send a different number of tokens. + +```bash +npx ts-node scripts/aptos2evm/ccipSendTokenRouter.ts --destChain sepolia --feeToken native --amount 0.001 --evmReceiver +``` + +To pay with LINK instead, change the `feeToken` argument: + +```bash +npx ts-node scripts/aptos2evm/ccipSendTokenRouter.ts --destChain sepolia --feeToken link --amount 0.001 --evmReceiver +``` + +### Understanding the Output + +When the script executes successfully, you'll see the logs similar to the following: + +``` +✅ Transaction successful: https://explorer.aptoslabs.com/txn/0xa5ca115b1f13bb7ace83218f81b60510df29074bfee1f64ce17b00b5737391bc?network=testnet +🆔 CCIP Message ID: 0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf +``` + +- The transaction hash is displayed along with the Aptos Explorer URL. +- The output provides the unique `CCIP Message ID`, which is essential for tracking your transfer. +- The CCIP Explorer URL allows you to monitor the message status across chains. + +## Verification and Monitoring + +After sending your token transfer, you can verify its arrival on Ethereum Sepolia in the following ways: + +### Check Message Execution + +#### Use the CCIP Explorer to check the message status + +Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle. + +```text +🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/ +``` + +#### Programmatically check the message status + +After you receive a CCIP Message ID, you can use the `aptos2evm/checkMsgExecutionStateOnEvm.ts` script to see if the message was successfully delivered. This script first fetches the [`CCIPMessageSent`](/ccip/api-reference/aptos/v1.6.0/events#ccip_send) event on Aptos to get the unique Message ID, then polls the destination chain (Ethereum Sepolia) for the corresponding `ExecutionStateChanged` event. + +After 1-2 minutes, run the script using the CCIP Message ID you received from the previous step. + + + +**Command**: + +```bash +npx ts-node scripts/aptos2evm/checkMsgExecutionStateOnEvm.ts --msgId --destChain sepolia +``` + +_Replace `` with the actual CCIP Message ID from the log output._ + +**Output**: +When the message has been successfully delivered, you will see the following output: + +```text +Execution state for CCIP message 0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf is SUCCESS +``` + +### Verify Token Balance + +Once the script confirms a `SUCCESS` state, you can perform a final verification on a block explorer. + +- Visit the [Sepolia Etherscan Explorer](https://sepolia.etherscan.io/). +- Search for your EVM wallet address. +- Under the "ERC20 Token Txns" tab, you should see the incoming transfer of CCIP-BnM tokens. + + diff --git a/src/content/ccip/tutorials/index.mdx b/src/content/ccip/tutorials/index.mdx index 2e9bc60c17b..f6d3b04709b 100644 --- a/src/content/ccip/tutorials/index.mdx +++ b/src/content/ccip/tutorials/index.mdx @@ -21,3 +21,4 @@ Choose the tutorial section based on your blockchain platform: - [EVM Tutorials](/ccip/tutorials/evm) - Tutorials for Ethereum Virtual Machine compatible chains - [SVM Tutorials](/ccip/tutorials/svm) - Tutorials for Solana Virtual Machine chains +- [Aptos Tutorials](/ccip/tutorials/aptos) - Tutorials for Aptos chain diff --git a/src/features/utils/index.ts b/src/features/utils/index.ts index 8b7156fa647..40acd970b46 100644 --- a/src/features/utils/index.ts +++ b/src/features/utils/index.ts @@ -62,16 +62,20 @@ export const getNativeCurrency = (supportedChain: SupportedChain) => { return chains[technology]?.chains[supportedChain]?.nativeCurrency } -export const getExplorerAddressUrl = (explorer: ExplorerInfo) => (contractAddress: string) => { - const url = `${explorer.baseUrl}/address/${contractAddress}` - if (!explorer.queryParameters) return url +export const getExplorerAddressUrl = + (explorer: ExplorerInfo, chainType: ChainType = "evm") => + (contractAddress: string) => { + // Use appropriate path segment based on chain type + const pathSegment = chainType === "aptos" ? "object" : "address" + const url = `${explorer.baseUrl}/${pathSegment}/${contractAddress}` + if (!explorer.queryParameters) return url - const queryString = Object.entries(explorer.queryParameters) - .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) - .join("&") + const queryString = Object.entries(explorer.queryParameters) + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + .join("&") - return queryString ? `${url}?${queryString}` : url -} + return queryString ? `${url}?${queryString}` : url + } export const getTitle = (supportedChain: SupportedChain) => { const technology = chainToTechnology[supportedChain] @@ -106,6 +110,9 @@ export const getChainTypeAndFamily = (supportedChain: SupportedChain): ChainType case "solana": chainFamily = "svm" break + case "sui": + chainFamily = "svm" + break default: throw new Error(`Unknown chain type: ${chainType}`) } @@ -456,6 +463,10 @@ export const directoryToSupportedChain = (chainInRdd: string): SupportedChain => return "KATANA_TATARA" case "bitcoin-mainnet-botanix": return "BOTANIX_MAINNET" + case "aptos-mainnet": + return "APTOS_MAINNET" + case "aptos-testnet": + return "APTOS_TESTNET" default: throw Error(`Chain not found ${chainInRdd}`) } @@ -731,6 +742,10 @@ export const supportedChainToChainInRdd = (supportedChain: SupportedChain): stri return "polygon-testnet-tatara" case "BOTANIX_MAINNET": return "bitcoin-mainnet-botanix" + case "APTOS_MAINNET": + return "aptos-mainnet" + case "APTOS_TESTNET": + return "aptos-testnet" default: throw Error(`Chain not found ${supportedChain}`) } diff --git a/src/pages/[product]/api-reference/[...id].astro b/src/pages/[product]/api-reference/[...id].astro index 60045cca4a8..1264313d646 100644 --- a/src/pages/[product]/api-reference/[...id].astro +++ b/src/pages/[product]/api-reference/[...id].astro @@ -12,11 +12,11 @@ interface RouteParams { export async function getStaticPaths(): Promise { // Helper to check if a version config has VM types (defined inside getStaticPaths) function hasVmTypes(config: any): config is VMVersionConfig { - return config && typeof config === "object" && "evm" in config && "svm" in config + return config && typeof config === "object" && "evm" in config && "svm" in config && "aptos" in config } const paths: RouteParams[] = [] - const vmTypes = ["evm", "svm"] // VM type list + const vmTypes = ["evm", "svm", "aptos"] // VM type list // Handle standard API reference path structure function processStandardApiPaths( diff --git a/src/pages/api/ccip/types/index.ts b/src/pages/api/ccip/types/index.ts index aa7f12031ca..5db83172172 100644 --- a/src/pages/api/ccip/types/index.ts +++ b/src/pages/api/ccip/types/index.ts @@ -5,7 +5,7 @@ import { Environment } from "@config/data/ccip/types.ts" export { Environment } // Chain type and family declarations -export type ChainType = "evm" | "solana" | "aptos" +export type ChainType = "evm" | "solana" | "aptos" | "sui" export type ChainFamily = "evm" | "mvm" | "svm" export const prerender = false