在上一篇文章中(传送门:区块链研究实验室 | 手把手教你使用Solidity开发智能合约(五)),我们了解了如何使用函数,并应用了到目前为止所学到的一切来构建一个多功能签名钱包。
在本文中,我们将看到如何从另一个合同中创建一个合同,以及如何定义抽象合同和接口。
合同创建
可以通过以太坊交易或在Solidity合约中使用new关键字创建合约,该关键字将部署该合约的新实例并返回其地址。
通过检查Solidity文档中给出的示例,让我们仔细看一下它是如何工作的。我创建了name变量,public以便我们可以检查其值并创建一个事件以读取该createToken函数的返回值(我们将在事件的另一篇文章中介绍):
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.8.0;
contract OwnedToken {
TokenCreator creator;
address owner;
bytes32 public name;
constructor(bytes32 _name) {
owner = msg.sender;
creator = TokenCreator(msg.sender);
name = _name;
}
function changeName(bytes32 newName) public {
if (msg.sender == address(creator))
name = newName;
}
function transfer(address newOwner) public {
if (msg.sender != owner) return;
if (creator.isTokenTransferOK(owner, newOwner))
owner = newOwner;
}
}
contract TokenCreator {
event TokenCreated(bytes32 name, address tokenAddress);
function createToken(bytes32 name)
public
returns (OwnedToken tokenAddress)
{
tokenAddress = new OwnedToken(name);
emit TokenCreated(name, address(tokenAddress));
}
function changeName(OwnedToken tokenAddress, bytes32 name) public {
tokenAddress.changeName(name);
}
function isTokenTransferOK(address currentOwner, address newOwner)
public
pure
returns (bool ok)
{
return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f;
}
}
这次,我将使用Tuffle框架——您可以按照他们制作的快速入门指南对其进行学习。
首先,我们将创建一个新项目并通过执行以下命令对其进行初始化:
> mkdir token
> cd token
> truffle init
打开项目,然后更新truffle-config.js文件以使用您将在其中部署合同的网络IP和端口以及您正在使用的Solidity编译器的版本。就我而言,我正在使用Ganache运行网络。
现在,我们可以在contracts文件夹中创建合同。复制并粘贴代码后,您需要在migrations文件夹中创建一个迁移文件以部署TokenCreator合同。将其命名为2_deploy_token.js,然后复制并粘贴以下代码。
const TokenCreator = artifacts.require("TokenCreator");
module.exports = function (deployer) {
deployer.deploy(TokenCreator);
};
现在返回命令行,然后键入truffle console以启动Truffle控制台-您可以在其中编译和部署合同:
我们现在要做的是检索TokenCreator已部署实例的实例。然后,我们将调用该createToken函数两次,并保存每个新创建合约的地址。
如果使用的是Ganache,则可以看到两个代表合同调用的交易添加到了交易列表中,其中数据字段设置为函数选择器的四个字节,并传递了参数。如果您想知道真正发生了什么以及如何创建这些合同,请继续关注我。
正如我们所知,合同只是另一种类型的帐户,那么到底发生了什么,当我们所谓的createToken功能状态数据库进行了更新,包括与四个变量,每个帐户存储(新创建的帐户nonce,balance,storage_root,code_hash)正确初始化。
如果现在返回到Truffle控制台,则可以检查每个事务的日志以获取每个合同的地址,然后可以调用名称getter来验证它们确实是两个单独的实体。
建设者宣言
合同的构造函数在创建合同时被调用,并且不会与其余的合同代码一起存储在区块链上。
构造函数是可选的。只允许一个构造函数,这意味着不支持重载。
使用constructor关键字声明构造函数:
contract A {
uint a;
bool b;
constructor(uint _a, bool _b){
a = _a;
b = _b;
}
...
}
抽象合约
需要将合同标记为abstract未执行其至少一项功能时。abstract即使实现了所有功能,合同也可能被标记为。
这可以使用abstract合同的关键字来完成,未实现的功能应具有virtual允许多态的关键字。
abstract contract A {
function f() public pure virtual;
}
即使所有功能都已实现,抽象协定也无法直接实例化。它们可以用作定义其他合同继承的特定行为的基本合同。已实现的功能应具有override关键字。
abstract contract A {
function f() public pure virtual;
}
abstract contract B is A {
function f() public pure override {
//function body
}
}
如果派生合同未实现所有未实现的功能,则也需要对其进行标记abstract。
界面
接口类似于抽象协定,但是不能实现任何功能。还有其他限制:
它们不能从其他合同继承,但是可以从其他接口继承
所有声明的函数必须是外部的
他们不能声明构造函数
他们不能声明状态变量
接口使用interface关键字声明。
interface A {
function f() external pure;
}
接口中声明的所有函数都是隐式的virtual。
结论
至此,我们对智能合约的创建与继承讲完了。下次,我们将研究工厂模式以及如何在Solidity中使用它们。
作者:链三丰,来源:区块链研究实验室
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。