# Cross-Chain Vault Adapter
Source: https://docs.chain.link/solutions/cross-chain-vault-adapter

> For the complete documentation index, see [llms.txt](/llms.txt).

## 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.

[CCIP](/ccip)

cross-chain, vaults

[View on GitHub](https://github.com/smartcontractkit/cross-chain-vault-adapters)

## What the adapter does

With the Cross-Chain Vault Adapter, a vault team can accept deposits and redemptions from users on other chains. The vault is the product, CCIP is the bridge, and the adapter is how users on other chains reach the vault. The hub chain is the chain where the vault and the adapter are deployed. A source chain is any other chain that users send deposits and redemptions from. The vault holds one asset and earns yield. The adapter is a contract on the hub chain that receives tokens and instructions through CCIP and calls the vault for the user.

The asset is the token users deposit and receive when they redeem. Shares represent their position in the vault.

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. The adapter does the hub-chain work in one transaction, then delivers the shares on the hub chain or returns them to the user on the source chain. Returning the shares completes the one-message experience: the user never has to touch the hub chain. A return requires the vault's share token to be a CCIP cross-chain token on both chains, as [Requirements](#requirements) lists. Redemptions work the same way, in the reverse direction.

> **NOTE: Message payload**
>
> A single message carries:
>
> 1. The asset itself, and,
> 2. A 128-byte payload that names: the vault, the beneficiary, the minimum output, the delivery option, and a local
>    refund address (in case the message fails on delivery and a refund is needed).

## Deposit or redeem from another chain

Both routes below are drawn against the same three chains, and the text version carries the same facts. Redemptions take the same route with shares in and the asset out.

The counts assume a successful request from an EVM wallet that uses CCIP directly, pays CCIP fees in the native gas token, and sends each transaction separately. Wallet network switches and CCIP execution are not transactions. A wallet whose allowance already covers the amount skips the approval steps.

**Deposit without the adapter.** The user:

1. Approves the source-chain Router to spend the asset.
2. Sends the asset through CCIP to their own wallet on the hub chain and pays the CCIP fee.
3. Switches the wallet to the hub chain and holds its gas token.
4. Approves the vault to spend the asset.
5. Deposits into the vault and receives shares. Hub-chain delivery ends here.
6. To return the shares, approves the hub-chain Router to spend them.
7. Sends the shares through CCIP to the source chain and pays a second CCIP fee.

**Deposit with the adapter.** The user:

1. Approves the source-chain Router to spend the asset.
2. Sends the asset and a 128-byte payload to the adapter through CCIP and pays the CCIP fee.

CCIP delivers the asset to the adapter and calls it. In that one hub-chain transaction, the adapter validates the request, deducts any configured adapter fee when the shares return to the source chain, approves the vault and deposits, and checks that the output is nonzero and at least the minimum. It then transfers the shares to the beneficiary on the hub chain, or approves the hub-chain Router and sends them back in a second CCIP message, paying that message's CCIP fee from its own native balance. The user signs nothing on the hub chain.

**Redeem.** The same routes with shares in and the asset out. Without the adapter, the user redeems their own shares, so there is no vault approval. With the adapter, the adapter redeems as owner and receiver, then deducts any configured adapter fee from the redeemed asset before returning it.

**Transactions the user's wallet signs.** Approvals are included; a wallet with existing allowances signs the number in parentheses fewer times.

| Action  | Result delivered         | Without the adapter | With the adapter | Chains signed on | Network switches | CCIP messages |
| ------- | ------------------------ | ------------------- | ---------------- | ---------------- | ---------------- | ------------- |
| Deposit | Back on the source chain | 6 (3 approvals)     | 2 (1 approval)   | 2 vs 1           | 1 vs 0           | 2             |
| Deposit | On the hub chain         | 4 (2 approvals)     | 2 (1 approval)   | 2 vs 1           | 1 vs 0           | 1             |
| Redeem  | Back on the source chain | 5 (2 approvals)     | 2 (1 approval)   | 2 vs 1           | 1 vs 0           | 2             |
| Redeem  | On the hub chain         | 3 (1 approval)      | 2 (1 approval)   | 2 vs 1           | 1 vs 0           | 1             |

Fees the user pays directly: without the adapter, one CCIP fee per message they send, so two when the output returns. With the adapter, one CCIP fee, plus any configured adapter fee when the output returns to the source chain; the adapter pays the return message's CCIP fee.

With the adapter, a request that fails after delivery is not reverted: the adapter keeps the tokens as a refundable failed message, and the refund or local recovery is a hub-chain action. [How it works](/solutions/cross-chain-vault-adapter/overview/how-it-works) explains the payload, processing, both delivery options, and fees.

The repository also contains a separate [multibridge implementation](https://github.com/smartcontractkit/cross-chain-vault-adapters/blob/main/docs/multibridge/README.md) 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](https://eips.ethereum.org/EIPS/eip-4626) vault on one chain and want deposits and redemptions from users on other chains connected by CCIP.
- **Integrators** who build deposit and redeem interfaces on source chains. They encode the payload and send the CCIP message, and deploy no contracts.

The contract also supports Solana source chains. These docs cover EVM source chains.

Asynchronous vaults, multi-asset vaults, and targets that are not ERC-4626 vaults need a fork; see [Use it as-is or customize it](#use-it-as-is-or-customize-it). An asynchronous vault that accepts synchronous ERC-4626 deposits needs the fork only for redemptions: deposits work with the adapter as-is.

## 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 chain           | Asset only                       |
| Deposit from another chain and receive shares back on the source chain   | Asset and shares                 |
| Redeem from another chain and receive the asset on the hub chain         | Shares only                      |
| Redeem from another chain and receive the asset back on the source chain | Shares and asset                 |

Each token that crosses chains must be configured as a [CCIP cross-chain token (CCT)](/ccip/concepts/cross-chain-token/overview) 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](/ccip/evm/tutorials/cross-chain-tokens/register-from-eoa-lock-mint-foundry).

Your vault and deployment must also meet these requirements:

| Requirement                          | Needed for                           | Details                                                                                                                                                        |
| ------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Standard ERC-4626 vault              | All flows                            | Synchronous 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 behavior             | All flows                            | The asset and share token must transfer exactly the amount requested. The adapter does not handle fee-on-transfer or rebasing tokens.                          |
| Shanghai-compatible hub chain        | Deploying the adapter                | The adapter bytecode uses the `PUSH0` opcode.                                                                                                                  |
| Native gas token held by the adapter | Returning output to the source chain | The 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.            |

> **NOTE: Need the adapter to support cross-chain tokens from other protocols?**
>
> 1. The multi-bridge version of our adapter contract is compatible with cross-chain tokens from some other protocols.
> 2. Please reach out to the [Chainlink team](https://chain.link/ccip-contact) for support if your use case demands that.

## What you deploy and operate

You deploy one adapter on the hub chain with one call to `deploy` on the `CrossChainERC4626AdapterFactory`. 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](/solutions/cross-chain-vault-adapter/reference/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](/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)](/ccip/concepts/ccvs/overview) settings for each CCIP 2.0 source chain.
- Fund the adapter with the native gas token for return legs.

The [deployment guide](/solutions/cross-chain-vault-adapter/guides/deploy-the-adapter) covers configuration and funding. Your team holds the three roles on the adapter:

| Role                 | Controls                                                                                                                                                |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEFAULT_ADMIN_ROLE` | Source 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_ROLE`    | Adapter fees                                                                                                                                            |
| `FEE_COLLECTOR_ROLE` | Withdrawing collected adapter fees                                                                                                                      |

Roles use OpenZeppelin [`AccessControlEnumerable`](https://docs.openzeppelin.com/contracts/5.x/api/access#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](/solutions/cross-chain-vault-adapter/overview/failures-and-recovery#what-counts-as-a-failure).

> **NOTE: Chainlink does not run your adapter**
>
> Chainlink does not deploy, operate, or control adapters. See [CCIP service
> responsibility](/ccip/concepts/service-responsibility).

## 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](/solutions/cross-chain-vault-adapter/overview/how-it-works#slippage-protection) 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](/solutions/cross-chain-vault-adapter/overview/how-it-works#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](https://ccip.chain.link) 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](/solutions/cross-chain-vault-adapter/overview/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](/solutions/cross-chain-vault-adapter/overview/how-it-works#fees) explains how fees are charged and funded, and [Limitations](/solutions/cross-chain-vault-adapter/reference/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](#requirements). Otherwise, fork the [repository](https://github.com/smartcontractkit/cross-chain-vault-adapters) and edit [`CrossChainERC4626Adapter.sol`](https://github.com/smartcontractkit/cross-chain-vault-adapters/blob/main/src/ccip/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](/solutions/cross-chain-vault-adapter/overview/failures-and-recovery#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](/solutions/cross-chain-vault-adapter/reference/limitations#customizing-the-adapter) covers the changes and safety checks.

## Where to go next

- [How it works](/solutions/cross-chain-vault-adapter/overview/how-it-works): the payload, both flows, delivery options, and fees.
- [Failures and recovery](/solutions/cross-chain-vault-adapter/overview/failures-and-recovery): failed requests and how users get their tokens back.
- [Limitations](/solutions/cross-chain-vault-adapter/reference/limitations): what the adapter does not do.
- [Adapter contract](/solutions/cross-chain-vault-adapter/reference/adapter-contract): the `CrossChainERC4626Adapter` interface.
- [Factory contract](/solutions/cross-chain-vault-adapter/reference/factory-contract): the `CrossChainERC4626AdapterFactory` interface and deployed addresses.
- [Deploy the vault and set up its share token](/solutions/cross-chain-vault-adapter/guides/deploy-the-vault): deploy an example ERC-4626 vault on Ethereum Sepolia and make its share token a CCIP cross-chain token.
- [Deploy the adapter](/solutions/cross-chain-vault-adapter/guides/deploy-the-adapter): deploy, configure, and fund an adapter for that vault through the factory.
- [Deposit from a source chain](/solutions/cross-chain-vault-adapter/guides/deposit-from-source-chain): deposit into the vault from Arbitrum Sepolia through the adapter and receive the shares on Arbitrum Sepolia.
- [Redeem from a source chain](/solutions/cross-chain-vault-adapter/guides/redeem-from-source-chain): redeem the shares from Arbitrum Sepolia, receive CCIP-BnM back, and withdraw the adapter fees as the vault operator.

## Disclaimer

Please note, this repo contains community examples only — these are not Chainlink products or services and are not supported or maintained by Chainlink. This code represents an example of using a Chainlink product or service, and is intended for demonstration and educational purposes only. It is provided “AS IS” and “AS AVAILABLE” without warranties of any kind, may not have been audited, and may omit checks or error handling. Each party intending to use this example code does so entirely at their own risk and must perform its own audits, security and code review, key management, and testing before any production deployment and ensure the operation and performance of such code matches expectations. Neither Chainlink Labs nor the Chainlink Foundation deploys, operates, monitors, maintains or endorses any deployment of this code. Note that this is not a Chainlink product, feature or service, and there are no commitments made with respect to the code, including compatibility with future Chainlink releases. You should not rely on this code without first conducting your own technical, engineering, and security review. This code is also outside the scope of any Chainlink bug bounty programs. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for outcomes due to errors in this example or how it is deployed or operated, or liable for any resulting claims or damages. Use of the Chainlink Network is subject to the Chainlink Foundation [Terms of Service](https://chain.link/terms), which provides important information and disclosures. By using this code, you acknowledge and agree to these terms.