从 Solidity 与其他合约交互
在之前的教程中,我们学习了很多关于如何部署你的第一个智能合约的知识,并为其添加了一些功能,例如使用修饰符控制访问 (opens in a new tab)或Solidity 中的错误处理 (opens in a new tab)。在本教程中,我们将学习如何从现有合约部署智能合约并与之交互。
我们将编写一个合约,通过为其创建一个工厂,使任何人都能拥有自己的 Counter 智能合约,其名称将是 CounterFactory。首先,这是我们最初的 Counter 智能合约的代码:
pragma solidity 0.5.17;
contract Counter {
uint256 private _count;
address private _owner;
address private _factory;
modifier onlyOwner(address caller) {
require(caller == _owner, "You're not the owner of the contract");
_;
}
modifier onlyFactory() {
require(msg.sender == _factory, "You need to use the factory");
_;
}
constructor(address owner) public {
_owner = owner;
_factory = msg.sender;
}
function getCount() public view returns (uint256) {
return _count;
}
function increment(address caller) public onlyFactory onlyOwner(caller) {
_count++;
}
}
请注意,我们稍微修改了合约代码,以跟踪工厂的地址和合约所有者的地址。当你从另一个合约调用合约代码时,msg.sender 将指向我们合约工厂的地址。这是一个非常需要理解的重点,因为使用一个合约与其他合约交互是一种常见做法。因此,在复杂情况下,你应该注意发送者是谁。
为此,我们还添加了一个 onlyFactory 修饰符,以确保改变状态的函数只能由工厂调用,工厂会将原始调用者作为参数传递。
在我们将用来管理所有其他 Counter 的新 CounterFactory 中,我们将添加一个映射,将所有者与其 counter 合约的地址关联起来:
mapping(address => Counter) _counters;
在以太坊中,映射(mapping)相当于 JavaScript 中的对象,它们能够将类型 A 的密钥映射到类型 B 的值。在这种情况下,我们将所有者的地址与其 Counter 的实例映射起来。
为某人实例化一个新的 Counter 将如下所示:
function createCounter() public {
require (_counters[msg.sender] == Counter(0));
_counters[msg.sender] = new Counter(msg.sender);
}
我们首先检查该人是否已经拥有一个 counter。如果他没有 counter,我们通过将他的地址传递给 Counter 构造函数来实例化一个新的 counter,并将新创建的实例分配给映射。
要获取特定 Counter 的计数,它将如下所示:
function getCount(address account) public view returns (uint256) {
require (_counters[account] != Counter(0));
return (_counters[account].getCount());
}
function getMyCount() public view returns (uint256) {
return (getCount(msg.sender));
}
第一个函数检查给定地址的 Counter 合约是否存在,然后从实例中调用 getCount 方法。第二个函数:getMyCount 只是一个简写,用于将 msg.sender 直接传递给 getCount 函数。
increment 函数非常相似,但将原始交易发送者传递给 Counter 合约:
function increment() public {
require (_counters[msg.sender] != Counter(0));
Counter(_counters[msg.sender]).increment(msg.sender);
}
请注意,如果调用次数过多,我们的 counter 可能会成为溢出的受害者。你应该尽可能多地使用 SafeMath 库 (opens in a new tab) 来防止这种可能的情况。
要部署我们的合约,你需要同时提供 CounterFactory 和 Counter 的代码。例如,在 Remix 中部署时,你需要选择 CounterFactory。
这是完整代码:
pragma solidity 0.5.17;
contract Counter {
uint256 private _count;
address private _owner;
address private _factory;
modifier onlyOwner(address caller) {
require(caller == _owner, "You're not the owner of the contract");
_;
}
modifier onlyFactory() {
require(msg.sender == _factory, "You need to use the factory");
_;
}
constructor(address owner) public {
_owner = owner;
_factory = msg.sender;
}
function getCount() public view returns (uint256) {
return _count;
}
function increment(address caller) public onlyFactory onlyOwner(caller) {
_count++;
}
}
contract CounterFactory {
mapping(address => Counter) _counters;
function createCounter() public {
require (_counters[msg.sender] == Counter(0));
_counters[msg.sender] = new Counter(msg.sender);
}
function increment() public {
require (_counters[msg.sender] != Counter(0));
Counter(_counters[msg.sender]).increment(msg.sender);
}
function getCount(address account) public view returns (uint256) {
require (_counters[account] != Counter(0));
return (_counters[account].getCount());
}
function getMyCount() public view returns (uint256) {
return (getCount(msg.sender));
}
}
编译后,在 Remix 部署部分,你将选择要部署的工厂:
然后你可以试用你的合约工厂并检查值的变化。如果你想从不同的地址调用智能合约,你需要在 Remix 的账户(Account)选择中更改地址。
