ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WTF Solidity 极简入门:第 21 讲 —— 调用其他合约(Interact with other Contract)

WTF Solidity 极简入门:第 21 讲 —— 调用其他合约(Interact with other Contract) WTF Solidity 极简入门第 21 讲 —— 调用其他合约Interact with other Contract【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本教程为 WTF Solidity 极简入门系列第 21 讲讲解在已知目标合约代码或 ABI与部署地址的前提下如何从 Solidity 合约内部创建合约引用并调用其函数。读完本文你将掌握四种跨合约调用的写法传入合约地址、传入合约变量、创建合约变量以及调用payable函数并同步发送 ETH并学会在 Remix 中部署与验证整个调用链路。为什么合约之间可以互相调用在 Solidity 中一个合约可以调用另一个合约的函数。合约间的交互不仅让区块链上的程序可以复用也极大地丰富了以太坊生态——许多web3Dapp 都依赖其他合约才能工作例如 DeFi 中的yield farming收益农耕就建立在对多个协议的组合调用之上。本讲将介绍最基础也最常用的一类场景当目标合约的代码或 ABI与地址都已知时如何从合约中调用它。在开始之前先明确两个概念ABIApplication Binary Interface描述合约函数的签名、参数类型与返回值的接口规范。只要有 ABI 和地址即使没有源码也能调用合约。合约引用contract reference在代码中以合约名(地址)的形式创建的“指针”用于定位并调用目标合约。目标合约OtherContract我们先编写一个简单的合约OtherContract用于被其他合约调用。该合约在 21_CallContract/CallContract.sol英文版见 Languages/en/21_CallContract_en/CallContract.sol中定义// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; contract OtherContract { uint256 private _x 0; // 状态变量 x // 收到 ETH 时触发的事件记录金额 amount 和剩余 gas event Log(uint amount, uint gas); // 返回合约的 ETH 余额 function getBalance() view public returns(uint) { return address(this).balance; } // 可调整状态变量 _x 的函数并且可以往合约转 ETH (payable) function setX(uint256 x) external payable{ _x x; // 如果转入 ETH则释放 Log 事件 if(msg.value 0){ emit Log(msg.value, gasleft()); } } // 读取 _x 的值 function getX() external view returns(uint x){ x _x; } }这个合约包含一个私有状态变量_x初始值为0一个事件Log(uint amount, uint gas)在收到ETH时触发记录转入金额与调用时的剩余 gas三个函数getBalance()view public返回合约的ETH余额setX()external payable可设置_x的值同时接收ETH若msg.value 0则释放Log事件getX()external view读取_x的值。这里的payable关键字是后续“调用合约并转账”的基础gasleft()则用于捕获调用时剩余的 gas方便观察转账消耗。调用OtherContract的核心语法要调用已部署的合约我们可以利用合约地址和合约代码或 ABI来创建合约引用合约名(合约地址).函数名(参数)即_Name(_Address).f()其中_Name是合约名必须与目标合约代码或 ABI中声明的合约名保持一致_Address是目标合约地址f()是要调用的目标函数。在 Remix 中编译合约后分别部署OtherContract和CallContract两个合约即可开始下面的四种调用实验。编译成功后在 “Deploy Run Transactions” 面板中先部署OtherContract再部署CallContract调用方随后复制OtherContract的地址作为后续各函数的入参。四种调用方式详解1. 传入合约地址address最直接的方式是在函数参数中传入目标合约地址用OtherContract(_Address)临时创建引用并调用目标函数。下面的callSetX会调用OtherContract的setX(x)function callSetX(address _Address, uint256 x) external{ OtherContract(_Address).setX(x); }操作步骤复制OtherContract的合约地址填入callSetX函数的_Address参数并令x 123。交易成功后调用OtherContract中的getX即可验证_x已变为123。这种写法适合目标函数仅在本次调用中被使用一次的场景代码最精简且_Address是普通address类型天然兼容来自外部的地址输入。2. 传入合约变量contract type除了address我们可以直接在函数参数中传入合约类型的引用只需把参数类型从address改为目标合约名例如OtherContract。下面的callGetX实现了调用目标合约的getX()function callGetX(OtherContract _Address) external view returns(uint x){ x _Address.getX(); }注意该函数的参数OtherContract _Address底层类型仍然是address。从编译产物可以印证这一点——在 out/CallContract.sol/CallContract.json 的 ABI 中callGetX的参数被声明为{name:_Address,type:address,internalType:contract OtherContract}对外部调用者而言它是address类型只是编译器内部记录其指向OtherContract合约。因此在 Remix 中调用callGetX时填入的依然是一个地址字符串。这种方式让代码可读性更强适合在同一函数内多次调用目标合约函数的场景编译器也能借助合约类型做一定的静态检查。3. 创建合约变量local contract variable第三种方式是在函数体内先创建一个合约类型的局部变量保存目标合约引用再通过该变量调用函数。下面的callGetX2与上例实现相同的功能function callGetX2(address _Address) external view returns(uint x){ OtherContract oc OtherContract(_Address); x oc.getX(); }复制OtherContract的地址填入callGetX2调用后即可成功获取_x的值。当需要在一次调用中多次访问目标合约例如先读状态再调函数时这种写法避免了重复写OtherContract(_Address)。从源码结构看这三种读函数写法callSetX、callGetX、callGetX2在 CallContract.sol 中依次排列分别演示了「临时强转」「类型参数」「局部变量」三种创建合约引用的语法实际编译后产生的函数选择器与 ABI 均指向同一目标合约。4. 调用合约并发送 ETHpayable 调用如果目标合约的函数是payable的那么可以通过调用它来给目标合约转账语法为合约名(合约地址).函数名{value: 转账金额}(参数)即_Name(_Address).f{value: _Value}()其中_Name是合约名_Address是合约地址f是目标函数名_Value是要转入的ETH数额以wei为单位。OtherContract的setX函数是payable的下面的setXTransferETH在调用setX的同时把本函数收到的全部msg.value转发过去function setXTransferETH(address otherContract, uint256 x) payable external{ OtherContract(otherContract).setX{value: msg.value}(x); }注意这里的调用方函数setXTransferETH自身也声明为payable——只有这样它才能接收外部转入的 ETH 并继续向下转发。在 Remix 中复制OtherContract的地址填入otherContract参数并在 VALUE 栏输入10 ETH后发起交易交易确认后可以通过两条途径观察目标合约余额的变化查看OtherContract释放的Log事件其中记录了转入的amount直接调用OtherContract的getBalance()返回值应为转入的10 ETH即10000000000000000000 wei。两种可复用的实战思路传地址 vs 传合约引用对比四种写法可以提炼出两条可复用的工程思路传地址address函数对外暴露的永远是address参数调用方无需感知目标合约类型适合被外部 EOA 或钱包直接调用的入口函数如callSetX、callGetX2。传合约引用contract type对外仍以address呈现但对内编译器已标注internalType为目标合约代码语义更清晰适合合约内部组合调用如callGetX。若目标函数为payable则必须使用{value: 金额}语法并确保调用链上的每一层函数都声明payable否则 ETH 无法传递转账会直接失败。总结本讲介绍了如何通过目标合约代码或 ABI和地址来创建合约引用从而调用目标合约的函数核心要点归纳如下调用方式语法适用场景传入合约地址OtherContract(_Address).setX(x)单次调用代码最精简传入合约变量callGetX(OtherContract _Address)多次调用语义清晰创建合约变量OtherContract oc OtherContract(_Address)同一函数内复用引用调用并转 ETHOtherContract(a).setX{value: msg.value}(x)目标函数为payable完整可编译源码位于 21_CallContract/CallContract.sol中文图文教程见 21_CallContract/readme.md本讲英文版见 Languages/en/21_CallContract_en/readme.md。掌握合约间调用是理解后续 DeFi 组合、代理合约与多合约协作的基础建议结合 Remix 动手跑通上述四个例子。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表