> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/circlefin/evm-cctp-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# V2 Deployment Guide

> Deploy EVM CCTP Contracts V2 using CREATE2 and proxy pattern

## Overview

CCTP V2 contracts use a CREATE2-based deployment strategy to ensure deterministic addresses across chains. The deployment process involves:

1. **Create2Factory**: Deploy factory for deterministic deployments
2. **Implementation Contracts**: Deploy logic contracts
3. **Proxy Contracts**: Deploy proxies pointing to implementations
4. **Setup Remote Resources**: Configure cross-chain connections
5. **Key Rotation** (Mainnet only): Rotate administrative keys
6. **AddressUtilsExternal**: Deploy helper library

<Note>
  All deployment scripts use [Forge Scripts](https://book.getfoundry.sh/tutorials/solidity-scripting) and are located in `scripts/v2/` directory.
</Note>

## Prerequisites

* Foundry CLI installed (forge 0.2.0)
* Git submodules initialized: `git submodule update --init --recursive`
* Node.js and Yarn installed: `yarn install`
* Sufficient funds in deployer accounts for gas

## Deployment Steps

### Step 1: Deploy Create2Factory

The Create2Factory enables deterministic address deployment across chains.

<Steps>
  <Step title="Configure Environment">
    Add to your `.env` file:

    ```bash theme={null}
    CREATE2_FACTORY_OWNER=<address>
    ```

    **Parameters**:

    * `CREATE2_FACTORY_OWNER`: Address that will own the Create2Factory
  </Step>

  <Step title="Simulate Deployment">
    Perform a dry run:

    ```bash theme={null}
    make simulate-deploy-create2-factory \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<PRIVATE_KEY>
    ```

    <Note>
      `SENDER` should match the address derived from `CREATE2_FACTORY_OWNER_KEY`.
    </Note>
  </Step>

  <Step title="Deploy Factory">
    Deploy the Create2Factory:

    ```bash theme={null}
    make deploy-create2-factory \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<PRIVATE_KEY>
    ```

    Record the deployed factory address for subsequent steps.
  </Step>
</Steps>

**Reference**: `scripts/v2/DeployCreate2Factory.s.sol`

### Step 2: Deploy V2 Implementation Contracts

Deploy the logic contracts that proxies will delegate to.

<Steps>
  <Step title="Configure Environment">
    Add to your `.env` file:

    ```bash theme={null}
    CREATE2_FACTORY_CONTRACT_ADDRESS=<address>
    TOKEN_MINTER_V2_OWNER_KEY=<private_key>
    TOKEN_CONTROLLER_ADDRESS=<address>
    DOMAIN=<uint32>
    MESSAGE_BODY_VERSION=<uint32>
    VERSION=<uint32>
    ```

    **Parameters**:

    * `CREATE2_FACTORY_CONTRACT_ADDRESS`: Address from Step 1
    * `TOKEN_MINTER_V2_OWNER_KEY`: Private key for TokenMinter owner
    * `TOKEN_CONTROLLER_ADDRESS`: Token controller address
    * `DOMAIN`: Local domain ID (e.g., 0 for Ethereum, 1 for Avalanche)
    * `MESSAGE_BODY_VERSION`: Message format version (typically 1)
    * `VERSION`: Protocol version (typically 2)
  </Step>

  <Step title="Simulate Deployment">
    ```bash theme={null}
    make simulate-deploy-implementations-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```
  </Step>

  <Step title="Deploy Implementations">
    ```bash theme={null}
    make deploy-implementations-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```

    This deploys:

    * **MessageTransmitterV2** implementation
    * **TokenMessengerV2** implementation
    * **TokenMinterV2** implementation

    Record all implementation addresses.
  </Step>
</Steps>

**Reference**: `scripts/v2/DeployImplementationsV2.s.sol`

### Step 3: Deploy V2 Proxies

Deploy proxy contracts with full initialization.

<Steps>
  <Step title="Configure Environment">
    Replace environment variables in `.env`:

    ```bash theme={null}
    # Token Configuration
    USDC_CONTRACT_ADDRESS=<address>
    TOKEN_CONTROLLER_ADDRESS=<address>

    # Remote Configuration (1:1:1 correspondence)
    REMOTE_DOMAINS=<uint32[]>                    # e.g., "1,2,3"
    REMOTE_USDC_CONTRACT_ADDRESSES=<address[]>   # e.g., "0x123...,0x456...,0x789..."
    REMOTE_TOKEN_MESSENGER_V2_ADDRESSES=<bytes32[]>

    # Factory
    CREATE2_FACTORY_CONTRACT_ADDRESS=<address>

    # MessageTransmitterV2 Roles
    MESSAGE_TRANSMITTER_V2_OWNER_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_PAUSER_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_RESCUER_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_ATTESTER_MANAGER_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_ATTESTER_1_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_ATTESTER_2_ADDRESS=<address>
    MESSAGE_TRANSMITTER_V2_PROXY_ADMIN_ADDRESS=<address>

    # TokenMinterV2 Roles
    TOKEN_MINTER_V2_PAUSER_ADDRESS=<address>
    TOKEN_MINTER_V2_RESCUER_ADDRESS=<address>

    # TokenMessengerV2 Roles (NEW in V2)
    TOKEN_MESSENGER_V2_OWNER_ADDRESS=<address>
    TOKEN_MESSENGER_V2_RESCUER_ADDRESS=<address>
    TOKEN_MESSENGER_V2_FEE_RECIPIENT_ADDRESS=<address>
    TOKEN_MESSENGER_V2_DENYLISTER_ADDRESS=<address>
    TOKEN_MESSENGER_V2_PROXY_ADMIN_ADDRESS=<address>
    TOKEN_MESSENGER_V2_MIN_FEE_CONTROLLER_ADDRESS=<address>
    TOKEN_MESSENGER_V2_MIN_FEE=<uint256>         # e.g., 5000 for 0.05%

    # Domain Configuration
    DOMAIN=<uint32>
    BURN_LIMIT_PER_MESSAGE=<uint256>

    # Deployer Keys
    TOKEN_CONTROLLER_KEY=<private_key>
    TOKEN_MINTER_V2_OWNER_KEY=<private_key>
    ```

    <Warning>
      **Important**: `REMOTE_DOMAINS`, `REMOTE_USDC_CONTRACT_ADDRESSES`, and `REMOTE_TOKEN_MESSENGER_V2_ADDRESSES` must correspond 1:1:1 in order.
    </Warning>

    **New V2 Role Addresses**:

    * `TOKEN_MESSENGER_V2_FEE_RECIPIENT_ADDRESS`: Receives collected fees
    * `TOKEN_MESSENGER_V2_DENYLISTER_ADDRESS`: Manages protocol denylist
    * `TOKEN_MESSENGER_V2_MIN_FEE_CONTROLLER_ADDRESS`: Sets minimum fees
    * `TOKEN_MESSENGER_V2_MIN_FEE`: Initial minimum fee in 1/1000 basis points
  </Step>

  <Step title="Simulate Deployment">
    ```bash theme={null}
    make simulate-deploy-proxies-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```
  </Step>

  <Step title="Deploy Proxies">
    ```bash theme={null}
    make deploy-proxies-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```

    This deploys and initializes:

    * **MessageTransmitterV2** proxy
    * **TokenMessengerV2** proxy
    * **TokenMinterV2** proxy

    All contracts are fully initialized with roles and remote configurations.
  </Step>
</Steps>

**Reference**: `scripts/v2/DeployProxiesV2.s.sol`

### Step 4: Setup Additional Remote Resources

<Note>
  **Only perform steps 4-7 for additional remote resources NOT already configured in Step 3.**
</Note>

If you need to add more remote chains after initial deployment:

<Steps>
  <Step title="Configure Environment">
    Update `.env` with additional remote configuration:

    ```bash theme={null}
    TOKEN_MESSENGER_V2_OWNER_KEY=<private_key>
    TOKEN_CONTROLLER_KEY=<private_key>
    TOKEN_MESSENGER_V2_CONTRACT_ADDRESS=<address>
    TOKEN_MINTER_V2_CONTRACT_ADDRESS=<address>
    USDC_CONTRACT_ADDRESS=<address>
    REMOTE_USDC_CONTRACT_ADDRESS=<address>
    REMOTE_DOMAIN=<uint32>
    ```

    Add **one remote resource at a time**, repeating these steps for each remote chain.
  </Step>

  <Step title="Simulate Setup">
    ```bash theme={null}
    make simulate-setup-remote-resources-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER>
    ```
  </Step>

  <Step title="Setup Remote Resources">
    ```bash theme={null}
    make setup-remote-resources-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER>
    ```

    This:

    * Links remote TokenMessenger
    * Configures remote USDC token pair
  </Step>

  <Step title="Repeat for Each Remote Chain">
    Repeat steps 1-3 for each additional remote chain you want to support.
  </Step>
</Steps>

**Reference**: `scripts/v2/SetupRemoteResourcesV2.s.sol`

### Step 5: Key Rotation (Mainnet Only)

<Warning>
  **Mainnet Only**: This step should only be performed on mainnet deployments. Skip for testnets.
</Warning>

<Steps>
  <Step title="Configure Environment">
    Add to your `.env` file:

    ```bash theme={null}
    # Current Contract Addresses
    MESSAGE_TRANSMITTER_V2_CONTRACT_ADDRESS=<address>
    TOKEN_MESSENGER_V2_CONTRACT_ADDRESS=<address>
    TOKEN_MINTER_V2_CONTRACT_ADDRESS=<address>

    # Current Owner Keys
    MESSAGE_TRANSMITTER_V2_OWNER_KEY=<private_key>
    TOKEN_MESSENGER_V2_OWNER_KEY=<private_key>
    TOKEN_MINTER_V2_OWNER_KEY=<private_key>

    # New Owner Addresses
    MESSAGE_TRANSMITTER_V2_NEW_OWNER_ADDRESS=<address>
    TOKEN_MESSENGER_V2_NEW_OWNER_ADDRESS=<address>
    TOKEN_MINTER_V2_NEW_OWNER_ADDRESS=<address>
    NEW_TOKEN_CONTROLLER_ADDRESS=<address>
    ```
  </Step>

  <Step title="Simulate Key Rotation">
    ```bash theme={null}
    make simulate-rotate-keys-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER>
    ```
  </Step>

  <Step title="Rotate Keys">
    ```bash theme={null}
    make rotate-keys-v2 \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER>
    ```

    This transfers ownership of all V2 contracts to new owner addresses.
  </Step>
</Steps>

**Reference**: `scripts/v2/RotateKeysV2.s.sol`

### Step 6: Deploy AddressUtilsExternal

Deploy the helper library for easy integration.

<Steps>
  <Step title="Configure Environment">
    Ensure `CREATE2_FACTORY_CONTRACT_ADDRESS` is set in `.env`:

    ```bash theme={null}
    CREATE2_FACTORY_CONTRACT_ADDRESS=<address>
    ```
  </Step>

  <Step title="Simulate Deployment">
    ```bash theme={null}
    make simulate-deploy-address-utils-external \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```
  </Step>

  <Step title="Deploy AddressUtilsExternal">
    ```bash theme={null}
    make deploy-address-utils-external \
      RPC_URL=<RPC_URL> \
      SENDER=<SENDER> \
      CREATE2_FACTORY_OWNER_KEY=<CREATE2_FACTORY_OWNER_KEY>
    ```

    This deploys `AddressUtilsExternal` to a deterministic address for easy integration.
  </Step>
</Steps>

**Reference**: `scripts/v2/DeployAddressUtilsExternal.s.sol`

## Predicting CREATE2 Addresses

You can predict deployment addresses before deploying:

```bash theme={null}
# Predict MessageTransmitterV2 Implementation
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "messageTransmitterV2Impl(address,uint32,uint32)" \
  <create2FactoryAddress> \
  <domain> \
  <messageVersion>

# Predict TokenMessengerV2 Implementation
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "tokenMessengerV2Impl(address,uint32)" \
  <create2FactoryAddress> \
  <messageBodyVersion>

# Predict MessageTransmitterV2 Proxy
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "messageTransmitterV2Proxy(address)" \
  <create2FactoryAddress>

# Predict TokenMessengerV2 Proxy
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "tokenMessengerV2Proxy(address)" \
  <create2FactoryAddress>

# Predict TokenMinterV2
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "tokenMinterV2(address)" \
  <create2FactoryAddress>

# Predict AddressUtilsExternal
forge script scripts/v2/PredictCreate2Deployments.s.sol \
  --sig "addressUtilsExternal(address)" \
  <create2FactoryAddress>
```

**Reference**: `scripts/v2/PredictCreate2Deployments.s.sol`

## Role Configuration Summary

### MessageTransmitterV2 Roles

| Role                | Responsibilities                        |
| ------------------- | --------------------------------------- |
| **owner**           | General administration, role updates    |
| **pauser**          | Emergency pause functionality           |
| **rescuer**         | Emergency token recovery                |
| **attesterManager** | Manages attester set                    |
| **attester**        | Signs attestations (multiple attesters) |
| **proxyAdmin**      | Proxy upgrade authority                 |

### TokenMessengerV2 Roles

| Role                 | Responsibilities              | New in V2 |
| -------------------- | ----------------------------- | --------- |
| **owner**            | General administration        |           |
| **rescuer**          | Emergency token recovery      |           |
| **feeRecipient**     | Receives collected fees       | ✅         |
| **denylister**       | Manages protocol denylist     | ✅         |
| **minFeeController** | Sets minimum fee requirements | ✅         |
| **proxyAdmin**       | Proxy upgrade authority       |           |

### TokenMinterV2 Roles

| Role                | Responsibilities                    |
| ------------------- | ----------------------------------- |
| **owner**           | General administration              |
| **pauser**          | Emergency pause functionality       |
| **rescuer**         | Emergency token recovery            |
| **tokenController** | Manages token pairs and burn limits |

## Environment Variables Checklist

<AccordionGroup>
  <Accordion title="Step 1: Create2Factory">
    ```bash theme={null}
    ✓ CREATE2_FACTORY_OWNER
    ```
  </Accordion>

  <Accordion title="Step 2: Implementation Contracts">
    ```bash theme={null}
    ✓ CREATE2_FACTORY_CONTRACT_ADDRESS
    ✓ TOKEN_MINTER_V2_OWNER_KEY
    ✓ TOKEN_CONTROLLER_ADDRESS
    ✓ DOMAIN
    ✓ MESSAGE_BODY_VERSION
    ✓ VERSION
    ```
  </Accordion>

  <Accordion title="Step 3: Proxy Deployment">
    ```bash theme={null}
    # Token & Factory
    ✓ USDC_CONTRACT_ADDRESS
    ✓ TOKEN_CONTROLLER_ADDRESS
    ✓ CREATE2_FACTORY_CONTRACT_ADDRESS

    # Remote Configuration
    ✓ REMOTE_DOMAINS
    ✓ REMOTE_USDC_CONTRACT_ADDRESSES
    ✓ REMOTE_TOKEN_MESSENGER_V2_ADDRESSES

    # MessageTransmitter Roles
    ✓ MESSAGE_TRANSMITTER_V2_OWNER_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_PAUSER_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_RESCUER_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_ATTESTER_MANAGER_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_ATTESTER_1_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_ATTESTER_2_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_PROXY_ADMIN_ADDRESS

    # TokenMinter Roles
    ✓ TOKEN_MINTER_V2_PAUSER_ADDRESS
    ✓ TOKEN_MINTER_V2_RESCUER_ADDRESS

    # TokenMessenger Roles (NEW)
    ✓ TOKEN_MESSENGER_V2_OWNER_ADDRESS
    ✓ TOKEN_MESSENGER_V2_RESCUER_ADDRESS
    ✓ TOKEN_MESSENGER_V2_FEE_RECIPIENT_ADDRESS
    ✓ TOKEN_MESSENGER_V2_DENYLISTER_ADDRESS
    ✓ TOKEN_MESSENGER_V2_PROXY_ADMIN_ADDRESS
    ✓ TOKEN_MESSENGER_V2_MIN_FEE_CONTROLLER_ADDRESS
    ✓ TOKEN_MESSENGER_V2_MIN_FEE

    # Domain
    ✓ DOMAIN
    ✓ BURN_LIMIT_PER_MESSAGE

    # Keys
    ✓ TOKEN_CONTROLLER_KEY
    ✓ TOKEN_MINTER_V2_OWNER_KEY
    ```
  </Accordion>

  <Accordion title="Step 4: Additional Remote Resources">
    ```bash theme={null}
    ✓ TOKEN_MESSENGER_V2_OWNER_KEY
    ✓ TOKEN_CONTROLLER_KEY
    ✓ TOKEN_MESSENGER_V2_CONTRACT_ADDRESS
    ✓ TOKEN_MINTER_V2_CONTRACT_ADDRESS
    ✓ USDC_CONTRACT_ADDRESS
    ✓ REMOTE_USDC_CONTRACT_ADDRESS
    ✓ REMOTE_DOMAIN
    ```
  </Accordion>

  <Accordion title="Step 5: Key Rotation (Mainnet)">
    ```bash theme={null}
    # Current
    ✓ MESSAGE_TRANSMITTER_V2_CONTRACT_ADDRESS
    ✓ TOKEN_MESSENGER_V2_CONTRACT_ADDRESS
    ✓ TOKEN_MINTER_V2_CONTRACT_ADDRESS
    ✓ MESSAGE_TRANSMITTER_V2_OWNER_KEY
    ✓ TOKEN_MESSENGER_V2_OWNER_KEY
    ✓ TOKEN_MINTER_V2_OWNER_KEY

    # New
    ✓ MESSAGE_TRANSMITTER_V2_NEW_OWNER_ADDRESS
    ✓ TOKEN_MESSENGER_V2_NEW_OWNER_ADDRESS
    ✓ TOKEN_MINTER_V2_NEW_OWNER_ADDRESS
    ✓ NEW_TOKEN_CONTROLLER_ADDRESS
    ```
  </Accordion>

  <Accordion title="Step 6: AddressUtilsExternal">
    ```bash theme={null}
    ✓ CREATE2_FACTORY_CONTRACT_ADDRESS
    ```
  </Accordion>
</AccordionGroup>

## Post-Deployment Verification

After deployment, verify:

<Steps>
  <Step title="Contract Addresses">
    Record all deployed addresses:

    * Create2Factory
    * MessageTransmitterV2 (implementation & proxy)
    * TokenMessengerV2 (implementation & proxy)
    * TokenMinterV2 (implementation & proxy)
    * AddressUtilsExternal
  </Step>

  <Step title="Role Configuration">
    Verify all roles are set correctly:

    ```bash theme={null}
    # Check owner
    cast call $TOKEN_MESSENGER_V2 "owner()" --rpc-url $RPC_URL

    # Check fee recipient
    cast call $TOKEN_MESSENGER_V2 "feeRecipient()" --rpc-url $RPC_URL

    # Check denylister
    cast call $TOKEN_MESSENGER_V2 "denylister()" --rpc-url $RPC_URL
    ```
  </Step>

  <Step title="Remote Configuration">
    Verify remote token messengers are linked:

    ```bash theme={null}
    # Check remote token messenger
    cast call $TOKEN_MESSENGER_V2 \
      "remoteTokenMessengers(uint32)" \
      $REMOTE_DOMAIN \
      --rpc-url $RPC_URL
    ```
  </Step>

  <Step title="Fee Configuration">
    Verify fee settings:

    ```bash theme={null}
    # Check minimum fee
    cast call $TOKEN_MESSENGER_V2 "minFee()" --rpc-url $RPC_URL

    # Check min fee controller
    cast call $TOKEN_MESSENGER_V2 "minFeeController()" --rpc-url $RPC_URL
    ```
  </Step>
</Steps>

## Troubleshooting

### Simulation Fails

* Verify all environment variables are set correctly
* Check that SENDER has sufficient funds for gas
* Ensure RPC\_URL is accessible and correct
* Verify private keys match expected addresses

### CREATE2 Address Mismatch

* Ensure Create2Factory address is consistent across chains
* Verify constructor parameters match exactly
* Check that salt values are identical

### Initialization Fails

* Verify all role addresses are non-zero
* Check that remote arrays have equal length
* Ensure owner address is set correctly

### Remote Resources Not Linking

* Verify remote domain IDs are correct
* Check that remote TokenMessenger addresses are bytes32 format
* Ensure TokenMinter has correct token pairs configured

## Next Steps

<CardGroup cols={2}>
  <Card title="Test Deployment" icon="flask" href="/guides/testing">
    Test your V2 deployment
  </Card>

  <Card title="Integration Guide" icon="plug" href="/guides/integration">
    Integrate V2 into your application
  </Card>

  <Card title="Migration Guide" icon="arrow-right" href="/v2/migration-guide">
    Migrate from V1 to V2
  </Card>

  <Card title="API Reference" icon="code" href="/api/v2/token-messenger-v2">
    Complete V2 API documentation
  </Card>
</CardGroup>
