diff --git a/.vscode/settings.json b/.vscode/settings.json index d88f4bac9c9..67d8ca91ad5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,6 +36,7 @@ "IERC", "Linea", "Mainnets", + "Merkle", "MOONRIVER", "MOVR", "Numberish", @@ -54,6 +55,7 @@ "typechain", "WAVAX", "WBNB", + "whatsnext", "WMATIC", "XDAI" ] diff --git a/public/images/ccip/manual-execution.png b/public/images/ccip/manual-execution.png new file mode 100644 index 00000000000..e587b9d8c3a 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..cf60063f4ef 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..6ad34c22629 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..6e5e14f91d3 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..49984f3e033 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..cc446ea159a 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..3588b225f97 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..11e3ac959ee 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..193cb0f01b6 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 d43d03f111c..689019525d6 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -881,6 +881,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", @@ -891,13 +895,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 6614dcf4b3f..1c52ea06961 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/resources/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", } diff --git a/src/content/ccip/best-practices.mdx b/src/content/ccip/best-practices.mdx index 25684a0ed46..3755de8aeb7 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 + +We advise separating the reception of CCIP messages from the core business logic of the contract. Implementing 'escape hatches' or fallback mechanisms is recommended 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). + ## 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..25f861a5a4d --- /dev/null +++ b/src/content/ccip/concepts/manual-execution.mdx @@ -0,0 +1,61 @@ +--- +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 } from "@components" + + + +CCIP messages are eligible for manual execution if either of these conditions is met: + +- Execution of the CCIP message on the destination blockchain has failed. +- The CCIP message timed out, with the current timeout threshold set at 8 hours. This scenario may arise during periods of extreme network congestion when CCIP cannot deliver the message within the allotted time frame. + +The flowchart presented below provides a visual guide through the process of a cross-chain transaction, emphasizing the steps involved in the manual execution: + +
+ + + +## CCIP execution + +1. A sender (a 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 transactions, computes a Merkle root, and records it to the [CommitStore contract](/ccip/architecture#commit-store) on the destination blockchain. +1. Upon successful verification, the [Risk Management Network](/ccip/concepts#risk-management-network) blesses the committed Merkle root. +1. Once the committed Merkle root is blessed, the [CCIP Executing DON](/ccip/architecture#executing-don) proceeds with the execution on the destination blockchain: + 1. If the CCIP message is older than 8 hours (timeout), then the **manual execution is enabled** (further details are provided below). + 1. If the CCIP message is within the time frame, the Executing DON assesses and adjusts the gas price to ensure the transaction’s execution on the destination blockchain. +1. The execution on the destination blockchain works as follows: + + 1. The CCIP initially determines whether the message involves token transfers. If affirmative, the tokens are transferred to the receiver to facilitate programmable transfers ahead of contract logic execution. + 1. For an EOA, the CCIP transaction is completed successfully. + 1. If the receiver is a contract, its [ccipReceive](/ccip/api-reference/ccip-receiver#ccipreceive) function is invoked. + + **Note:** The transaction is atomic, meaning that it will either fully succeed, with all steps completed, or fail entirely, with none of the steps taking effect. If the transaction fails, the **manual execution is enabled** (further details are provided below). + +## Manual execution + +In instances where a transaction on the destination blockchain fails or a CCIP message times out, you can refer to procedure below. **Note:** Any account can manually execute a CCIP message that is eligible for manual execution, but 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. Use the [CCIP explorer](https://ccip.chain.link/) to retry the transaction execution on the destination blockchain manually. **Note:** Manual execution is expected to be available. If it is unavailable, please get in touch with Chainlink Labs Support for assistance. +1. Manual execution may be helpful under two scenarios: + + 1. Insufficient gas limit: The [gas limit](/ccip/api-reference/client#evmextraargsv1) set in the CCIP message is insufficient to call the receiver. Then, increase the gas limit. + 1. Receiver logic error: The failure was due to an implementation error in the receiver contract. Developers must fix the implementation and then deploy a new version. **Note:** This corrective measure only applies if the receiver contract is upgradeable and uses the [proxy pattern](https://blog.openzeppelin.com/proxy-patterns). Non-upgradable contracts will not benefit from manual execution in this context. + + + +1. Once the underlying issue has been addressed—such as by adjusting the gas limit—users can trigger the manual execution through the CCIP explorer. 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. -| 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 | +| Timeout | 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 | +| Timeout | Maximum duration for the execution of a [CCIP message](/ccip/api-reference/client#evm2anymessage) | 8 hours | ### Version 1.0.0 @@ -67,8 +70,9 @@ 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) | 50 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) | 50 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 | +| Timeout | 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..cfcee1c897d --- /dev/null +++ b/src/content/ccip/tutorials/manual-execution.mdx @@ -0,0 +1,235 @@ +--- +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", + "Transfer Tokens Between EOAs": "/ccip/tutorials/cross-chain-tokens-from-eoa", + "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 Ethereum Sepolia 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 (Ethereum Sepolia), 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 _Ethereum Sepolia_ 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 _Ethereum Sepolia_ to _Polygon Mumbai_ so check the list of supported tokens [here](/ccip/supported-networks/v1_2_0/testnet#ethereum-sepolia-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 _Ethereum Sepolia_ 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 _Ethereum Sepolia_ and enable sending messages to _Polygon Mumbai_: + + 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. 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 _Ethereum Sepolia_. + 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 _Ethereum Sepolia_: + 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 _Ethereum Sepolia_: + 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 _Ethereum Sepolia_, and as allowed. + +At this point, you have one _sender_ contract on _Ethereum Sepolia_ 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 _Ethereum Sepolia_. + +### 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 _Ethereum Sepolia_: + + 1. Open MetaMask and select the network _Ethereum Sepolia_. + 1. In Remix IDE, under _Deploy & Run Transactions_, open the list of transactions of your smart contract deployed on _Ethereum Sepolia_. + 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 (_Ethereum Sepolia_ 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://sepolia.etherscan.io/tx/0x807ef3a86828f3e7e7bed6fa38f770c39a67704221ece124c55ca1ba8b771992) of a transaction on _Ethereum Sepolia_. + + + +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 _0x72909df2541f905029270c030fd42d6eef06813fc2f9bf08df78663c6ce90486_. + +
+ + + +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 _0x5298b58cbbaa87fd3627f9d20daf0822577c5985b71a228bf6ed28e07975e69d_. +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 _0xa2e0e032096d65BFfCA1fDC222239E2f75e5C456_. + +#### 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 _0xb06089d7b133d24335958c7af99f5b8975ba775741d889e17bf844cf8ed3687b_, 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 _Ethereum Sepolia_ to _Polygon Mumbai_ and from _Polygon Mumbai_ back to _Ethereum Sepolia_. + +## 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 9a8ef1a4bcf..f0dcc8c7503 100644 --- a/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx +++ b/src/content/ccip/tutorials/programmable-token-transfers-defensive.mdx @@ -53,7 +53,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. @@ -65,7 +65,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_: