Transfer TNZO Cross-Chain via CCT
Chainlink CCT (Cross-Chain Token) v1.6+ is a pool-based standard for moving the same token across multiple chains over CCIP. TNZO is deployed as a CCT across Ethereum, Base, Arbitrum, Optimism, and Solana. This tutorial covers the pool registry, CCIP fee quoting, and actually submitting a transfer.
What You'll Build
- Inspect the TNZO CCT pool registry
- Quote a CCIP fee from Ethereum to Base
- Send 100 TNZO cross-chain with
ccip_send_message - Track the message via
ccip_track_message
Pool Model
LockRelease (canonical chain)
outbound: ERC-20 transferFrom(user, pool) → pool holds tokens
inbound: pool transfer(to, amount) → pool releases tokens
BurnMint (remote chains)
outbound: pool.burn(from, amount) → supply decreases
inbound: pool.mint(to, amount) → supply increases
Invariant: sum(locked on canonical) == sum(minted on all remotes)Step 1: List the Pools
tenzro_cctListPools()
-> [
{ chain: "ethereum", pool_type: "LockRelease", pool_address: "0x...", token_address: "0x...", ccip_router: "0x...", rmn_proxy: "0x..." },
{ chain: "base", pool_type: "BurnMint", pool_address: "0x...", token_address: "0x...", ccip_router: "0x...", rmn_proxy: "0x..." },
{ chain: "arbitrum", pool_type: "BurnMint", pool_address: "0x...", token_address: "0x...", ccip_router: "0x...", rmn_proxy: "0x..." },
{ chain: "optimism", pool_type: "BurnMint", pool_address: "0x...", token_address: "0x...", ccip_router: "0x...", rmn_proxy: "0x..." },
{ chain: "solana", pool_type: "BurnMint", pool_address: "...", token_address: "...", ccip_router: "...", rmn_proxy: "..." }
]Step 2: Look Up a Specific Pool
tenzro_cctGetPool({ "chain": "base" })
-> {
chain: "base",
pool_type: "BurnMint",
pool_address: "0x...",
token_address: "0x...",
ccip_router: "0x...",
rmn_proxy: "0x..."
}Step 3: Quote the CCIP Fee
Fees are denominated in LINK or native gas. The quote is a real Router.getFee() eth_call under the hood:
ccip_get_fee({
"source_chain": "ethereum",
"dest_chain": "base",
"token": "TNZO",
"amount": "100.0"
})
-> { fee: "0.02 LINK", fee_token: "LINK", source_router: "0x..." }Step 4: Send the Message
CCIP encodes the transfer as a Router.ccipSend() call. On the source chain the pool locks/burns; the CCIP OCR DON and RMN independently sign; the destination pool unlocks/mints on finalization:
ccip_send_message({
"source_chain": "ethereum",
"dest_chain": "base",
"receiver": "0xRecipientOnBase...",
"token": "TNZO",
"amount": "100.0",
"fee_token": "LINK"
})
-> { tx_hash: "0x...", message_id: "0x..." }Step 5: Track the Message
ccip_track_message({ "message_id": "0x..." })
-> {
state: "Success", // InProgress | Success | Failure
source_tx: "0x...",
dest_tx: "0x...",
dest_chain: "base"
}CLI
# List all TNZO CCT pools
tenzro cct list
# Look up a specific chain's pool
tenzro cct pool --chain base
# Quote and send via the chainlink MCP tools
tenzro chainlink ccip-get-fee --source ethereum --dest base --token TNZO --amount 100
tenzro chainlink ccip-send --source ethereum --dest base --token TNZO --amount 100 --receiver 0x...TypeScript SDK
import { TenzroClient } from "@tenzro/sdk";
const client = new TenzroClient();
// 1. Pick a destination pool
const pool = await client.cct.getPool({ chain: "base" });
console.log("dst pool:", pool.pool_address, "type:", pool.pool_type);
// 2. Quote the CCIP fee
const fee = await client.chainlink.ccipGetFee({
source_chain: "ethereum",
dest_chain: "base",
token: "TNZO",
amount: "100.0",
});
// 3. Build and submit the CCIP message
const res = await client.chainlink.ccipSend({
source_chain: "ethereum",
dest_chain: "base",
token: "TNZO",
amount: "100.0",
receiver: "0xRecipientOnBase...",
fee_token: "LINK",
});
console.log("tx", res.tx_hash, "message", res.message_id);RMN defense-in-depth. Each pool references an RMN proxy — an independent "second opinion" committee on CCIP messages. A transfer only executes when the primary DON and the RMN both sign, so a compromise of one committee doesn't directly forge transfers.
CCT vs Cross-VM Pointer Model
Within the Tenzro ledger itself, TNZO uses a Sei-V2-style pointer model (wTNZO ERC-20 pointer, SPL adapter, CIP-56 DAML holding) — notCCT — because all VMs share the same native balance and there is no cross-chain risk. CCT+CCIP is reserved for true external chains where a lock/mint bridge is required.
What You Learned
- Pool types — LockRelease on the canonical chain, BurnMint on remotes
- CCT registry —
tenzro_cctListPools/tenzro_cctGetPool - CCIP flow — fee quote → send → track
- RMN — why CCT is considered higher-assurance than single-committee bridges
Next Steps
- See the Wormhole Bridging tutorial for an alternative trust domain
- See the LI.FI Aggregation tutorial to auto-route across CCT, LayerZero, and Stargate
- Read the CCT reference docs