# 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

> **NOTE: ERC-4626?**
>
> These docs assume familiarity with the ERC-4626 standard. Please refer to
> [ethereum.org](https://ethereum.org/developers/docs/standards/tokens/erc-4626/) for more details.

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.

> **NOTE: Key Terms You Need to Know**
>
> 1. **Hub Chain:** The chain where the vault and the adapter are deployed. 2. **Source Chain:** Any other chain that
>    users send deposits and redemptions from. 3. **Vault:** An ERC-4626 contract. Holds the assets and earns yield for the
>    user. 4. **Adapter:** A Chainlink contract, deployed on the hub chain, that receives tokens and instructions through
>    CCIP and calls the vault for the user. 5. **Asset:** The token users deposit and receive back when they redeem. 6.
>    **Shares**: Represent a user's 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.

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

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.

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.

> 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 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](#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 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) for more details.

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`, 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](/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.