1

For some reason, chainlink CCIP is not working when I use the token USDC (address 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 on ETH Sepolia) but it is working on the token which I have created. I tried it with both chainlink CCIP Javascript SDK as well as the contract, none of which are working.

const { txHash, messageId } = await ccipClient.transferTokens({ client: walletClient, routerAddress: routerAddress as `0x${string}`, tokenAddress: usdcAddress as `0x${string}`, amount: amountWei, destinationAccount: address as `0x${string}`, destinationChainSelector: destChainSelector, }) 
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IRouterClient} from "@ccip/contracts/src/v0.8/ccip/interfaces/IRouterClient.sol"; import {OwnerIsCreator} from "@ccip/contracts/src/v0.8/shared/access/OwnerIsCreator.sol"; import {Client} from "@ccip/contracts/src/v0.8/ccip/libraries/Client.sol"; import {CCIPReceiver} from "@ccip/contracts/src/v0.8/ccip/applications/CCIPReceiver.sol"; import {IERC20} from "@ccip/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@ccip/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title USDCBridge * @notice Facilitates USDC transfers across chains using Chainlink CCIP */ contract USDCBridge is CCIPReceiver, OwnerIsCreator { using SafeERC20 for IERC20; error USDCBridge__NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees); error USDCBridge__NothingToWithdraw(); error USDCBridge__DestinationChainNotAllowed(uint64 destinationChainSelector); error USDCBridge__SourceChainNotAllowed(uint64 sourceChainSelector); error USDCBridge__SenderNotAllowed(address sender); error USDCBridge__InvalidReceiverAddress(); event MessageSent( bytes32 indexed messageId, uint64 indexed destinationChainSelector, address receiver, uint256 amount, uint256 fees ); event MessageReceived( bytes32 indexed messageId, uint64 indexed sourceChainSelector, address sender, address recipient, uint256 amount ); IERC20 public immutable i_usdcToken; IERC20 public immutable i_linkToken; mapping(uint64 => bool) public allowlistedDestinationChains; mapping(uint64 => bool) public allowlistedSourceChains; mapping(address => bool) public allowlistedSenders; modifier onlyAllowlistedDestinationChain(uint64 _destinationChainSelector) { if (!allowlistedDestinationChains[_destinationChainSelector]) revert USDCBridge__DestinationChainNotAllowed(_destinationChainSelector); _; } modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) { if (!allowlistedSourceChains[_sourceChainSelector]) revert USDCBridge__SourceChainNotAllowed(_sourceChainSelector); if (!allowlistedSenders[_sender]) revert USDCBridge__SenderNotAllowed(_sender); _; } modifier validateReceiver(address _receiver) { if (_receiver == address(0)) revert USDCBridge__InvalidReceiverAddress(); _; } constructor(address _router, address _usdc, address _linkToken) CCIPReceiver(_router) { i_usdcToken = IERC20(_usdc); i_linkToken = IERC20(_linkToken); } /** * @notice Sends USDC with LINK token for fees * @param _destinationChainSelector The identifier for the destination blockchain * @param _receiver The address of the recipient on the destination blockchain * @param _amount The amount of USDC to transfer * @return messageId The ID of the CCIP message that was sent */ function sendUSDCPayLINK( uint64 _destinationChainSelector, address _receiver, uint256 _amount ) external onlyAllowlistedDestinationChain(_destinationChainSelector) validateReceiver(_receiver) returns (bytes32 messageId) { Client.EVM2AnyMessage memory message = _buildCCIPMessage(_receiver, _amount, address(i_linkToken)); // Initialize a router client instance to interact with cross-chain router IRouterClient router = IRouterClient(this.getRouter()); uint256 fees = router.getFee(_destinationChainSelector, message); if (fees > i_linkToken.balanceOf(address(this))) revert USDCBridge__NotEnoughBalance(i_linkToken.balanceOf(address(this)), fees); // Approve the Router to transfer USDC tokens on contract's behalf i_usdcToken.approve(address(router), _amount); // Approve the Router to transfer LINK tokens for fees i_linkToken.approve(address(router), fees); // Send the message through the router and store the returned message ID messageId = router.ccipSend(_destinationChainSelector, message); emit MessageSent( messageId, _destinationChainSelector, _receiver, _amount, fees ); return messageId; } /** * @notice Construct a CCIP message * @param _receiver The address of the receiver * @param _amount The amount of USDC to send * @param _feeTokenAddress The address of the token used for fees * @return Client.EVM2AnyMessage memory The constructed CCIP message */ function _buildCCIPMessage( address _receiver, uint256 _amount, address _feeTokenAddress ) private view returns (Client.EVM2AnyMessage memory) { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(i_usdcToken), amount: _amount }); return Client.EVM2AnyMessage({ receiver: abi.encode(_receiver), data: "", tokenAmounts: tokenAmounts, extraArgs: Client._argsToBytes( Client.EVMExtraArgsV1({gasLimit: 200_000}) ), feeToken: _feeTokenAddress }); } /** * @notice Handle received messages from other chains */ function _ccipReceive( Client.Any2EVMMessage memory any2EvmMessage ) internal override onlyAllowlisted( any2EvmMessage.sourceChainSelector, abi.decode(any2EvmMessage.sender, (address)) ) { bytes32 messageId = any2EvmMessage.messageId; uint64 sourceChainSelector = any2EvmMessage.sourceChainSelector; address sender = abi.decode(any2EvmMessage.sender, (address)); address originalSender = abi.decode(any2EvmMessage.data, (address)); address token = any2EvmMessage.destTokenAmounts[0].token; uint256 amount = any2EvmMessage.destTokenAmounts[0].amount; IERC20(token).safeTransfer(originalSender, amount); emit MessageReceived( messageId, sourceChainSelector, sender, originalSender, amount ); } /** * @notice Get estimated fees for sending USDC */ function getFee( uint64 _destinationChainSelector, address _receiver, uint256 _amount, address _feeToken ) external view returns (uint256 fee) { Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage( _receiver, _amount, _feeToken ); return IRouterClient(this.getRouter()).getFee(_destinationChainSelector, evm2AnyMessage); } receive() external payable {} /** * @notice Allows the contract owner to withdraw the entire balance of a specific ERC20 token */ function withdrawToken( address _beneficiary, address _token ) public onlyOwner { uint256 amount = IERC20(_token).balanceOf(address(this)); if (amount == 0) revert USDCBridge__NothingToWithdraw(); IERC20(_token).safeTransfer(_beneficiary, amount); } function allowlistDestinationChain( uint64 _destinationChainSelector, bool allowed ) external onlyOwner { allowlistedDestinationChains[_destinationChainSelector] = allowed; } function allowlistSourceChain( uint64 _sourceChainSelector, bool allowed ) external onlyOwner { allowlistedSourceChains[_sourceChainSelector] = allowed; } function allowlistSender(address _sender, bool allowed) external onlyOwner { allowlistedSenders[_sender] = allowed; } } 

What went wrong suddenly?

1 Answer 1

0

If Chainlink CCIP works on your custom token but not on USDC, the issue is likely access control or token approvals. CCIP-supported bridges may restrict USDC transfers to authorized contracts. Ensure the CCIP router is whitelisted and has sufficient allowance. Custom tokens lack such restrictions, so they work without extra permissions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.