如何使用OpenZeppelin开发智能合约
本文不是讲如何使用Solidity语言编写可约,而是如何使用框架来开发合约。关于Solidity语言的语法、关键字等通过其他渠道学习。Node..js开发环境准备等也不具体展开。
首先创建一个项目:
mkdir learn && cd learn
npm init -y
比较知名的以太坊开发框架是Hardhat,一般配合ethers.js使用。另一个比较知名的开发框架是Truffle,一般配合web3.js使用。每个框架都有自己的优势。
首先在我们的项目目录安装Hardhat
npm install --save-dev hardhat
安装执行完成以后,可以使用npx hardhat命令创建Hardhat配置文件hardhat.config.js
第一个合约
创建一个目录contracts用于存储Solidity源码文件,这个目录和其他语言的src目录差不多。
创建一个名称为Box的简易智能合约,它可以存储一个值然后被检索到。
创建文件contracts/Box.sol。
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Box {
uint256 private _value;
// Emitted when the stored value changes
event ValueChanged(uint256 value);
// Stores a new value in the contract
function store(uint256 value) public {
_value = value;
emit ValueChanged(value);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return _value;
}
}
编译Solidity
由于以太坊虚拟机(EVM)不能直接运行Solidity代码,我们需要把Solidity代码编译为EVM字节码。
我们的智能合约使用的Solidity版本为0.8,需要在hardhat.config.js稍做配置。
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
};
编译合约:npx hardhat compile
编译任务会自动在contracts目录下检索合约并且使用配置在hardhat.config.js中的Solidity编译器编译他们,所以需要注意合约中pragma的Solidity版本号与hardhat.config.js中的版本号一致。
编译任务完成以后,项目根目录会生成一个名为artifacts的目录,它包含编译生成的字节码和元数据的json文件。可以将这个目录添加到.gitignore文件中。
添加更多合约
随着项目发展,我们会创建更多互相交互的合约,这些合约都会被存储在自己的.sol文件中。
现在给Box合约添加一个简单的访问控制系统。创建一个Auth合约用于存储管理员地址,只允许Box合约使用Auth合约允许的账号。
由于编译器会自动检索contracts目录及其子目录下的所有文件,我们可以很自由的来组织代码结构,我们创建一个子目录access-control用于存储Auth合约。
// contracts/access-control/Auth.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Auth {
address private _administrator;
constructor(address deployer) {
// Make the deployer of the contract the administrator
_administrator = deployer;
}
function isAdministrator(address user) public view returns (bool) {
return user == _administrator;
}
}
为了使用这个合约,我们在Box合约中使用import关联Auth合约的相对路径。
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// Import Auth from the access-control subdirectory
import "./access-control/Auth.sol";
contract Box {
uint256 private _value;
Auth private _auth;
event ValueChanged(uint256 value);
constructor() {
_auth = new Auth(msg.sender);
}
function store(uint256 value) public {
// Require that the caller is registered as an administrator in Auth
require(_auth.isAdministrator(msg.sender), "Unauthorized");
_value = value;
emit ValueChanged(value);
}
function retrieve() public view returns (uint256) {
return _value;
}
}
通过将一个复杂合约分解成多个合约是一个保持每个合约简单的好方法,也是一个合约开发的最佳实践。当然也可以通过Solidity的继承来实现。
使用OpenZeppelin合约
可复用的模块和库是好软件的基础,OpenZeppelin合约包含很多经过审计的智能合约模板,可以轻易使用。
关于继承
我们可以通过在OpenZeppelin合约中添加功能来实现自己的目标。Solidity的多重继承是个很好的方法。
比如,Ownable合约规定部署者账号为合约的所有人,并且提供一个叫做onlyOwner的modifier 。
导入OpenZeppelin合约
执行以下命令安装最新已发布的OpenZeppelin合约库。
npm install --save-dev @openzeppelin/contracts
为了使用某个合约,可以使用路径前缀@openzeppelin/contracts导入。比如,为了替换我们自己的Auth合约,我们可以使用@openzeppelin/contracts/access/Ownable.sol导入Ownable.sol合约以给Box添加访问控制。
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// Import Ownable from the OpenZeppelin Contracts library
import "@openzeppelin/contracts/access/Ownable.sol";
// Make Box inherit from the Ownable contract
contract Box is Ownable {
uint256 private _value;
event ValueChanged(uint256 value);
// The onlyOwner modifier restricts who can call the store function
function store(uint256 value) public onlyOwner {
_value = value;
emit ValueChanged(value);
}
function retrieve() public view returns (uint256) {
return _value;
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)