Skip to main content

Deployment Issues

Docker on M1 Chips

Some machines, including those with M1 chips, may be unable to build the Docker image locally. This is a known issue.
Workaround options:
  1. Use native Foundry installation: Instead of Docker, install Foundry directly using the official installation script:
    curl -L https://foundry.paradigm.xyz | bash
    foundryup
    
  2. Use Rosetta: Run Docker with Rosetta 2 emulation:
    docker run --platform linux/amd64 --rm foundry "<COMMAND>"
    
  3. Build on different architecture: Build the Docker image on an x86_64 machine and push to a registry.

Environment Configuration

If deployment scripts fail, verify all required environment variables are set:
MESSAGE_TRANSMITTER_DEPLOYER_KEY
TOKEN_MESSENGER_DEPLOYER_KEY
TOKEN_MINTER_DEPLOYER_KEY
TOKEN_CONTROLLER_DEPLOYER_KEY
ATTESTER_ADDRESS
USDC_CONTRACT_ADDRESS
REMOTE_USDC_CONTRACT_ADDRESS
MESSAGE_TRANSMITTER_PAUSER_ADDRESS
TOKEN_MINTER_PAUSER_ADDRESS
MESSAGE_TRANSMITTER_RESCUER_ADDRESS
TOKEN_MESSENGER_RESCUER_ADDRESS
TOKEN_MINTER_RESCUER_ADDRESS
TOKEN_CONTROLLER_ADDRESS
DOMAIN
REMOTE_DOMAIN
BURN_LIMIT_PER_MESSAGE
For V2 deployments, see the V2 deployment section for a complete list of required environment variables.

Attestation Service Issues

Rate Limiting

The attestation service is rate-limited to 1 request per second. Exceeding this limit will result in failed requests.
Solution: Implement proper rate limiting in your application:
let attestationResponse = {status: 'pending'};
while(attestationResponse.status != 'complete') {
    const response = await fetch(
        `https://iris-api-sandbox.circle.com/attestations/${messageHash}`
    );
    attestationResponse = await response.json();
    // Wait 2 seconds between requests
    await new Promise(r => setTimeout(r, 2000));
}

Attestation Pending Too Long

If an attestation remains in pending status for an extended period:
  1. Verify the transaction was confirmed: Check that your depositForBurn transaction was successfully mined
  2. Check the correct network: Ensure you’re querying the right attestation service (sandbox vs. production)
  3. Wait for finality: Attestations require block finality, which varies by chain
  4. Verify message hash: Ensure the message hash was correctly extracted from transaction logs
const eventTopic = web3.utils.keccak256('MessageSent(bytes)');
const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic);
const messageBytes = web3.eth.abi.decodeParameters(['bytes'], log.data)[0];
const messageHash = web3.utils.keccak256(messageBytes);

Transaction Issues

Gas Estimation Failures

If gas estimation fails when calling CCTP functions:
Ensure you’ve approved the TokenMessenger contract before calling depositForBurn:
const approveTx = await usdcContract.methods
    .approve(TOKEN_MESSENGER_ADDRESS, amount)
    .send({gas: approveTxGas});
Confirm your address has enough USDC for the transfer amount plus fees.
The contract may be paused. Check the paused state:
const isPaused = await messageTransmitter.methods.paused().call();
if (isPaused) {
    console.log('Contract is paused');
}
Ensure all parameters meet requirements:
  • Amount > 0
  • Recipient address is not zero
  • Max fee < amount
  • Min fee requirements met (V2)

“Invalid Message Version” Error

This error occurs when message versions don’t match:
  • V1 contracts: Use message version 0
  • V2 contracts: Use message version 1
Ensure you’re interacting with the correct contract version and using compatible message formats.

”Nonce Already Used” Error

This error means the message has already been processed on the destination chain. Causes:
  • Message was already received and processed
  • Duplicate transaction submission
Solution: This is expected behavior for replay protection. Each message can only be processed once.

Testing and Debugging

Running Tests with Debug Logs

Use Foundry’s verbosity flags to see detailed test output:
# Basic test output
forge test

# Show console.log() statements
forge test -vv

# Show stack traces for failures
forge test -vvv

# Show all traces including passing tests
forge test -vvvv

# Show all traces and setup
forge test -vvvvv
Contracts must import lib/forge-std/src/console.sol to use console.log() statements.

Static Analysis

Run Mythril for security analysis:
# Analyze specific component
make analyze-message-transmitter
make analyze-message-transmitter-v2
make analyze-token-messenger-minter

# Or analyze a specific file
myth -v4 analyze $FILE_PATH --solc-json mythril.config.json --solv 0.7.6
Mythril analysis can take several minutes to complete.

Common Error Messages

You’re attempting to burn 0 tokens. Ensure the amount is greater than 0.
The recipient address is set to zero. Provide a valid destination address.
The max fee equals or exceeds the transfer amount. Reduce the max fee.
The max fee is below the minimum required fee. Calculate minimum fee:
const minFee = await tokenMessenger.methods.getMinFeeAmount(amount).call();
Contract operations are paused. Wait for the contract to be unpaused by the pauser role.
The destination domain is not configured. Verify the domain ID and ensure remote token messenger is registered.
Your address (msg.sender or tx.origin) is on the denylist. Contact support if you believe this is an error.
The signature threshold is set higher than the number of enabled attesters. This is a configuration error during initialization.

Integration Testing

Running Anvil Tests

Test your integration locally with Anvil:
# Start Anvil testnet and run integration tests
make anvil-test
Example integration tests are available in the anvil/ directory.

Linting

Run Solhint to catch common issues:
yarn lint

Need More Help?

FAQ

Frequently asked questions

Security

Report security vulnerabilities

Build docs developers (and LLMs) love