目录

​一、前言​

​二、函数修饰符​

​1、回顾​

​1.可见性修饰符​

​2.状态修饰符​

​3.自定义修饰符​

​2、Payable修饰符​

​3、实战​

​1.要求​

​2.代码​

​三、提现Withdraws​

​1、引入​

​2、提现​

​3、实战1​

​1.要求​

​2.代码​

​4、实战2-僵尸战斗​

​1.要求​

​2.代码​


一、前言

看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。

前面我们讲了很多函数修饰符,今天我们再来讲一个新的函数修饰符,并讲一下如何将这个修饰符中涉及到进行提现。

二、函数修饰符

1、回顾

我们之前也学到过一些函数修饰符,我们先来回顾一下,我们都学习了哪些函数修饰符:

如果我们要给这些函数修饰符分类,我们可以分如下几类:

1.可见性修饰符

2.状态修饰符

3.自定义修饰符

1.可见性修饰符

我们有决定函数何时和被谁调用,也就是说,我们可以设置函数的可见性,设置函数可见性来控制函数何时或者被谁调用的修饰符,我们叫它可见性修饰符

可见性修饰符分为如下四个:

1.​​private​​ 意味着它只能被合约内部调用;

2.​​internal​​​ 就像 ​​private​​ 但是也能被继承的合约调用;

3.​​external​​ 只能从合约外部调用;

4. ​​public​​ 可以在任何地方调用,不管是内部还是外部。

2.状态修饰符

我们可以设置函数如何与区块链进行交互,设置函数状态来控制函数与区块链的交互方式的修饰符,我们叫它状态修饰符

我们主要学习了两个状态修饰符:

1. ​​view​​ 告诉我们运行这个函数不会更改和保存任何数据;

2.​​pure​​ 告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。

注:这两种修饰符在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)

3.自定义修饰符

我们可以自定义修饰符,我们通过modifiers来定义,自定义modifiers修饰符可以自己定义其对函数的约束逻辑,这种修饰符是自定义修饰符。比如我们之前学习的 ​​onlyOwner​​​ 和 ​​aboveLevel​​。

 

上述这些修饰符,可以同时作用于一个函数定义上,示例如下:

function test() external view onlyOwner anotherModifier { /* ... */ }

 

2、Payable修饰符

我们先来看一下啥是payable:

​payable​​ 方法是一种可以接收以太的特殊函数

首先我们先来考虑一个问题,就是我们调用一个普通的网站服务器上的API函数,我们无法通过这个函数传送美元。

但是在以太坊中,钱 (以太), 数据 (事务负载), 以及合约代码本身都存在于以太坊。你可以在同时调用函数并付钱给另外一个合约。

通过这个功能,我们就可以实现很多有趣的功能。比如向一个合约要求支付一定的钱来运行一个函数。

我们看一个简单的示例:

contract OnlineStore {
function buySomething() external payable {
// 检查以确定0.001以太发送出去来运行函数:
require(msg.value == 0.001 ether);
// 如果为真,一些用来向函数调用者发送数字内容的逻辑
transferThing(msg.sender);
}
}

在这里,​msg.value 是一种可以查看向合约发送了多少以太的方法,另外 ​ether​ 是一个內建单元。这里发生的事是,一些人会从 web3.js 调用这个函数 (从DApp的前端), 像这样 :

// 假设 `OnlineStore` 在以太坊上指向你的合约:
OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))

注意这个 ​​value​​​ 字段, JavaScript 调用来指定发送多少(0.001)​​以太​​​。如果把事务想象成一个信封,你发送到函数的参数就是信的内容。 添加一个 ​​value​​ 很像在信封里面放钱 —— 信件内容和钱同时发送给了接收者。

注:如果函数没有标记pyable,上述方法就无法发送以太,函数会直接拒绝你的事务。

 

3、实战

1.要求

假定在我们的游戏中,玩家可以通过支付ETH来升级他们的僵尸。ETH将存储在你拥有的合约中 —— 一个简单明了的例子,向你展示你可以通过自己的游戏赚钱。

1.定义一个 ​​uint​​​ ,命名为 ​​levelUpFee​​​, 将值设定为 ​​0.001 ether​​。

​2.​​​定义一个名为 ​​levelUp​​​ 的函数。 它将接收一个 ​​uint​​​ 参数 ​​_zombieId​​​。 函数应该修饰为 ​​external​​​ 以及 ​​payable​​。

3.这个函数首先应该 ​​require​​​ 确保 ​​msg.value​​​ 等于 ​​levelUpFee​​。

4.然后它应该增加僵尸的 ​​level​​​: ​​zombies[_zombieId].level++​​。。

 

2.代码

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

// 1. Define levelUpFee here
uint levelUpFee = 0.001 ether;
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}

// 2. Insert levelUp function here
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
zombies[_zombieId].name = _newName;
}

function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
zombies[_zombieId].dna = _newDna;
}

function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}

}

三、提现Withdraws

1、引入

我们经常会用微信和QQ发红包,如果我们微信和QQ中原本是没有钱的,那我们就需要从银行卡中提钱。而我们收到的红包,也会留在微信或QQ的钱包中,在一些时候,我们想用这些钱是没办法直接使用的,那我们需要把钱从这些虚拟钱包提取到银行卡中。

2、提现

上面提到的提现,在以太坊中也会有相同的问题,我们发送以太之后,它将被存储进以合约的以太坊账户中, 并冻结在哪里,想要能够使用这些以太,我们就需要把这些全部提现。

 

我们看一个简单的示例:

contract GetPaid is Ownable {
function withdraw() external onlyOwner {
address payable _owner = address(uint160(owner()));
_owner.transfer(address(this).balance);
}
}

我们可以通过 transfer 函数向一个地址发送以太, 然后 ​​this.balance​​​ 将返回当前合约存储了多少以太。 所以如果100个用户每人向我们支付1以太, ​​this.balance​​ 将是100以太。

可以通过 ​​transfer​​​ 向任何以太坊地址付钱。 比如,你可以有一个函数在 ​​msg.sender​​ 超额付款的时候给他们退钱 :

uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);

或者在一个有卖家和卖家的合约中, 你可以把卖家的地址存储起来, 当有人买了它的东西的时候,把买家支付的钱发送给它:

seller.transfer(msg.value);

所有的上述这些操作都是自动运行的,也就是说,我们拥有了一个不被任何第三方控制的去中心化市场,可以进行交易。

 

3、实战1

1.要求

可以通过支付ETH来升级他们的僵尸。ETH将存储在你拥有的合约中 —— 一个简单明了的例子,向你展示你可以通过自己的游戏赚钱。

1.在我们的合约里创建一个 ​​withdraw​​​ 函数,它应该几乎和上面的​​GetPaid​​一样。

​2.​​​以太的价格在过去几年内翻了十几倍,在我们写这个教程的时候 0.01 以太相当于1美元,如果它再翻十倍 0.001 以太将是10美元,那我们的游戏就太贵了。所以我们应该再创建一个函数,允许我们以合约拥有者的身份来设置 ​​levelUpFee​​。

(1)创建一个函数,名为 ​​setLevelUpFee​​​, 其接收一个参数 ​​uint _fee​​​,是 ​​external​​​ 并使用修饰符 ​​onlyOwner​​。

(2)这个函数应该设置 ​​levelUpFee​​​ 等于 ​​_fee​​。

2.代码

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

uint levelUpFee = 0.001 ether;

modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}

// 1. Create withdraw function here
function withdraw() external onlyOwner {
address payable _owner = address(uint160(owner()));
_owner.transfer(address(this).balance);
}

// 2. Create setLevelUpFee function here
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}

function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}

function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
zombies[_zombieId].name = _newName;
}

function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
zombies[_zombieId].dna = _newDna;
}

function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}

}

 

4、实战2-僵尸战斗

1.要求

在我们学习了可支付函数和合约余额之后,是时候为僵尸战斗添加功能了。

遵循上一章的格式,我们新建一个攻击功能合约,并将代码放进新的文件中,引入上一个合约。

 

1.在文件开头定义 Solidity 的版本  ​​>=0.5.0 <0.6.0​​。

​2.import​​​ 自 ​​zombiehelper.sol​​ 。

3.声明一个新的 ​​contract​​​,命名为 ​​ZombieBattle​​​, 继承自​​ZombieHelper​​。函数体就先空着吧。

2.代码

pragma solidity >=0.5.0 <0.6.0;
import "./zombiehelper.sol";

contract ZombieAttack is ZombieHelper {

}