入门
您可以使用任何区块链开发框架与Chainlink CCIP进行交互。在本次大师班中,我们将使用Remix IDE。
首先,访问https://remix.ethereum.org/ 并点击“Create new Workspace”按钮来创建一个新项目。选择“Blank”模板,并将工作区命名为“CCIP Masterclass 4”。
或者,您可以克隆:
@chainlink/contracts-ccip NPM包
要使用Chainlink CCIP,您需要与@chainlink/contracts-ccip NPM 钱包中的Chainlink CCIP专用合约进行交互。
要安装它,请创建一个新的Solidity文件,并粘贴以下内容。这是一个空合约,仅导入了@chainlink/contracts-ccip和@openzeppelin/contracts包中的合约,我们将在整个大师班中使用这些合约。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Empty {}
该合约要求至少使用0.8.20版本的Solidity编译器。需要特别注意的是,在最新的Remix IDE版本中,默认的EVM版本设置为“Cancun”。在上海升级(发生在当前的Cancun升级之前)中,向以太坊虚拟机中添加了一个新的操作码PUSH0。
然而,除了以太坊,大多数区块链尚未包含PUSH0操作码。这意味着PUSH0操作码现在可以成为合约字节码的一部分,如果您正在使用的链不支持它,它将会出现“无效操作码”错误。我们希望将以太坊虚拟机版本降级到“Paris”。
要了解更多信息,我们强烈建议您查看这个StackOverflow回答:
要将EVM版本设置为“Paris”,请导航到“Solidity compiler”标签,然后按照以下步骤操作:
将“COMPILER”版本设置为0.8.20+commit.a1b79de6
切换“Advanced Configurations”下拉菜单
切换“EVM VERSION”下拉菜单,并选择“paris”而不是默认值
现在通过点击“Compile Empty.sol”按钮来编译智能合约。如果编译成功,返回“File explorer”标签,如果生成了新的.deps/npm/@chainlink/contracts-ccip和.deps/npm/@openzeppelin/contracts文件夹,这意味着我们已经成功地将所有必要的包导入到了Remix IDE工作区。
水龙头
在本次大师班中,我们将把USDC从Avalanche Fuji测试网转移到以太坊Sepolia测试网。要在Avalanche Fuji测试网上获取一些测试网USDC,请导航到以下网站:https://faucet.circle.com/
要支付CCIP费用,您可以使用LINK代币或给定区块链上的原生/包装原生资产。在本次大师班中,我们需要至少3个LINK或Avalanche Fuji测试网的资产。要获取它,请导航到以下网站: https://faucets.chain.link/fuji
开发TransferUSDC智能合约
点击“Create new file”按钮创建一个新的Solidity文件,将其命名为TransferUSDC.sol,并粘贴以下Solidity代码。
// 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 {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol";
import {SafeERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/utils/SafeERC20.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.
*/
contract TransferUSDC is OwnerIsCreator {
using SafeERC20 for IERC20;
error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees);
error DestinationChainNotAllowlisted(uint64 destinationChainSelector);
error NothingToWithdraw();
IRouterClient private immutable i_ccipRouter;
IERC20 private immutable i_linkToken;
IERC20 private immutable i_usdcToken;
mapping(uint64 => bool) public allowlistedChains;
modifier onlyAllowlistedChain(uint64 _destinationChainSelector) {
if (!allowlistedChains[_destinationChainSelector])
revert DestinationChainNotAllowlisted(_destinationChainSelector);
_;
}
event UsdcTransferred(
bytes32 messageId,
uint64 destinationChainSelector,
address receiver,
uint256 amount,
uint256 ccipFee
);
constructor(address ccipRouter, address linkToken, address usdcToken) {
i_ccipRouter = IRouterClient(ccipRouter);
i_linkToken = IERC20(linkToken);
i_usdcToken = IERC20(usdcToken);
}
function allowlistDestinationChain(
uint64 _destinationChainSelector,
bool _allowed
) external onlyOwner {
allowlistedChains[_destinationChainSelector] = _allowed;
}
function transferUsdc(
uint64 _destinationChainSelector,
address _receiver,
uint256 _amount,
uint64 _gasLimit
)
external
onlyOwner
onlyAllowlistedChain(_destinationChainSelector)
returns (bytes32 messageId)
{
Client.EVMTokenAmount[]
memory tokenAmounts = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(i_usdcToken),
amount: _amount
});
tokenAmounts[0] = tokenAmount;
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(_receiver),
data: "",
tokenAmounts: tokenAmounts,
extraArgs: Client._argsToBytes(
Client.EVMExtraArgsV1({gasLimit: _gasLimit})
),
feeToken: address(i_linkToken)
});
uint256 ccipFee = i_ccipRouter.getFee(
_destinationChainSelector,
message
);
if (ccipFee > i_linkToken.balanceOf(address(this)))
revert NotEnoughBalance(
i_linkToken.balanceOf(address(this)),
ccipFee
);
i_linkToken.approve(address(i_ccipRouter), ccipFee);
i_usdcToken.safeTransferFrom(msg.sender, address(this), _amount);
i_usdcToken.approve(address(i_ccipRouter), _amount);
// Send CCIP Message
messageId = i_ccipRouter.ccipSend(_destinationChainSelector, message);
emit UsdcTransferred(
messageId,
_destinationChainSelector,
_receiver,
_amount,
ccipFee
);
}
function withdrawToken(
address _beneficiary,
address _token
) public onlyOwner {
uint256 amount = IERC20(_token).balanceOf(address(this));
if (amount == 0) revert NothingToWithdraw();
IERC20(_token).transfer(_beneficiary, amount);
}
}
准备部署
导航到“Deploy & run transactions”标签,从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。
如果您使用的是Metamask钱包,请确保已添加Avalanche Fuji C-Chain和Ethereum Sepolia(默认情况下应已添加)网络。
访问 Chainlist.org 并搜索“avalanche fuji”。当您看到链ID为43113的网络时,点击“Add to Metamask”按钮。
以太坊Sepolia网络应默认已添加到您的Metamask钱包中。然而,如果您需要手动添加,您可以重复我们为Avalanche Fuji C-Chain所做的步骤。导航到 Chainlist.org 并搜索“sepolia”。当您看到链ID为11155111的网络时,点击“Add to Metamask”按钮。
步骤1)将TransferUSDC.sol部署到Avalanche Fuji
打开您的Metamask钱包并切换到Avalanche Fuji网络。 打开TransferUSDC.sol文件。 导航到“Solidity Compiler”标签,并点击“Compile TransferUSDC.sol”按钮。 导航到“Deploy & run transactions”标签,从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。确保链ID已切换到43113(如果没有,您可能需要刷新浏览器中的Remix IDE页面)。 在“Contract”下拉菜单下,确保选择了“TransferUSDC - TransferUSDC.sol”
找到橙色的“Deploy”按钮。提供以下信息:
0xF694E193200268f9a4868e4Aa017A0118C9a8177 作为ccipRoute
0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846
作为linkToken
0x5425890298aed601595a70AB815c96711a31Bc65
作为usdcToken。
点击橙色的“Deploy”/“Transact”按钮。
Metamask通知将弹出。签署交易。
步骤2)在Avalanche Fuji上调用allowlistDestinationChain函数
在“Deployed Contracts”部分,您应该能找到之前部署到Avalanche Fuji的TransferUSDC.sol合约。找到allowlistDestinationChain函数并提供以下信息:
16015286601757825753,这是以太坊Sepolia测试网络的CCIP链选择器,作为_destinationChainSelector参数。
点击橙色的“Transact”按钮。
步骤3)在Avalanche Fuji上,向TransferUSDC.sol合约充值3个LINK。
为了支付CCIP费用,请向TransferUSDC.sol合约充值一定数量的LINK,3个LINK对于本次演示来说应该足够。
步骤4)在Avalanche Fuji上,调用USDC.sol合约的approve函数。
访问 Avalanche Fuji Snowtrace Explorer并搜索USDC代币。找到“Contract”标签,然后点击“Write as Proxy”标签。将您的钱包连接到区块链浏览器。最后,找到“approve”函数。
我们希望批准TransferUSDC.sol代表我们支出1个USDC。为此,我们必须提供以下信息:
之前部署的TransferUSDC.sol智能合约地址,作为spender参数
因为USDC代币有6位小数,1000000意味着我们将批准支出1个USDC。
点击“Write”按钮。Metamask弹窗将出现。签署交易。
步骤5)在Avalanche Fuji上调用transferUsdc函数。
在“Deployed Contracts”部分,您应该能找到之前部署到Avalanche Fuji的TransferUSDC.sol合约。找到transferUsdc函数并提供以下信息:
16015286601757825753,这是以太坊Sepolia测试网络的CCIP链选择器,作为_destinationChainSelector参数。
将_gasLimit参数设置为0,因为我们将代币发送到一个EOA(外部拥有账户),因此在目标端执行ccipReceive函数没有费用。
点击橙色的“Transact”按钮。
现在,您可以将交易哈希复制到 Chainlink CCIP Explorer的搜索栏中,以监控跨链消息的实时状态。
CCIP Explorer | Chainlink