Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Example 08: CCT Burn and Mint With AdvancedPoolHooks

This example covers BurnMint CCT on Sepolia → Amoy with AdvancedPoolHooks attached to the pool.

Token Pool Hooks Development

Token pool hooks are standalone contracts used by token pools for additional checks and behavior.

At a high level:

  • pool calls hook preflightCheck(...) on source-side lock/burn
  • pool calls hook postflightCheck(...) on destination-side release/mint
  • hook can enforce allowlists and CCV requirements
  • hook can optionally call a policy engine

For custom development, the pool-level integration point is IAdvancedPoolHooks and the pool constructor receives the hook address.

AdvancedPoolHooks includes optional policy engine integration through IPolicyEngine. In this example, policy engine is intentionally disabled (address(0)) to focus on hook attachment and allowlist flow.

When building policy-engine-driven flows, Chainlink ACE provides the policy engine interfaces and implementation model.

This flow still demonstrates the correct hook wiring pattern for ACE-enabled setups.

What This Example Covers

  1. Deploy CrossChainToken + AdvancedPoolHooks + BurnMint pool on both chains (Hardhat Ignition).
  2. Configure pools to trust each other (example08-step1).
  3. Send token transfer with ExtraArgsV3 default-finality sender (example08-step2).
  4. (Optional) Update hook allowlist (example08-step3).
  5. Verify hook attachment and allowlist with helper tasks.

Modules and tasks used:

  • Deploy token + hook + pool: Hardhat Ignition module ignition/modules/BurnMintTokenPoolAdvancedPoolHook.ts (deploy on each chain with the appropriate parameters file).
  • Configure remote lane: Hardhat task example08-step1.
  • Send CCIP token transfer: Hardhat task example08-step2.
  • Update allowlist: Hardhat task example08-step3.
  • Verify: Helper tasks token-admin-registry-get-pool, get-allow-list.

Before You Start

Important: Keystore first

Use a local keystore account for task execution:

npx hardhat keystore set AMOY_PRIVATE_KEY
npx hardhat keystore set SEPOLIA_PRIVATE_KEY
npx hardhat keystore set AMOY_RPC_URL
npx hardhat keystore set SEPOLIA_RPC_URL

Ensure ignition/paramsEthSepolia.json and ignition/paramsAmoy.json contain the correct routerAddress, armProxy, registryModuleOwnerCustom, tokenAdminRegistry, and thresholdAmountForAdditionalCCVs for each chain.

Deployment Defaults

The BurnMintTokenPoolAdvancedPoolHook Ignition module deploys:

  • Token: CrossChainToken (same defaults and registerAdminViaGetCCIPAdmin flow as Example 06): TestToken (TEST), 18 decimals, 1_000_000 × 10¹⁸ premint, 100_000_000 × 10¹⁸ max supply.
  • Hook: AdvancedPoolHooks with allowlist seeded with deployer EOA, thresholdAmountForAdditionalCCVs from params, policy engine disabled, pool authorized as caller.
  • Pool: BurnMintTokenPool with the hook attached.

Step 1: Deploy Token + Hook + Pool on Sepolia

npx hardhat ignition deploy ignition/modules/BurnMintTokenPoolAdvancedPoolHook.ts --network <NETWORK_NAME> --parameters <PARAMETERS_FILE>

--parameters is the path to the chain’s Ignition parameters JSON (e.g. ignition/paramsEthSepolia.json or ignition/paramsAmoy.json); it supplies router, arm proxy, token admin registry, thresholdAmountForAdditionalCCVs, and other addresses to the module.

Save from the deployment output:

  • <SEPOLIA_TOKEN_ADDRESS>
  • <SEPOLIA_ADVANCED_POOL_HOOK_ADDRESS>
  • <SEPOLIA_POOL_ADDRESS>

Step 2: Deploy Token + Hook + Pool on Amoy

npx hardhat ignition deploy ignition/modules/BurnMintTokenPoolAdvancedPoolHook.ts --network <NETWORK_NAME> --parameters <PARAMETERS_FILE>

Use the parameters file for the chain you are deploying to.

Save from the deployment output:

  • <AMOY_TOKEN_ADDRESS>
  • <AMOY_ADVANCED_POOL_HOOK_ADDRESS>
  • <AMOY_POOL_ADDRESS>

Step 3: Configure Sepolia Pool With Amoy Remote

npx hardhat example08-step1 --network <NETWORK_NAME> \
  --local-pool <SEPOLIA_POOL_ADDRESS> \
  --remote-chain-selector <AMOY_CHAIN_SELECTOR> \
  --remote-token <AMOY_TOKEN_ADDRESS> \
  --remote-pool <AMOY_POOL_ADDRESS>

Step 4: Configure Amoy Pool With Sepolia Remote

npx hardhat example08-step1 --network <NETWORK_NAME> \
  --local-pool <AMOY_POOL_ADDRESS> \
  --remote-chain-selector <SEPOLIA_CHAIN_SELECTOR> \
  --remote-token <SEPOLIA_TOKEN_ADDRESS> \
  --remote-pool <SEPOLIA_POOL_ADDRESS>

Step 5: Send Transfer (ExtraArgsV3 + Default Finality)

npx hardhat example08-step2 --network <NETWORK_NAME> \
  --source-router <SEPOLIA_ROUTER> \
  --destination-chain-selector <DESTINATION_CHAIN_SELECTOR> \
  --receiver <RECEIVER_ON_AMOY> \
  --token-to-send <SEPOLIA_TOKEN_ADDRESS> \
  --amount <AMOUNT> \
  --gas-limit 0 \
  --block-confirmations 0 \
  --fee-token-address <FEE_TOKEN_ADDRESS>

Optional: Update Hook Allowlist

To add or remove senders in the allowlist after deployment, use the example08-step3 task.

Add one sender address:

npx hardhat example08-step3 --network <NETWORK_NAME> \
  --advanced-pool-hook <ADVANCED_POOL_HOOK_ADDRESS> \
  --removes "" \
  --adds "<ADDRESS_TO_ADD>"

Remove one sender address:

npx hardhat example08-step3 --network <NETWORK_NAME> \
  --advanced-pool-hook <ADVANCED_POOL_HOOK_ADDRESS> \
  --removes "<ADDRESS_TO_REMOVE>" \
  --adds ""

To add or remove multiple addresses, use comma-separated values for --adds and --removes.

Step 6: Verify Hook and Pool State

Verify token→pool registration:

npx hardhat token-admin-registry-get-pool --network <NETWORK_NAME> --registry <TOKEN_ADMIN_REGISTRY> --token <TOKEN_ADDRESS>

Verify allowlist (addresses in the hook):

npx hardhat get-allow-list --network <NETWORK_NAME> --advanced-pool-hook <ADVANCED_POOL_HOOK_ADDRESS>

To verify the attached hook address on each pool or the allowlist-enabled flag, use your RPC and a contract read (e.g. getAdvancedPoolHooks(), getAllowListEnabled()).

Monitor message status with the message ID on:

  • https://ccip.chain.link