目录
一、前言
二、函数修饰符
1、回顾
1.可见性修饰符
2.状态修饰符
3.自定义修饰符
2、Payable修饰符
3、实战
1.要求
2.代码
三、提现Withdraws
1、引入
2、提现
3、实战1
1.要求
2.代码
4、实战2-僵尸战斗
1.要求
2.代码
看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。
前面我们讲了很多函数修饰符,今天我们再来讲一个新的函数修饰符,并讲一下如何将这个修饰符中涉及到的以太币进行提现。
如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!
我们之前也学到过一些函数修饰符,我们先来回顾一下,我们都学习了哪些函数修饰符:
如果我们要给这些函数修饰符分类,我们可以分如下几类:
1.可见性修饰符
2.状态修饰符
3.自定义修饰符
我们有决定函数何时和被谁调用,也就是说,我们可以设置函数的可见性,设置函数可见性来控制函数何时或者被谁调用的修饰符,我们叫它可见性修饰符。
可见性修饰符分为如下四个:
1.
private
意味着它只能被合约内部调用;2.
internal
就像private
但是也能被继承的合约调用;3.
external
只能从合约外部调用;4.
public
可以在任何地方调用,不管是内部还是外部。
我们可以设置函数如何与区块链进行交互,设置函数状态来控制函数与区块链的交互方式的修饰符,我们叫它状态修饰符。
我们主要学习了两个状态修饰符:
1.
view
告诉我们运行这个函数不会更改和保存任何数据;2.
pure
告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。
注:这两种修饰符在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。
我们可以自定义修饰符,我们通过modifiers来定义,自定义modifiers修饰符可以自己定义其对函数的约束逻辑,这种修饰符是自定义修饰符。比如我们之前学习的 onlyOwner
和 aboveLevel
。
上述这些修饰符,可以同时作用于一个函数定义上,示例如下:
function test() external view onlyOwner anotherModifier { /* ... */ }
我们先来看一下啥是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,上述方法就无法发送以太,函数会直接拒绝你的事务。
假定在我们的游戏中,玩家可以通过支付ETH来升级他们的僵尸。ETH将存储在你拥有的合约中 —— 一个简单明了的例子,向你展示你可以通过自己的游戏赚钱。
1.定义一个
uint
,命名为levelUpFee
, 将值设定为0.001 ether
。
2.
定义一个名为levelUp
的函数。 它将接收一个uint
参数_zombieId
。 函数应该修饰为external
以及payable
。3.这个函数首先应该
require
确保msg.value
等于levelUpFee
。4.然后它应该增加僵尸的
level
:zombies[_zombieId].level++
。。
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;
}
}
我们经常会用微信和QQ发红包,如果我们微信和QQ中原本是没有钱的,那我们就需要从银行卡中提钱。而我们收到的红包,也会留在微信或QQ的钱包中,在一些时候,我们想用这些钱是没办法直接使用的,那我们需要把钱从这些虚拟钱包提取到银行卡中。
上面提到的提现,在以太坊中也会有相同的问题,我们发送以太之后,它将被存储进以合约的以太坊账户中, 并冻结在哪里,想要能够使用这些以太,我们就需要把这些全部提现。
我们看一个简单的示例:
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);
所有的上述这些操作都是自动运行的,也就是说,我们拥有了一个不被任何第三方控制的去中心化市场,可以进行交易。
可以通过支付ETH来升级他们的僵尸。ETH将存储在你拥有的合约中 —— 一个简单明了的例子,向你展示你可以通过自己的游戏赚钱。
1.在我们的合约里创建一个
withdraw
函数,它应该几乎和上面的GetPaid
一样。
2.
以太的价格在过去几年内翻了十几倍,在我们写这个教程的时候 0.01 以太相当于1美元,如果它再翻十倍 0.001 以太将是10美元,那我们的游戏就太贵了。所以我们应该再创建一个函数,允许我们以合约拥有者的身份来设置levelUpFee
。(1)创建一个函数,名为
setLevelUpFee
, 其接收一个参数uint _fee
,是external
并使用修饰符onlyOwner
。(2)这个函数应该设置
levelUpFee
等于_fee
。
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;
}
}
在我们学习了可支付函数和合约余额之后,是时候为僵尸战斗添加功能了。
遵循上一章的格式,我们新建一个攻击功能合约,并将代码放进新的文件中,引入上一个合约。
1.在文件开头定义 Solidity 的版本
>=0.5.0 <0.6.0
。
2.import
自zombiehelper.sol
。3.声明一个新的
contract
,命名为ZombieBattle
, 继承自ZombieHelper
。函数体就先空着吧。
pragma solidity >=0.5.0 <0.6.0;
import "./zombiehelper.sol";
contract ZombieAttack is ZombieHelper {
}