跳转至主要内容

通过solidity与其他合约进行交互

智能合约solidityremix工厂部署可组合性
高级
jdourlens
EthereumDev(opens in a new tab)
2020年4月5日
5 分钟阅读 minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

在之前的教程中,我们学习到了关于 如何部署您的第一个智能合约的内容。在此基础上,还进一步学习了一些特性,比如使用修饰符控制访问权限(opens in a new tab)Solidity 中的错误处理等(opens in a new tab)。 在本教程中,我们将学习如何基于一个已有的合约进行智能合约的部署,并与其交互。

我们将会通过创建工厂的方式编写一个允许任何人拥有自己的Counter智能合约的合约,这个合约的名称将为CounterFactory。 首先,我们给大家展示一下初始Counter智能合约的代码。

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
显示全部
复制

注意,我们稍微修改了合约代码,来记录工厂的地址和合约所有者的地址。 当您从另一个合约来调用这个合约的代码的时,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}
5
6function 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)来防止出现这种可能的情况。

要部署合约,您需要同时提供CounterFactoryCounter的代码。 针对 Remix 中的示例进行部署时,您需要选择 CounterFactory。

下面是完整的代码:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
显示全部
复制

编译完成后,您将在 Remix 的部署部分选择要部署的工厂。

选择要在Remix中部署的工厂。

然后,您可以运行工厂合约并且观察值的变化情况。 如果希望通过其他地址来调用智能合约,您需要在 Remix 的帐户选择中更改地址。

上次修改时间: @nhsz(opens in a new tab), 2023年8月15日

本教程对你有帮助吗?