diff --git a/.vscode/settings.json b/.vscode/settings.json index 1ab5639c2c8..59b141ad90e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,6 +36,7 @@ "IERC", "Linea", "Mainnets", + "Merkle", "MOONRIVER", "MOVR", "Numberish", @@ -55,6 +56,7 @@ "typechain", "WAVAX", "WBNB", + "whatsnext", "WMATIC", "XDAI" ] diff --git a/public/images/ccip/manual-execution-status.jpg b/public/images/ccip/manual-execution-status.jpg new file mode 100644 index 00000000000..88520981af1 Binary files /dev/null and b/public/images/ccip/manual-execution-status.jpg differ diff --git a/public/images/ccip/manual-execution.png b/public/images/ccip/manual-execution.png new file mode 100644 index 00000000000..f79d422001c Binary files /dev/null and b/public/images/ccip/manual-execution.png differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-confirmation.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-confirmation.jpg new file mode 100644 index 00000000000..b6ab6ce8ed7 Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-confirmation.jpg differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-success.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-success.jpg new file mode 100644 index 00000000000..0e7f0edf26b Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-success.jpg differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderl1.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderl1.jpg new file mode 100644 index 00000000000..bdec0482a9d Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderl1.jpg differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderly2.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderly2.jpg new file mode 100644 index 00000000000..f374d40e13e Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-manual-execution-tenderly2.jpg differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-low-gaslimit-tx-details.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-low-gaslimit-tx-details.jpg new file mode 100644 index 00000000000..d3262c27e68 Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-low-gaslimit-tx-details.jpg differ diff --git a/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-tx-details-ready-manual-execution.jpg b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-tx-details-ready-manual-execution.jpg new file mode 100644 index 00000000000..2c397b91db6 Binary files /dev/null and b/public/images/ccip/tutorials/ccip-explorer-send-tokens-message-pay-link-tx-details-ready-manual-execution.jpg differ diff --git a/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-failed.jpg b/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-failed.jpg new file mode 100644 index 00000000000..6622af5abfe Binary files /dev/null and b/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-failed.jpg differ diff --git a/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-manual-success.jpg b/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-manual-success.jpg new file mode 100644 index 00000000000..024f4a2a554 Binary files /dev/null and b/public/images/ccip/tutorials/mumbai-token-messagedetails-pay-link-manual-success.jpg differ diff --git a/public/images/ccip/tutorials/mumbai-token-override-gas-limit.jpg b/public/images/ccip/tutorials/mumbai-token-override-gas-limit.jpg new file mode 100644 index 00000000000..b15733e8582 Binary files /dev/null and b/public/images/ccip/tutorials/mumbai-token-override-gas-limit.jpg differ diff --git a/public/samples/CCIP/ProgrammableTokenTransfersLowGasLimit.sol b/public/samples/CCIP/ProgrammableTokenTransfersLowGasLimit.sol new file mode 100644 index 00000000000..c44effa7f4e --- /dev/null +++ b/public/samples/CCIP/ProgrammableTokenTransfersLowGasLimit.sol @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; +import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol"; +import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; +import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; +import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +/// @title - A simple messenger contract for transferring/receiving tokens and data across chains. +contract ProgrammableTokenTransfersLowGasLimit is CCIPReceiver, OwnerIsCreator { + // Custom errors to provide more descriptive revert messages. + error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees); // Used to make sure contract has enough balance to cover the fees. + error NothingToWithdraw(); // Used when trying to withdraw Ether but there's nothing to withdraw. + error DestinationChainNotAllowed(uint64 destinationChainSelector); // Used when the destination chain has not been allowlisted by the contract owner. + error SourceChainNotAllowed(uint64 sourceChainSelector); // Used when the source chain has not been allowlisted by the contract owner. + error SenderNotAllowed(address sender); // Used when the sender has not been allowlisted by the contract owner. + + // Event emitted when a message is sent to another chain. + event MessageSent( + bytes32 indexed messageId, // The unique ID of the CCIP message. + uint64 indexed destinationChainSelector, // The chain selector of the destination chain. + address receiver, // The address of the receiver on the destination chain. + string text, // The text being sent. + address token, // The token address that was transferred. + uint256 tokenAmount, // The token amount that was transferred. + address feeToken, // the token address used to pay CCIP fees. + uint256 fees // The fees paid for sending the message. + ); + + // Event emitted when a message is received from another chain. + event MessageReceived( + bytes32 indexed messageId, // The unique ID of the CCIP message. + uint64 indexed sourceChainSelector, // The chain selector of the source chain. + address sender, // The address of the sender from the source chain. + string text, // The text that was received. + address token, // The token address that was transferred. + uint256 tokenAmount // The token amount that was transferred. + ); + + bytes32 private s_lastReceivedMessageId; // Store the last received messageId. + address private s_lastReceivedTokenAddress; // Store the last received token address. + uint256 private s_lastReceivedTokenAmount; // Store the last received amount. + string private s_lastReceivedText; // Store the last received text. + + // Mapping to keep track of allowlisted destination chains. + mapping(uint64 => bool) public allowlistedDestinationChains; + + // Mapping to keep track of allowlisted source chains. + mapping(uint64 => bool) public allowlistedSourceChains; + + // Mapping to keep track of allowlisted senders. + mapping(address => bool) public allowlistedSenders; + + IERC20 private s_linkToken; + + /// @notice Constructor initializes the contract with the router address. + /// @param _router The address of the router contract. + /// @param _link The address of the link contract. + constructor(address _router, address _link) CCIPReceiver(_router) { + s_linkToken = IERC20(_link); + } + + /// @dev Modifier that checks if the chain with the given destinationChainSelector is allowlisted. + /// @param _destinationChainSelector The selector of the destination chain. + modifier onlyAllowlistedDestinationChain(uint64 _destinationChainSelector) { + if (!allowlistedDestinationChains[_destinationChainSelector]) + revert DestinationChainNotAllowed(_destinationChainSelector); + _; + } + + /// @dev Modifier that checks if the chain with the given sourceChainSelector is allowlisted and if the sender is allowlisted. + /// @param _sourceChainSelector The selector of the destination chain. + /// @param _sender The address of the sender. + modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) { + if (!allowlistedSourceChains[_sourceChainSelector]) + revert SourceChainNotAllowed(_sourceChainSelector); + if (!allowlistedSenders[_sender]) revert SenderNotAllowed(_sender); + _; + } + + /// @dev Updates the allowlist status of a destination chain for transactions. + /// @notice This function can only be called by the owner. + /// @param _destinationChainSelector The selector of the destination chain to be updated. + /// @param allowed The allowlist status to be set for the destination chain. + function allowlistDestinationChain( + uint64 _destinationChainSelector, + bool allowed + ) external onlyOwner { + allowlistedDestinationChains[_destinationChainSelector] = allowed; + } + + /// @dev Updates the allowlist status of a source chain + /// @notice This function can only be called by the owner. + /// @param _sourceChainSelector The selector of the source chain to be updated. + /// @param allowed The allowlist status to be set for the source chain. + function allowlistSourceChain( + uint64 _sourceChainSelector, + bool allowed + ) external onlyOwner { + allowlistedSourceChains[_sourceChainSelector] = allowed; + } + + /// @dev Updates the allowlist status of a sender for transactions. + /// @notice This function can only be called by the owner. + /// @param _sender The address of the sender to be updated. + /// @param allowed The allowlist status to be set for the sender. + function allowlistSender(address _sender, bool allowed) external onlyOwner { + allowlistedSenders[_sender] = allowed; + } + + /// @notice Sends data and transfer tokens to receiver on the destination chain. + /// @notice Pay for fees in LINK. + /// @notice the gasLimit is set to 20_000 on purpose to force the execution to fail on the destination chain + /// @dev Assumes your contract has sufficient LINK to pay for CCIP fees. + /// @param _destinationChainSelector The identifier (aka selector) for the destination blockchain. + /// @param _receiver The address of the recipient on the destination blockchain. + /// @param _text The string data to be sent. + /// @param _token token address. + /// @param _amount token amount. + /// @return messageId The ID of the CCIP message that was sent. + function sendMessagePayLINK( + uint64 _destinationChainSelector, + address _receiver, + string calldata _text, + address _token, + uint256 _amount + ) + external + onlyOwner + onlyAllowlistedDestinationChain(_destinationChainSelector) + returns (bytes32 messageId) + { + // Set the token amounts + Client.EVMTokenAmount[] + memory tokenAmounts = new Client.EVMTokenAmount[](1); + tokenAmounts[0] = Client.EVMTokenAmount({ + token: _token, + amount: _amount + }); + + // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message + // address(linkToken) means fees are paid in LINK + + Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({ + receiver: abi.encode(_receiver), // ABI-encoded receiver address + data: abi.encode(_text), // ABI-encoded string + tokenAmounts: tokenAmounts, // The amount and type of token being transferred + extraArgs: Client._argsToBytes( + // gasLimit set to 20_000 on purpose to force the execution to fail on the destination chain + Client.EVMExtraArgsV1({gasLimit: 20_000}) + ), + // Set the feeToken to a LINK token address + feeToken: address(s_linkToken) + }); + + // Initialize a router client instance to interact with cross-chain router + IRouterClient router = IRouterClient(this.getRouter()); + + // Get the fee required to send the CCIP message + uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage); + + if (fees > s_linkToken.balanceOf(address(this))) + revert NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees); + + // approve the Router to transfer LINK tokens on contract's behalf. It will spend the fees in LINK + s_linkToken.approve(address(router), fees); + + // approve the Router to spend tokens on contract's behalf. It will spend the amount of the given token + IERC20(_token).approve(address(router), _amount); + + // Send the message through the router and store the returned message ID + messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage); + + // Emit an event with message details + emit MessageSent( + messageId, + _destinationChainSelector, + _receiver, + _text, + _token, + _amount, + address(s_linkToken), + fees + ); + + // Return the message ID + return messageId; + } + + /** + * @notice Returns the details of the last CCIP received message. + * @dev This function retrieves the ID, text, token address, and token amount of the last received CCIP message. + * @return messageId The ID of the last received CCIP message. + * @return text The text of the last received CCIP message. + * @return tokenAddress The address of the token in the last CCIP received message. + * @return tokenAmount The amount of the token in the last CCIP received message. + */ + function getLastReceivedMessageDetails() + public + view + returns ( + bytes32 messageId, + string memory text, + address tokenAddress, + uint256 tokenAmount + ) + { + return ( + s_lastReceivedMessageId, + s_lastReceivedText, + s_lastReceivedTokenAddress, + s_lastReceivedTokenAmount + ); + } + + /// handle a received message + function _ccipReceive( + Client.Any2EVMMessage memory any2EvmMessage + ) + internal + override + onlyAllowlisted( + any2EvmMessage.sourceChainSelector, + abi.decode(any2EvmMessage.sender, (address)) + ) // Make sure source chain and sender are allowlisted + { + s_lastReceivedMessageId = any2EvmMessage.messageId; // fetch the messageId + s_lastReceivedText = abi.decode(any2EvmMessage.data, (string)); // abi-decoding of the sent text + // Expect one token to be transferred at once, but you can transfer several tokens. + s_lastReceivedTokenAddress = any2EvmMessage.destTokenAmounts[0].token; + s_lastReceivedTokenAmount = any2EvmMessage.destTokenAmounts[0].amount; + + emit MessageReceived( + any2EvmMessage.messageId, + any2EvmMessage.sourceChainSelector, // fetch the source chain identifier (aka selector) + abi.decode(any2EvmMessage.sender, (address)), // abi-decoding of the sender address, + abi.decode(any2EvmMessage.data, (string)), + any2EvmMessage.destTokenAmounts[0].token, + any2EvmMessage.destTokenAmounts[0].amount + ); + } + + /// @notice Allows the owner of the contract to withdraw all tokens of a specific ERC20 token. + /// @dev This function reverts with a 'NothingToWithdraw' error if there are no tokens to withdraw. + /// @param _beneficiary The address to which the tokens will be sent. + /// @param _token The contract address of the ERC20 token to be withdrawn. + function withdrawToken( + address _beneficiary, + address _token + ) public onlyOwner { + // Retrieve the balance of this contract + uint256 amount = IERC20(_token).balanceOf(address(this)); + + // Revert if there is nothing to withdraw + if (amount == 0) revert NothingToWithdraw(); + + IERC20(_token).transfer(_beneficiary, amount); + } +} diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index cb1ce74799a..e10c757e6c9 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -885,6 +885,10 @@ export const SIDEBAR: Partial> = { title: "Send Arbitrary Data", url: "ccip/tutorials/send-arbitrary-data", }, + { + title: "Manual Execution", + url: "ccip/tutorials/manual-execution", + }, { title: "Acquire Test Tokens", url: "ccip/test-tokens", @@ -895,13 +899,17 @@ export const SIDEBAR: Partial> = { section: "Concepts", contents: [ { - title: "Concept Overview", + title: "Conceptual Overview", url: "ccip/concepts", }, { title: "Architecture", url: "ccip/architecture", }, + { + title: "Manual execution", + url: "ccip/concepts/manual-execution", + }, { title: "Best Practices", url: "ccip/best-practices", diff --git a/src/content/ccip/architecture.mdx b/src/content/ccip/architecture.mdx index 4b2f3b9e8fa..8705c907cc7 100644 --- a/src/content/ccip/architecture.mdx +++ b/src/content/ccip/architecture.mdx @@ -4,6 +4,7 @@ date: Last Modified title: "CCIP Architecture" whatsnext: { + "CCIP manual execution": "/ccip/concepts/manual-execution", "Learn CCIP best practices": "/ccip/best-practices", "Find the list of supported networks, lanes, and rate limits on the CCIP Supported Networks page": "/ccip/supported-networks", } @@ -169,4 +170,4 @@ Chainlink CCIP has been purposely designed to take a security-first approach to If the fee paid on the source chain is within an acceptable range of the execution cost on the destination chain, the message will be transmitted as soon as possible after it is blessed by the Risk Management Network. If the cost of execution increases between request and execution time, CCIP incrementally increases the gas price to attempt to reach eventual execution, but this might introduce additional latency. -Execution delays in excess of one hour should be rare as a result of _Smart Execution_. The `permissionlessExecutionThresholdSeconds` parameter represents the waiting time before manual execution is enabled. If the DON fails to execute within the duration of `permissionlessExecutionThresholdSeconds` due to extreme network conditions, you can manually execute the message through the [CCIP Explorer](https://ccip.chain.link/). +Execution delays in excess of one hour should be rare as a result of _Smart Execution_. The Smart Execution time window parameter represents the waiting time before manual execution is enabled. If the DON fails to execute within the duration of Smart Execution time window due to extreme network conditions, you can manually execute the message through the [CCIP Explorer](https://ccip.chain.link/). Read the [manual execution](/ccip/concepts/manual-execution) conceptual guide to learn more. diff --git a/src/content/ccip/best-practices.mdx b/src/content/ccip/best-practices.mdx index 25684a0ed46..02720495f13 100644 --- a/src/content/ccip/best-practices.mdx +++ b/src/content/ccip/best-practices.mdx @@ -53,6 +53,10 @@ The purpose of `extraArgs` is to allow compatibility with future CCIP upgrades. If `extraArgs` are left empty, a default of _200000_ `gasLimit` will be set. +## Decoupling CCIP Message Reception and Business Logic + +As a best practice, separate the reception of CCIP messages from the core business logic of the contract. Implement 'escape hatches' or fallback mechanisms to gracefully manage situations where the business logic encounters issues. To explore this concept further, refer to the [Defensive Example](/ccip/tutorials/programmable-token-transfers-defensive) guide. + ## Evaluate the security and reliability of the networks that you use Although CCIP has been thoroughly reviewed and audited, inherent risks might still exist based on your use case, the blockchain networks where you deploy your contracts, and the network conditions on those blockchains. diff --git a/src/content/ccip/concepts.mdx b/src/content/ccip/concepts/index.mdx similarity index 100% rename from src/content/ccip/concepts.mdx rename to src/content/ccip/concepts/index.mdx diff --git a/src/content/ccip/concepts/manual-execution.mdx b/src/content/ccip/concepts/manual-execution.mdx new file mode 100644 index 00000000000..8795a8efa60 --- /dev/null +++ b/src/content/ccip/concepts/manual-execution.mdx @@ -0,0 +1,114 @@ +--- +section: ccip +date: Last Modified +title: "CCIP Manual Execution" +whatsnext: + { "Manual execution guide": "/ccip/tutorials/manual-execution", "Learn CCIP best practices": "/ccip/best-practices" } +--- + +import { Aside, ClickToZoom, CopyText } from "@components" + + + +In general, messages are successfully delivered and processed via CCIP as described in the [Architecture](/ccip/architecture) page. CCIP has a built-in gas bumping mechanism called _Smart Execution_ to ensure that messages are reliably delivered to the destination blockchain. Read the [CCIP Execution latency](/ccip/architecture#ccip-execution-latency) section to learn more about _Smart Execution_. However, some exceptional conditions might require users to manually execute the transaction on the destination blockchain: + + - The receiver contract on the destination blockchain reverted due to an unhandled exception such as a logical error. + - The receiver contract on the destination blockchain reverted due to the gas limit being insufficient to execute the triggered function (Note: The gas limit value is set in the [extraArgs](/ccip/api-reference/client#evmextraargsv1) param of the message). + - The message could not be executed on the destination chain within CCIP’s Smart Execution time window, which is currently set to 8 hours. This could happen, for example, during extreme network congestion and resulting gas spikes. + +The flowchart below displays the process of a cross-chain transaction, detailing the steps involved in the manual execution: + +
+ + + +## CCIP execution + +1. A sender contract or EOA initiates a CCIP message on the source blockchain. +1. [CCIP Committing DON](/ccip/architecture#committing-don) awaits [finality](/ccip/concepts#finality) on the source blockchain. +1. Post finality, the [CCIP Committing DON](/ccip/architecture#committing-don) assembles a batch of eligible transactions, computes a Merkle root, and records it to the [CommitStore contract](/ccip/architecture#commit-store) on the destination blockchain. +1. After successful verification, the [Risk Management Network](/ccip/concepts#risk-management-network) blesses the committed Merkle root. +1. After the committed Merkle root is blessed, the [CCIP Executing DON](/ccip/architecture#executing-don) proceeds with the execution on the destination blockchain: + - During the Smart Execution time window, the Executing DON assesses and, if necessary, adjusts the gas price (a process known as gas bumping) to facilitate successful transaction execution on the destination blockchain as part of the Smart Execution process. + - If the Smart Execution time window has passed and the CCIP message has still not been executed, then Manual Execution is enabled. See the [Manual execution](/ccip/concepts/manual-execution#manual-execution) section for more details. +1. The execution on the destination blockchain works as follows: + + 1. If the message involves token transfers, the tokens are first transferred to the receiver. + + - If the receiver is an EOA, then this transaction is considered complete with no further processing. + - If the receiver is a smart contract, the [ccipReceive](/ccip/api-reference/ccip-receiver#ccipreceive) function is invoked after the token transfer. The ccipReceive function processes the CCIP message and any user-specified logic in the receiver contract. The execution of the CCIP message is atomic (all or none). If the ccipReceive function successfully executes, then all aspects of the transaction are complete, and there is no revert. If, however, there is an issue in the receiver execution due to insufficient gas limit or unhandled exceptions, the attempted token transfer will also revert. The transaction then becomes eligible for manual execution. + + 1. If the message does not involve token transfers, only arbitrary messaging, and the receiver execution fails due to gas limits or unhandled exceptions, the transaction becomes eligible for manual execution. + +## Manual execution + +As described above, a CCIP message becomes eligible for manual execution for various reasons. Manual execution means that a user has to manually trigger the execution of the destination transaction after the issue that caused manual execution has been resolved. + +When a CCIP message is eligible for manual execution, the [CCIP explorer](https://ccip.chain.link/) shows the following information: + + - _Ready for manual execution_ status + - The possibility to override the gas limit and a _Trigger Manual Execution_ button + +
+ + + +Depending on the situation, you can take one of the following steps: + +- Insufficient gas limit: The executor can connect their wallet, override the gas limit parameter, increase the gas limit for this particular execution, and trigger a manual execution. If this new gas limit override is sufficient, the transaction will go through successfully. **Note:** This gas limit override applies only to this manually executed transaction. +- Unhandled exception (logical error) in the receiver contract: If the receiver contract is [upgradeable](https://blog.chain.link/upgradable-smart-contracts/), developers must correct the logic, re-deploy the logic contract, and then manually execute the same transaction. If the receiver contract is not upgradeable, developers must deploy a new receiver contract, and then users can send a new CCIP message. Non-upgradable contracts will not benefit from manual execution. **Note:** Always make sure to test your smart contracts thoroughly. As a best practice, implement fallback mechanisms in the CCIP receiver contracts to manage unhandled exceptions gracefully. Read the [Defensive example](/ccip/tutorials/programmable-token-transfers-defensive) to learn more. +- If the issue was due to extreme gas spikes or network conditions, and CCIP was not able to successfully transfer the message despite gas bumping for the entire duration of the Smart Execution time window (currently 8 hours), then you can try manual execution. + +When manual execution is initiated, a Merkle proof is generated for the message to be executed. During execution, the CCIP explorer submits the Merkle proof and the new gas limit (if the initial failure was due to a low gas limit). This Merkle proof is verified by the [OffRamp contract](/ccip/architecture#offramp) against the Merkle root in the [CommitStore contract](/ccip/architecture#commit-store), and that was blessed by the [Risk Management Network](/ccip/architecture#risk-management-network). This mirrors the automated execution performed by the [CCIP Executing DON](/ccip/architecture#executing-don), with the addition that the execution is resubmitted using the gas limit you provide. + +## Frequently Asked Questions + +1. **Can anyone execute a transaction on the CCIP explorer even if they are not the initiator of the transaction?** + + Yes, any EOA can manually execute a CCIP message that is eligible for manual execution. However, the executing account must have sufficient native gas tokens (such as ETH on Ethereum or MATIC on Polygon) to cover the gas costs associated with the delivery of the CCIP message. + +
+ +1. **If a user sends multiple messages and the first message isn’t successfully delivered and goes into a _manual execution_ mode, does that mean all subsequent messages from the user will also be stuck?** + + It depends. If a message goes into manual execution mode due to receiver errors (unhandled exceptions or gas limit issues), subsequent messages don’t get automatically blocked, unless they would encounter the same error. However, suppose a message goes into manual execution mode after the Smart Execution time window expires (currently 8 hours). In that case, subsequent messages must wait for the first message to be processed to maintain the default sequence. + +
+ +1. **If the maximum gas limit is 2M (2,000,000) on mainnet, but it turns out that the destination blockchain requires more than that, will an override of > 2M still work?** + + Yes, but only for this execution. This works because the gas limit for this execution instance isn’t passing through the CCIP validation for the gas limit, for which the CCIP executing DON pays the gas. However, if you consistently need more than 2M for your use case, please reach out to us via this [contact form](https://chain.link/contact?v=Build%20Cross-Chain%20Applications). + +
+ +1. **Will Chainlink Labs reimburse us for manual execution fees?** + + Since most manual execution situations are due to insufficient gas limit or an unhandled exception in the receiver contract, Chainlink Labs does not reimburse these fees. If you are a dApp developer, please ensure you test thoroughly to avoid manual executions to the extent possible. + +
+ +1. **Do I have to manually execute via the CCIP explorer? Are there any other ways to do this?** + + The CCIP explorer provides the easiest way to execute manually. It handles all the complexity of submitting the Merkle proof needed for successful transaction execution. + +
+ +1. **How do I know if my receiver error is due to a gas limit issue or an unhandled exception?** + + If you see a _ReceiverError_ with a revert reason as empty (0x), this is likely due to a gas limit issue. You can look at the transaction trace for the destination transaction on a tool such as [Tenderly](https://tenderly.co/), and you will likely see an _out of gas_ reason mentioned in such cases. Determine the gas limit that would work for your transaction and manually override it. Read the [manual execution](/ccip/tutorials/manual-execution) tutorial to analyze an example of an exception due to a low gas limit. + +
+ +1. **How can I write contracts that avoid manual execution situations in the first place?** + + - Test thoroughly to ensure logical conditions for all paths are gracefully handled in your receiver contract. + - Set a gas limit that works for complex execution paths, not just the simplest ones. Read the [best practices](/ccip/best-practices#setting-gaslimit) for gas estimation. + - Refer to the [Defensive example](/ccip/tutorials/programmable-token-transfers-defensive) tutorial for an example of how to design a programmable token transfer that handles errors gracefully. +
+ +1. **My transaction meets one of the conditions to trigger manual execution, but I do not see this option on the CCIP explorer. How am I supposed to execute this manually?** + + This should not happen, but in the unlikely scenario that this does happen, please reach out directly to Chainlink Labs by filling out this [form](https://chainlinkcommunity.typeform.com/ccip-explorer). Make sure to include the CCIP Message ID, your preferred contact details, and the issue you faced in your communication. diff --git a/src/content/ccip/getting-started.mdx b/src/content/ccip/getting-started.mdx index 0388a55c41b..b7b5df5490b 100644 --- a/src/content/ccip/getting-started.mdx +++ b/src/content/ccip/getting-started.mdx @@ -43,7 +43,7 @@ Deploy the `Sender.sol` contract on _Ethereum Sepolia_. To see a detailed explan 1. Open MetaMask and select the _Ethereum Sepolia_ network. 1. In Remix under the **Deploy & Run Transactions** tab, select _Injected Provider - MetaMask_ in the **Environment** list. Remix will use the MetaMask wallet to communicate with _Ethereum Sepolia_. - 1. Under the **Deploy** section, fill in the router address and the LINK token contract addresses for your specific blockchain. You can find both of these addresses on the [Supported Networks](/ccip/supported-networks) page. The LINK token contract address is also listed on the [LINK Token Contracts](/resources/link-token-contracts) page. For _Ethereum Sepolia_, the router address is and the LINK address is . + 1. Under the **Deploy** section, fill in the router address and the LINK token contract addresses for your specific blockchain. You can find both of these addresses on the [Supported Networks](/ccip/supported-networks) page. The LINK token contract address is also listed on the [LINK Token Contracts](/resources/link-token-contracts) page. For _Ethereum Sepolia_, the router address is and the LINK address is . @@ -79,7 +79,7 @@ Deploy the receiver contract on _Polygon Mumbai_. You will use this contract to 1. Open MetaMask and select the _Polygon Mumbai_ network. 1. In Remix under the **Deploy & Run Transactions** tab, make sure the **Environment** is still set to _Injected Provider - MetaMask_. - 1. Under the **Deploy** section, fill in the router address field. For _Polygon Mumbai_, the Router address is . You can find the addresses for each network on the [Supported Networks](/ccip/supported-networks) page. + 1. Under the **Deploy** section, fill in the router address field. For _Polygon Mumbai_, the Router address is . You can find the addresses for each network on the [Supported Networks](/ccip/supported-networks) page. + If you consistently need more than 2M for your use case, please reach out to your Chainlink Labs point of contact or + contact us via this [contact form](https://chain.link/contact?v=Build%20Cross-Chain%20Applications). + + +| Item | Description | Limit | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------- | +| Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/client#evm2anymessage) | 30 kilobytes | +| Message Gas Limit | User specified [gas limit](/ccip/api-reference/client#evmextraargsv1) | 2,000,000 | +| Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | +| Smart Execution time window | Maximum duration for the execution of a [CCIP message](/ccip/api-reference/client#evm2anymessage) | 8 hours | ### Version 1.0.0 @@ -38,18 +44,20 @@ import { Aside } from "@components" [release notes](/ccip/release-notes) for a comprehensive overview of the enhancements and new features in v1.2.0. -| Item | Description | Limit | -| -------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------- | -| Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/client#evm2anymessage) | 30 kilobytes | -| Message Gas Limit | User specified [gas limit](/ccip/api-reference/client#evmextraargsv1) | 2,000,000 | -| Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | +| Item | Description | Limit | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------- | +| Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/client#evm2anymessage) | 30 kilobytes | +| Message Gas Limit | User specified [gas limit](/ccip/api-reference/client#evmextraargsv1) | 2,000,000 | +| Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | +| Smart Execution time window | Maximum duration for the execution of a [CCIP message](/ccip/api-reference/client#evm2anymessage) | 8 hours | ## Testnet ### Version 1.2.0 -| Item | Description | Limit | -| -------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------- | -| Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/client#evm2anymessage) | 30 kilobytes | -| Message Gas Limit | User specified [gas limit](/ccip/api-reference/client#evmextraargsv1) | 3,000,000 | -| Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | +| Item | Description | Limit | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------- | +| Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/client#evm2anymessage) | 30 kilobytes | +| Message Gas Limit | User specified [gas limit](/ccip/api-reference/client#evmextraargsv1) | 3,000,000 | +| Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | +| Smart Execution timeframe | Maximum duration for the execution of a [CCIP message](/ccip/api-reference/client#evm2anymessage) | 8 hours | diff --git a/src/content/ccip/tutorials/cross-chain-tokens-from-eoa.mdx b/src/content/ccip/tutorials/cross-chain-tokens-from-eoa.mdx index be77352738c..9aa59f5cd62 100644 --- a/src/content/ccip/tutorials/cross-chain-tokens-from-eoa.mdx +++ b/src/content/ccip/tutorials/cross-chain-tokens-from-eoa.mdx @@ -61,7 +61,7 @@ In this tutorial, you will use Chainlink CCIP to transfer tokens directly from y npm install ``` -1. For higher security, the starter kit imports [@chainlink/env-enc](https://www.npmjs.com/package/@chainlink/env-enc). Use this tool to encrypt your environment variables at rest. +1. For higher security, the examples repository imports [@chainlink/env-enc](https://www.npmjs.com/package/@chainlink/env-enc). Use this tool to encrypt your environment variables at rest. 1. Set an encryption password for your environment variables. @@ -122,8 +122,8 @@ Complete the following steps in your terminal: ``` $ node src/transfer-tokens.js ethereumSepolia polygonMumbai 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000 0x779877A7B0D9E8603169DdbD7836e478b4624789 Estimated fees (wei): 45004872518799270 - approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x5216b9c85d576a6d97c80edaadd890f2a7ae0132e54e7bda9a102374de3c3003 - approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend fees 45004872518799270 of token 0x779877A7B0D9E8603169DdbD7836e478b4624789. Transaction: 0x63ca647e8fd8af177eea85ea02f18e6ece00f7c0e7d7917b050221141cd47a72 + approved router 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x5216b9c85d576a6d97c80edaadd890f2a7ae0132e54e7bda9a102374de3c3003 + approved router 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 to spend fees 45004872518799270 of token 0x779877A7B0D9E8603169DdbD7836e478b4624789. Transaction: 0x63ca647e8fd8af177eea85ea02f18e6ece00f7c0e7d7917b050221141cd47a72 ✅ 1000000000000000 of Tokens(0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain polygonMumbai using CCIP. Transaction hash 0x7dda59032f1f157a830755264907139602f3fee73666938e12d17c26923c9e07 - Message id is 0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2 @@ -200,7 +200,7 @@ Complete the following steps in your terminal: ``` $ node src/transfer-tokens.js ethereumSepolia polygonMumbai 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000 Estimated fees (wei): 278051815072596 - approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x85d91b16c08546ad5e8e7791cfe4ebf59cc986f805244230872487fe8ad6862d + approved router 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x85d91b16c08546ad5e8e7791cfe4ebf59cc986f805244230872487fe8ad6862d ✅ 1000000000000000 of Tokens(0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain polygonMumbai using CCIP. Transaction hash 0x358c2abccd75c52aeabf5b330fda2b17a4c29bfc2612fc85a8619d22c3d1be00 - Message id is 0x1dc9f4d0e798802b19668a26b59681b97c02ad16bef3765eae6c5232f1d2e050 diff --git a/src/content/ccip/tutorials/cross-chain-tokens.mdx b/src/content/ccip/tutorials/cross-chain-tokens.mdx index 34a542c07f8..fafc1ad26f6 100644 --- a/src/content/ccip/tutorials/cross-chain-tokens.mdx +++ b/src/content/ccip/tutorials/cross-chain-tokens.mdx @@ -53,7 +53,7 @@ To use this contract: 1. Open MetaMask and select the network _Ethereum Sepolia_. 1. In Remix IDE, click _Deploy & Run Transactions_ and select _Injected Provider - MetaMask_ from the environment list. Remix will then interact with your MetaMask wallet to communicate with _Ethereum Sepolia_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer _CCIP-BnM_ to your contract. diff --git a/src/content/ccip/tutorials/manual-execution.mdx b/src/content/ccip/tutorials/manual-execution.mdx new file mode 100644 index 00000000000..2193e5cf26d --- /dev/null +++ b/src/content/ccip/tutorials/manual-execution.mdx @@ -0,0 +1,234 @@ +--- +section: ccip +date: Last Modified +title: "Manual Execution" +whatsnext: + { + "Learn how to handle errors gracefully when making CCIP transactions": "/ccip/tutorials/programmable-token-transfers-defensive", + "See example cross-chain dApps and tools": "/ccip/examples", + "See the list of supported networks": "/ccip/supported-networks", + "Learn CCIP best practices": "/ccip/best-practices", + } +--- + +import { CodeSample, ClickToZoom, CopyText, Aside } from "@components" + + + +This tutorial is similar to the [programmable token transfers example](/ccip/tutorials/programmable-token-transfers). It demonstrates the use of Chainlink CCIP for transferring tokens and arbitrary data between smart contracts on different blockchains. A distinctive feature of this tutorial is that we intentionally set a very low gas limit when using CCIP to send our message. This low gas limit is designed to cause the execution on the destination chain to fail, providing an opportunity to demonstrate the manual execution feature. Here's how you will proceed: + +1. Initiate a Transfer: You'll transfer tokens and arbitrary data from your source contract on Avalanche Fuji to a receiver contract on Polygon Mumbai. You will notice that the CCIP message has a very low gas limit, causing the execution on the receiver contract to fail. +1. Failure of CCIP Message Delivery: Once the transaction is finalized on the source chain (Avalanche Fuji), CCIP will deliver your message to the receiver contract on the destination chain (Polygon Mumbai). You can follow the progress of your transaction using the [CCIP explorer](https://ccip.chain.link/). Here, you'll observe that the execution on the receiver contract failed due to the low gas limit. +1. Manual Execution via CCIP Explorer: Using the [CCIP explorer](https://ccip.chain.link/), you will override the previously set gas limit and retry the execution. This process is referred to as _manual execution_. +1. Confirm Successful Execution: After manually executing the transaction with an adequate gas limit, you'll see that the status of your CCIP message is updated to successful. This indicates that the tokens and data were correctly transferred to the receiver contract. + +## Before you begin + +1. You should understand how to write, compile, deploy, and fund a smart contract. If you need to brush up on the basics, read this [tutorial](/quickstarts/deploy-your-first-contract), which will guide you through using the [Solidity programming language](https://soliditylang.org/), interacting with the [MetaMask wallet](https://metamask.io) and working within the [Remix Development Environment](https://remix.ethereum.org/). +1. Your account must have some ETH and LINK tokens on _Avalanche Fuji_ and MATIC tokens on _Polygon Mumbai_. Learn how to [Acquire testnet LINK](/resources/acquire-link). +1. Check the [Supported Networks page](/ccip/supported-networks) to confirm that the tokens you will transfer are supported for your lane. In this example, you will transfer tokens from _Avalanche Fuji_ to _Polygon Mumbai_ so check the list of supported tokens [here](/ccip/supported-networks/v1_2_0/testnet#avalanche-fuji-polygon-mumbai). +1. Learn how to [acquire CCIP test tokens](/ccip/test-tokens#mint-test-tokens). Following this guide, you should have CCIP-BnM tokens, and CCIP-BnM should appear in the list of your tokens in MetaMask. +1. Learn how to [fund your contract](/resources/fund-your-contract). This guide shows how to fund your contract in LINK, but you can use the same guide for funding your contract with any ERC20 tokens as long as they appear in the list of tokens in MetaMask. +1. Follow the previous tutorial: [_Transfer Tokens with Data_](/ccip/tutorials/programmable-token-transfers) to learn how to make programmable token transfers using CCIP. +1. Create a free account on [tenderly](https://tenderly.co/). You will use tenderly to investigate the failed execution of the receiver contract. + +## Tutorial + +In this tutorial, you'll send a text _string_ and CCIP-BnM tokens between smart contracts on _Avalanche Fuji_ and _Polygon Mumbai_ using CCIP and pay transaction fees in LINK. The tutorial demonstrates setting a deliberately low gas limit in the CCIP message, causing initial execution failure on the receiver contract. You will then: + +1. Use the [CCIP explorer](https://ccip.chain.link/) to increase the gas limit. +1. Manually retry the execution. +1. Observe successful execution after the gas limit adjustment. + + + +### Deploy your contracts + +To use this contract: + +1. [Open the contract in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/CCIP/ProgrammableTokenTransfersLowGasLimit.sol). + +1. Compile your contract. +1. Deploy, fund your sender contract on _Avalanche Fuji_ and enable sending messages to _Polygon Mumbai_: + + 1. Open MetaMask and select the network _Avalanche Fuji_. + 1. In Remix IDE, click on _Deploy & Run Transactions_ and select _Injected Provider - MetaMask_ from the environment list. Remix will then interact with your MetaMask wallet to communicate with _Avalanche Fuji_. + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Avalanche Fuji_, the router address is and the LINK contract address is . + 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. + Note your contract address. + 1. Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer _CCIP-BnM_ to your contract. + 1. Open MetaMask and fund your contract with LINK tokens. You can transfer _LINK_ to your contract. In this example, LINK is used to pay the CCIP fees. + 1. Enable your contract to send CCIP messages to _Polygon Mumbai_: + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Avalanche Fuji_. + 1. Call the `allowlistDestinationChain` with as the destination chain selector, and as allowed. Each chain selector is found on the [supported networks page](/ccip/supported-networks). + +1. Deploy your receiver contract on _Polygon Mumbai_ and enable receiving messages from your sender contract: + + 1. Open MetaMask and select the network _Polygon Mumbai_. + 1. In Remix IDE, under _Deploy & Run Transactions_, make sure the environment is still _Injected Provider - MetaMask_. + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . + 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. + Note your contract address. + 1. Enable your contract to receive CCIP messages from _Avalanche Fuji_: + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Polygon Mumbai_. + 1. Call the `allowlistSourceChain` with as the source chain selector, and as allowed. Each chain selector is found on the [supported networks page](/ccip/supported-networks). + 1. Enable your contract to receive CCIP messages from the contract that you deployed on _Avalanche Fuji_: + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Polygon Mumbai_. + 1. Call the `allowlistSender` with the contract address of the contract that you deployed on _Avalanche Fuji_, and as allowed. + +At this point, you have one _sender_ contract on _Avalanche Fuji_ and one _receiver_ contract on _Polygon Mumbai_. As security measures, you enabled the sender contract to send CCIP messages to _Polygon Mumbai_ and the receiver contract to receive CCIP messages from the sender and _Avalanche Fuji_. + +### Transfer and Receive tokens and data and pay in LINK + +You will transfer _0.001 CCIP-BnM_ and a text. The CCIP fees for using CCIP will be paid in LINK. + +1. Send a string data with tokens from _Avalanche Fuji_: + + 1. Open MetaMask and select the network _Avalanche Fuji_. + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Avalanche Fuji_. + 1. Fill in the arguments of the _**sendMessagePayLINK**_ function: + +
+ + | Argument | Value and Description | + | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | \_destinationChainSelector |
CCIP Chain identifier of the destination blockchain (_Polygon Mumbai_ in this example). You can find each chain selector on the [supported networks page](/ccip/supported-networks). | + | \_receiver | Your receiver contract address at _Polygon Mumbai_.
The destination contract address. | + | \_text |
Any `string` | + | \_token |
The _CCIP-BnM_ contract address at the source chain (_Avalanche Fuji_ in this example). You can find all the addresses for each supported blockchain on the [supported networks page](/ccip/supported-networks). | + | \_amount |
The token amount (_0.001 CCIP-BnM_). | + + 1. Click on `transact` and confirm the transaction on MetaMask. + 1. After the transaction is successful, record the transaction hash. Here is an [example](https://testnet.snowtrace.io/tx/0xec6f53071380a6cf1ddad4aa8197647171c280e352fc4a44d396b667ef77bdf9) of a transaction on _Avalanche Fuji_. + + + +1. Open the [CCIP explorer](https://ccip.chain.link/) and search your cross-chain transaction using the transaction hash. Note that the _Gas Limit_ is _20000_. In this example, the CCIP message ID is _0x21c3b177dd118a7347e744e0ac64cea69ce85d0a207e5a14b74867b1f911622a_. + +
+ + + +1. After a few minutes, the status will be updated to _Ready for manual execution_ indicating that CCIP could not successfully deliver the message due to the initial low gas limit. At this stage, you have the option to override the gas limit. + +
+ + + +1. You can also confirm that the CCIP message was not delivered to the receiver contract on the destination chain: + + 1. Open MetaMask and select the network _Polygon Mumbai_. + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Polygon Mumbai_. + 1. Call the `getLastReceivedMessageDetails` function. + +
+ + + + 1. Observe that the returned data is empty: the received messageId is _0x0000000000000000000000000000000000000000000000000000000000000000_, indicating no message was received. Additionally, the received text field is empty, the token address is the default _0x0000000000000000000000000000000000000000_, and the token amount shows as _0_. + +### Manual execution + +#### Investigate the root cause of receiver contract execution failure + +To determine if a low gas limit is causing the failure in the receiver contract's execution, consider the following methods: + +- Error analysis: Examine the error description in the CCIP explorer. An error labeled _ReceiverError. This may be due to an out of gas error on the destination chain. Error code: 0x_, often indicates a low gas issue + +- Advanced Investigation Tool: For a comprehensive analysis, employ a sophisticated tool like [tenderly](https://tenderly.co/). Tenderly can provide detailed insights into the transaction processes, helping to pinpoint the exact cause of the failure. + +To use [tenderly](https://tenderly.co/): + +1. Copy the destination transaction hash from the CCIP explorer. In this example, the destination transaction hash is _0x06cb1c7d92483e67382a932e99411c4525e2c3aca6e46498c2ba64bf7eb08aba_. +1. Open tenderly and search for your transaction. You should see an interface similar to the following: + +
+ + + +1. Enable _Full Trace_ then click on _Reverts_. + + + +1. Notice the _out of gas_ error in the receiver contract. In this example, the receiver contract is _0x4314123b4E8739f5cb1eE176C33Bd45f8573c41C_. + +#### Trigger manual execution + +You will increase the gas limit and trigger manual execution: + +1. In the [CCIP explorer](https://ccip.chain.link/), set the _Gas limit override_ to then click on _Trigger Manual Execution_. + +
+ + + +1. After you confirm the transaction on Metamask, the CCIP explorer shows you a confirmation screen. + +
+ + + +1. Click on the _Close_ button and observe the status marked as _Success_. + +
+ + + +1. Check the receiver contract on the destination chain: + + 1. Open MetaMask and select the network _Polygon Mumbai_. + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Polygon Mumbai_. + 1. Call the `getLastReceivedMessageDetails` function. + +
+ + + + 1. Notice the received messageId is _0x21c3b177dd118a7347e744e0ac64cea69ce85d0a207e5a14b74867b1f911622a_, the received text is _Hello World!_, the token address is _0xf1E3A5842EeEF51F2967b3F05D45DD4f4205FF40_ (CCIP-BnM token address on _Polygon Mumbai_) and the token amount is 1000000000000000 (0.001 CCIP-BnM). + +**Note**: These example contracts are designed to work bi-directionally. As an exercise, you can use them to transfer tokens and data from _Avalanche Fuji_ to _Polygon Mumbai_ and from _Polygon Mumbai_ back to _Avalanche Fuji_. + +## Explanation + +The smart contract used in this tutorial is configured to use CCIP for transferring and receiving tokens with data, similar to the contract in the [_Transfer Tokens with Data_](/ccip/tutorials/programmable-token-transfers) tutorial. For a detailed understanding of the contract code, refer to the [code explanation](/ccip/tutorials/programmable-token-transfers#explanation) section of that tutorial. + +A key distinction in this tutorial is the intentional setup of a low gas limit of `20,000` for building the CCIP message. This specific gas limit setting is expected to fail the message delivery on the receiver contract in the destination chain: + +```solidity +Client._argsToBytes( + Client.EVMExtraArgsV1({gasLimit: 20_000}) +) +``` diff --git a/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx b/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx index 8559f25ba9e..e95cbc3e754 100644 --- a/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx +++ b/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx @@ -54,7 +54,7 @@ To use this contract: 1. Open MetaMask and select the network _Ethereum Sepolia_. 1. In Remix IDE, click on _Deploy & Run Transactions_ and select _Injected Provider - MetaMask_ from the environment list. Remix will then interact with your MetaMask wallet to communicate with _Ethereum Sepolia_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer _CCIP-BnM_ to your contract. @@ -66,7 +66,7 @@ To use this contract: 1. Open MetaMask and select the network _Polygon Mumbai_. 1. In Remix IDE, under _Deploy & Run Transactions_, make sure the environment is still _Injected Provider - MetaMask_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Enable your contract to receive CCIP messages from _Ethereum Sepolia_: diff --git a/src/content/ccip/tutorials/programmable-token-transfers.mdx b/src/content/ccip/tutorials/programmable-token-transfers.mdx index 4afc253db1f..dbb82e50d61 100644 --- a/src/content/ccip/tutorials/programmable-token-transfers.mdx +++ b/src/content/ccip/tutorials/programmable-token-transfers.mdx @@ -4,6 +4,7 @@ date: Last Modified title: "Transfer Tokens with Data" whatsnext: { + "Learn how to manually execute a failed CCIP transaction": "/ccip/tutorials/manual-execution", "Learn how to handle errors gracefully when making CCIP transactions": "/ccip/tutorials/programmable-token-transfers-defensive", "Transfer Tokens Between EOAs": "/ccip/tutorials/cross-chain-tokens-from-eoa", "See example cross-chain dApps and tools": "/ccip/examples", @@ -54,7 +55,7 @@ To use this contract: 1. Open MetaMask and select the network _Ethereum Sepolia_. 1. In Remix IDE, click on _Deploy & Run Transactions_ and select _Injected Provider - MetaMask_ from the environment list. Remix will then interact with your MetaMask wallet to communicate with _Ethereum Sepolia_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Ethereum Sepolia_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer _CCIP-BnM_ to your contract. @@ -66,7 +67,7 @@ To use this contract: 1. Open MetaMask and select the network _Polygon Mumbai_. 1. In Remix IDE, under _Deploy & Run Transactions_, make sure the environment is still _Injected Provider - MetaMask_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Enable your contract to receive CCIP messages from _Ethereum Sepolia_: diff --git a/src/content/ccip/tutorials/send-arbitrary-data.mdx b/src/content/ccip/tutorials/send-arbitrary-data.mdx index 4706ef5a45f..8686ff24fee 100644 --- a/src/content/ccip/tutorials/send-arbitrary-data.mdx +++ b/src/content/ccip/tutorials/send-arbitrary-data.mdx @@ -43,7 +43,7 @@ To use this contract: 1. Open MetaMask and select the network _Ethereum Sepolia_. 1. In Remix IDE, click on _Deploy & Run Transactions_ and select _Injected Provider - MetaMask_ from the environment list. Remix will then interact with your MetaMask wallet to communicate with _Ethereum Sepolia_. - 1. Fill in the router address and the link address for your network. You can find the router address on the [supported networks page](/ccip/supported-networks) and the LINK token address on the [LINK Token contracts page](/resources/link-token-contracts?parent=ccip). For _Ethereum Sepolia_, the router address is and the LINK contract address is . + 1. Fill in the router address and the link address for your network. You can find the router address on the [supported networks page](/ccip/supported-networks) and the LINK token address on the [LINK Token contracts page](/resources/link-token-contracts?parent=ccip). For _Ethereum Sepolia_, the router address is and the LINK contract address is . 1. Click on _transact_. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Enable your contract to send CCIP messages to _Polygon Mumbai_: @@ -54,7 +54,7 @@ To use this contract: 1. Open MetaMask and select the network _Polygon Mumbai_. 1. In Remix IDE, under _Deploy & Run Transactions_, make sure the environment is still _Injected Provider - MetaMask_. - 1. Fill in the router address and the LINK address for your network. You can find the router address on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . + 1. Fill in the router address and the LINK address for your network. You can find the router address on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . 1. Click on _transact_. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Enable your contract to receive CCIP messages from _Ethereum Sepolia_: diff --git a/src/content/ccip/tutorials/usdc.mdx b/src/content/ccip/tutorials/usdc.mdx index 05f9f9c974c..545258dddaa 100644 --- a/src/content/ccip/tutorials/usdc.mdx +++ b/src/content/ccip/tutorials/usdc.mdx @@ -79,7 +79,7 @@ To use this contract: 1. Open MetaMask and select the network _Polygon Mumbai_. 1. In Remix IDE, under _Deploy & Run Transactions_, make sure the environment is still _Injected Provider - MetaMask_. - 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . + 1. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [supported networks page](/ccip/supported-networks) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For _Polygon Mumbai_, the router address is and the LINK contract address is . 1. Click the **transact** button. After you confirm the transaction, the contract address appears on the _Deployed Contracts_ list. Note your contract address. 1. Enable your contract to receive CCIP messages from _Avalanche Fuji_: