风险提示
根据银保监会等五部门于 2018 年 8月发布《关于防范以「虚拟货币」「区块链」名义进行非法集资的风险提示》的文件, 请广大公众理性看待区块链,不要盲目相信天花乱坠的承诺,树立正确的货币观念和投资理念,切实提高风险意识;对发现的违法犯罪线索,可积极向有关部门举报反映。
前情提示
上一篇文章大致讲了下什么是区块链和一个最基础的智能合约。合约基础到什么程度呢,基础到只有一个函数。这一篇文章主要带来一个具有写入函数以及权限控制的智能合约。因为是由浅入深,所以跨度不会太大,希望大家可以理解。话不多说,咱们直奔主题。
主题:我就是主题
智能合约的代码如下:
solidity
contract SimpleStorage {
uint public num;
address internal owner;
event LogSet(address owner, address caller);
constructor() {
owner = msg.sender;
}
function set(uint _num) public {
emit LogSet(owner, msg.sender);
require(msg.sender == owner, "no permission");
num = _num;
}
function get() public view returns (uint) {
return num;
}
function getOwner() external view returns(address) {
return owner;
}
}
从代码里我们可以看到,定义一个类型为 uint256 且可见性为public
的变量num和一个类型为 address 且可见性为internal
的变量owner, 且在构造函数里将msg.sender
赋值给 owenr。 msg.sender 是由 solidity 内置的一个东西,其值是变化的。如果是钱包AddressA 部署了 SimpleStorage 合约,则 msg.sender 就是钱包AddressA的地址。如果 SimpleStorage 是通过合约 B 部署的,则 msg.sender 就是合约B的地址。着重看下权限控制代码
ini
require(msg.sender == owner, "no permission");
这里通过内置函数require
来检查权限,require函数接受两个参数,第一个参数是条件判断表达式,是必选的,第二个参数为要返回的异常消息提醒,该参数是可选的。如果第一个参数的结果为true,则执行require下面的代码,反之则抛出异常,后续的代码也不会执行。
话题好像跑远了,应该测试下功能的😅,为了方便查看owner和函数调用者,我特意加了一个LogSet事件
csharp
event LogSet(address owner, address caller);
该事件抛出了owner和函数调用者
scss
emit LogSet(owner, msg.sender);
调用set
函数并传入参数10,我们看到了输出日志里 owner和caller地址都是我们账户地址0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,并且 num已经被成功设置为10
那如果调用者不是合约的owner呢,让我们眼见为实
如上图所示,我将账户切换为0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,并传参20给set函数,其结果是合约直接拒绝并抛出错误"no permission",并且num依旧是10,这就意味着权限控制生效了。
题外话
既然讲到了通过合约部署合约,那我们就来测试一下。代码如下:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ISimpleStorage {
function getOwner() external returns(address);
}
contract SimpleStorage {
uint public num;
address internal owner;
constructor() {
owner = msg.sender;
}
function set(uint _num) public {
require(msg.sender == owner, "no permission");
num = _num;
}
function get() public view returns (uint) {
return num;
}
function getOwner() external view returns(address) {
return owner;
}
}
contract FactoryContract {
address public owner;
uint public index;
constructor() {
owner = msg.sender;
}
function createContract() public returns(address) {
address c = address(new SimpleStorage{salt: keccak256(abi.encode(msg.sender, index))}());
index += 1;
return c;
}
function getSimpleStorageOwner(address _addr) public returns(address) {
return ISimpleStorage(_addr).getOwner();
}
}
可以看到我们在 FactoryContract 合约里直接通过 new
关键字直接创建合约。
我们试试通过 Factory 合约部署 SimpleStorage。
- 第一步,我们部署 Factory 合约(地址为:0xDA0bab807633f07f013f94DD0E6A4F96F8742B53),并调用 createContract 函数,并通过函数的返回值拿到了 SimpleStorage 合约地址为 0xAE77aeafd5569aa06Eef08Aff16Ba04e792AEf0f
- 第二步,我们通过调用FactoryContract的getSimpleStorageOwner 函数,并传入刚刚 拿到的 SimpleStorage 地址
调用FactoryContract的getSimpleStorageOwner,我们拿到了合约0xAE77aeafd5569aa06Eef08Aff16Ba04e792AEf0f 的owner为0xDA0bab807633f07f013f94DD0E6A4F96F8742B53,和Factory合约地址是一致的。
下集预告
这篇文章主要是给大家介绍了权限控制以及通过合约创建合约,下篇给大家讲下如何通过智能合约实现接收和发送Ether(老值钱了)。