Cross-Chain Vault Adapter
Uses CCIP View on GitHub

Cross-Chain Vault Adapter

What is it

A CCIP adapter that lets users on other chains deposit into and redeem from your existing ERC-4626 vault, while vault accounting stays on the hub chain, the chain where the vault and the adapter are deployed.

cross-chainvaults
View on GitHub

What the adapter does

Right now, without the adapter, users of a De-Fi protocol backed by an ERC-4626 have to go through a multi-step process to deposit funds into, or redeem funds from, a vault deployed on a different chain from the one they currently have funds on. With Chainlink's Cross-Chain Vault Adapter, a team can accept deposits and redemptions from users on other chains in a single transaction.

Without the adapter, a user on a source chain bridges the asset to the hub chain themselves, switches their wallet network, holds the hub chain's gas token, approves and calls the vault, and bridges the shares back. With the adapter, they approve the source-chain CCIP Router, unless an allowance already exists, and send one CCIP message from the chain they are on.

A return requires the vault's share token to be a CCIP cross-chain token on both chains, as Requirements lists. Redemptions work the same way, in the reverse direction.

Deposit or redeem from another chain

This diagram illustrates the flow of deposits and redemptions with and without the adapter.

In a nutshell:

  1. When you deposit into a 4626 vault, you put in an ERC-20 asset compliant with the vault, and in return, you receive shares, sometimes referred to as vault tokens, or synthetic tokens, that represent your position in the vault.
  2. When you redeem those shares, you get back an equivalent amount of the underlying asset back.

Route comparison

Without the adapter

6 signatures

(3 approvals) · 2 chains · 1 network switch · gas on 2 chains · 2 CCIP messages · 2 CCIP fees

Your wallet asset out · shares back
asset 1st message · you pay the CCIP fee
shares 2nd message · you pay the CCIP fee
Your wallet on the hub chain switch network · hold hub gas
ERC-4626 vault the vault, unchanged

Step 1 of 10 Token approval. Approve the source Router. Approve the source-chain Router to spend your asset. Skipped when your allowance already covers the amount.

With the adapter

2 signatures

(1 approval) · 1 chain · no network switch · gas on 1 chain · 2 CCIP messages · 1 CCIP fee, plus any configured adapter fee taken from the asset

Your wallet asset out · shares back
asset+ 128-byte payload+ payload 1st message · you pay the CCIP fee
shares 2nd message · CCIP fee paid by the adapter
Adapter one hub-chain transaction
  1. aValidate
  2. bAdapter fee
  3. cDeposit
  4. dCheck the output
  5. eSend back
ERC-4626 vault the vault, unchanged
Beneficiary on the hub chain named in your payload

Step 1 of 6 Token approval. Approve the source Router. Approve the source-chain Router to spend your asset. Skipped when your allowance already covers the amount.

Click a numbered stop, or use the arrows, to read each step. transaction you sign token approval wallet network switch second CCIP message

Counts assume a successful request from an EVM wallet, CCIP fees paid in the native gas token, and one transaction per action. Approvals are skipped when the allowance already exists. Nothing here measures time or fee amounts.

The repository also contains a separate multibridge implementation for vaults whose asset or share token moves over other cross-chain protocols. These docs cover the CCIP implementation.

Who it's for

  • Vault teams, such as issuers, protocols, and curators, that run an ERC-4626 vault on one chain and want deposits and redemptions from users on other chains connected by CCIP to become seamless.
  • Integrators who build deposit and redeem interfaces on source chains. They encode the payload and send the CCIP message, and deploy no contracts.

Asynchronous vaults, multi-asset vaults, and targets that are not ERC-4626 vaults need a fork; see Use it as-is or customize it.

Requirements

Only tokens that cross chains need CCIP support. Which tokens those are depends on where users send tokens from and where they want to receive the result:

If you want users to...Tokens that must be CCIP-enabled
Deposit from another chain and receive shares on the hub chainAsset only
Deposit from another chain and receive shares back on the source chainAsset and shares
Redeem from another chain and receive the asset on the hub chainShares only
Redeem from another chain and receive the asset back on the source chainShares and asset

Each token that crosses chains must be configured as a CCIP cross-chain token (CCT) between the source chain and the hub chain.

If your vault's asset is not CCIP-enabled, you can still offer redemptions from another chain with delivery on the hub chain. Users send their CCIP-enabled shares to the adapter, and the adapter redeems them and delivers the asset locally. The asset does not cross chains in this flow.

To enable cross-chain transfers of shares, the share token typically uses Lock & Release on the hub chain, because only the vault may mint shares, and Burn & Mint on source chains.

See the Lock & Mint tutorial for more details.

Your vault and deployment must also meet these requirements:

RequirementNeeded forDetails
Standard ERC-4626 vaultAll flowsSynchronous and single-asset. The adapter calls deposit and redeem itself as depositor and share owner, so the vault must accept a contract in both roles.
Standard ERC-20 behaviorAll flowsThe asset and share token must transfer exactly the amount requested. The adapter does not handle fee-on-transfer or rebasing tokens.
Shanghai-compatible hub chainDeploying the adapterThe adapter bytecode uses the PUSH0 opcode.
Native gas token held by the adapterReturning output to the source chainThe adapter pays each return leg's CCIP fee from its own native gas token balance. If the balance is too low, the request becomes a failed message.

What you deploy and operate

You deploy one adapter on the hub chain with one call to deploy on the CrossChainERC4626AdapterFactory, and that's it.

That transaction sets the accepted source chains, allowlists your vault when you pass its address and enable it, sets the deposit and redeem switches and the adapter fees, and grants the admin, fee setter, and fee collector roles to your accounts. The factory then renounces all of its own roles. The Factory contract reference lists the factory's deployed addresses and its inputs. One adapter can allowlist several vaults on the same chain, and each payload names the vault it targets.

A lane's version is defined as the version of the CCIP OnRamp and OffRamp contracts that serve it, and the CCIP Directory shows it. The factory does not apply CCIP 2.0 lane settings or fund the adapter, so after deployment you:

  • Set the return format for each CCIP 2.0 lane to GENERIC_EXTRA_ARGS_V3_BASIC.
  • Optionally set the inbound finality and Cross-Chain Verifier (CCV) settings for each CCIP 2.0 source chain.
  • Fund the adapter with the native gas token for return legs.

The deployment guide covers configuration and funding. Your team holds the three roles on the adapter:

Role
Controls
DEFAULT_ADMIN_ROLESource chains, the vault allowlist, the deposit and redeem switches, CCIP 2.0 lane settings, roles, and withdrawing the entire native gas token balance
FEE_SETTER_ROLEAdapter fees
FEE_COLLECTOR_ROLEWithdrawing collected adapter fees

Roles use OpenZeppelin AccessControlEnumerable. Role changes take effect in one step, and renouncing the last admin locks the configuration permanently.

You also monitor the MessageFailed event. A failed request leaves its tokens in the adapter until someone refunds or recovers them. Disabling a source chain, a vault, or processing does not pause CCIP delivery. Messages that arrive become failed messages. See What counts as a failure.

Design choices

Vault accounting stays on the hub chain. Every deposit and redemption executes against the vault on the hub chain, with no copy of the vault or its share price elsewhere. Each request waits for CCIP delivery and uses the vault's exchange rate at execution, not at send time. A minimum output bounds the difference, measured after the adapter fee.

No contracts on source chains. Users send a standard CCIP message, and the adapter has no sender allowlist, so any address on an enabled source chain can use it. Nothing checks the payload before the tokens reach the hub chain, and a malformed payload can strand tokens permanently. Build it as described in What a user sends.

Processing failures are held, not reverted. When processing reverts, for example in the vault call, the minimum output check, or the return send, the adapter rolls back the vault call, keeps the inbound tokens, and records a failed message. The CCIP Explorer still shows Success, so track MessageFailed instead. Nothing is retried or refunded automatically.

Permissionless cross-chain refund, optional local recovery. Anyone can call refundFailedMessage to return the tokens to the original sender on the source chain, and the caller pays the CCIP fee in the native gas token. Refunds go to the original sender, not the beneficiary. The payload's local refund address can call recoverFailedMessageLocally on the hub chain instead; that address must be set before sending. Either way, recovery is a hub-chain action. See Failures and recovery.

Fees. Local delivery has no adapter fee. When output returns to the source chain, the user pays any configured adapter fee in asset units, and the adapter pays the return leg's CCIP fee from its native gas token balance. Fees explains how fees are charged and funded, and Limitations covers the remaining trade-offs.

Use it as-is or customize it

Use the adapter as-is through the factory when your vault meets the requirements. Otherwise, fork the repository and edit CrossChainERC4626Adapter.sol. The contract has no virtual hooks for processing, so you change processMessage and _processTarget directly. Common reasons to fork are asynchronous vaults (typically for the redemption path), multi-asset vaults, targets that are not ERC-4626 vaults, custom payload encoding, and custom validation of the source chain, sender, or beneficiary.

A fork must keep these safety properties:

  • ccipReceive accepts calls only from the CCIP Router.
  • Processing runs in a self-call (this.processMessage) inside try/catch, so a processing revert does not revert CCIP execution.
  • Failed messages are stored and stay recoverable through cross-chain refund and local recovery.
  • Address conversion checks reject malformed addresses, such as a beneficiary whose upper 96 bits are not zero.
  • The failure handler (the catch path) must not revert on any input, because a revert there strands the tokens, as described in Failures with no recovery path.

The contracts in src/ccip are audited. A fork is not the audited code and needs its own review and its own deployment, because the factory deploys only the original adapter. Customizing the adapter covers the changes and safety checks.

Where to go next

Get the latest Chainlink content straight to your inbox.