为什么要在一个合约中调用另外一个合约的函数?
有的时候,一个应用是由多个合约组成的。比如,我曾经创建过一个应用,它是由三个合约组成的并且彼此之间需要交互,但是我没法将它部署在以太坊的主网上,因为它们占的空间太大了,超出了区块限制。我不得不将这个应用重构为5个更小的智能合约,这样这个应用才可以被发布。
另一个有趣的应用场景是可升级的合约。区块链是不可更改的,这就意味着在智能合约部署以后,代码就不能被修改了。但是可以通过代理函数来指向其他合约来完成“升级”。
如果你想要改变逻辑,你可以给代理合约提供一个不同的目标合约地址,比如一个更新过的合约。还可以把逻辑和数据分到不同的智能合约中。这样,逻辑合约可以被代理合约升级或者替换,但是所有的数据还是存储在数据合约之中。
这个特性非常有用,因为它允许代码被重复利用,部署的合约可以被当成一个库来使用。因此,它还可以减少部署时的消耗,因为当合约可以被重复使用的时候,每次新的应用需要部署的合约就可能变少。
Solidity中调用另一个智能合约
让我们使用Remix这个在线IDE来进行一些尝试。
创建你的第一个智能合约
创建一个文件,用来存储智能合约,你可以将多个智能合约存储在一个文件中!
在Remix中,点击左边工具栏的“File Explorers”。
点击“Create a new file”按钮。
给你的文件命名为Contracts.sol
你将要在这个文件中编写并且部署两个合约。
首先,定义SPDX license信息和编译器版本。复制并且粘贴这部分代码:
//SPDX-License-Identifier:MIT
pragma solidity 0.8.16;
Counter合约
第一个合约是“Counter”,合约中只有一个数字自增的函数。
复制并且粘贴这个例子:
contract Counter{
uint public number;
function increment()external{
number+=1;
}
}
Counter合约有:
一个公共的变量number,是一个无符号整型变量,这个变量会做自增。
一个函数increment给整型变量增加1。它是一个external函数,所以它可以被其他合约发送的交易调用。
编译Counter合约
在左边工具栏中找到“Solidity compiler”按钮。
点击“Compile Contracts.sol”按钮
Enable自动编译(auto-compile)选项
看下有没有绿色的标志,如果有的话就说明编译成功了
部署Counter合约
在左边的工具栏,点击按钮“Deploy and run transactions”。
现在,我们只有一个智能合约,所以“Counter”在“Contracts”下拉菜单中会被自动选择。
点击“Deploy”按钮
在下方找到部署好的合约,然后复制它的地址。就像下面这个地址:
0xd9145CCE52D386f254917e481eB44e9943F39138
你会看到如下图所示:
1-1.png
在左边工具栏的下方可以找到部署好的合约
与Counter合约交互
打开Counter合约然后:
点击“number”按钮,然后检查它的值是不是0。
点击“increment”按钮,发送一个交易来给数字增加1。
再次点击“number”按钮,检查现在的值是不是1。
这个是最终结果:
1-2.png
counter合约的最终结果
CounterCaller合约
现在我们第一个合约Counter已经部署了,让我们创建另一个合约来使用Counter合约中的increment函数。这个机制就像是依赖注入(Dependency Injection),调用者可以通过合约函数的签名和变量来初始化一个合约。
在Contract.sol文件的底部复制和粘贴以下的代码:
contract CounterCaller{
Counter public myCounter;
constructor(address counterAddress){
myCounter=Counter(counterAddress);
}
function counterIncrement()external{
myCounter.increment();
}
}
CounterCaller合约有:
一个叫myCounter的变量,数据类型就是Counter。这意味着我们已经将即将调用的合约作为一个数据类型,以便我们可以通过这个变量来获得合约的函数和变量。
构造函数,构造函数只有在部署的过程中才会被调用,它会使用之前部署的Counter合约的地址初始化myCounter。
counterIncrement函数会通过myCounter调用increment函数,这里的myCounter就像是指向Counter智能合约的“指针”。
请看完整的Contracts.sol文件:
//SPDX-License-Identifier:MIT
pragma solidity 0.8.16;
contract Counter{
uint public number;
function increment()external{
number+=1;
}
}
contract CounterCaller{
Counter public myCounter;
constructor(address counterAddress){
myCounter=Counter(counterAddress);
}
function counterIncrement()external{
myCounter.increment();
}
}
编译CounterCaller合约
如果你已经enable来auto-compile选项,它就已经完成了编译。
查看按钮旁边的绿色按钮,已确定是否已经编译成功。
或者,根据之前的步骤去编译Counter合约。
现在,让我们来部署CounterCaller合约。在左边的工具栏中,点击按钮“Deploy and run transactions”。
在合约的下拉菜单中,选择CounterCaller。
在Deploy按钮旁边粘贴Counter合约的地址。
点击“Deploy”按钮。
1-3.png
部署ContractCaller合约。
与CounterCaller合约交互
在左边工具栏中找到CounterCaller合约,然后展开。
点击“myCounter”按钮然后查看Counter的地址。
点击“counterIncrement”按钮,发送交易来调用Counter合约中的increment函数。
在Counter合约中点击number按钮,验证数字是否增加了1,现在应该已经是2了。
这是最终的结果:
1-4.png
与CounterCaller合约交互。*
这个基础的例子演示了如何从在一个合约中调用另一个合约。
还有一些别的方式在不同的合约之间互相调用,只要你知道部署好的合约的地址,函数和公共变量,另一个合约就可以调用它。