通过solidity与其他合约进行交互
在之前的教程中,我们学习到了关于 如何部署您的第一个智能合约的内容。在此基础上,还进一步学习了一些特性,比如使用修饰符控制访问权限(opens in a new tab)、Solidity 中的错误处理等(opens in a new tab)。 在本教程中,我们将学习如何基于一个已有的合约进行智能合约的部署,并与其交互。
我们将会通过创建工厂的方式编写一个允许任何人拥有自己的Counter
智能合约的合约,这个合约的名称将为CounterFactory
。 首先,我们给大家展示一下初始Counter
智能合约的代码。
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}显示全部复制
注意,我们稍微修改了合约代码,来记录工厂的地址和合约所有者的地址。 当您从另一个合约来调用这个合约的代码的时,msg.sender 将引用合约工厂的地址。 这是要理解的一个要点,因为使用合约来与其他合约进行交互是一种常见做法。 因此,在复杂的情况下,您需要特别注意谁是发送者。
为此,我们也添加了修饰符onlyFactory
,以确保改变状态的函数只能由将传递原始调用者作为参数的工厂调用。
在我们将管理所有其他 Counters 的新CounterFactory
中,我们会添加一个映射,该映射会将所有者关联到其 counter 合约地址:
1mapping(address => Counter) _counters;复制
在以太坊中,映射等同于 javascript 中的对象,它们允许将类型 A 的值映射到类型 B 的值。在本例中,我们将所有者的地址与 Counter 的实例进行映射。
为某人实例化一个新的 Counter 的代码将类似于如下:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }复制
首先,我们会检查一下此人是否已经拥有了一个 counter。 如果他未拥有 counter,我们会将他的地址传给Counter
构造函数来实例化一个新的 counter,并将新创建的实例分配至映射。
获取一个特定 Counter 的计数的代码将类似于如下:
1function getCount(address account) public view returns (uint256) {2 require (_counters[account] != Counter(0));3 return (_counters[account].getCount());4}56function getMyCount() public view returns (uint256) {7 return (getCount(msg.sender));8}复制
第一个函数会检查对于某个给定的地址 Counter 合约是否存在,然后从实例中调用getCount
方法。 第二个函数getMyCount
只是将 msg.sender 直接传递到getCount
函数的方法。
increment
函数非常相似,但是会将原始交易发送者传递到Counter
合约。
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }复制
注意,如果被调用次数过多,counter 可能会遇到溢出的问题。 您应尽可能多地使用SafeMath 库(opens in a new tab)来防止出现这种可能的情况。
要部署合约,您需要同时提供CounterFactory
和Counter
的代码。 针对 Remix 中的示例进行部署时,您需要选择 CounterFactory。
下面是完整的代码:
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}3435contract CounterFactory {3637 mapping(address => Counter) _counters;3839 function createCounter() public {40 require (_counters[msg.sender] == Counter(0));41 _counters[msg.sender] = new Counter(msg.sender);42 }4344 function increment() public {45 require (_counters[msg.sender] != Counter(0));46 Counter(_counters[msg.sender]).increment(msg.sender);47 }4849 function getCount(address account) public view returns (uint256) {50 require (_counters[account] != Counter(0));51 return (_counters[account].getCount());52 }5354 function getMyCount() public view returns (uint256) {55 return (getCount(msg.sender));56 }5758}显示全部复制
编译完成后,您将在 Remix 的部署部分选择要部署的工厂。
然后,您可以运行工厂合约并且观察值的变化情况。 如果希望通过其他地址来调用智能合约,您需要在 Remix 的帐户选择中更改地址。
上次修改时间: @nhsz(opens in a new tab), 2023年8月15日