【CryptoZombies - 2 Solidity 进阶】006 payable修饰符与提现

目录

一、前言

二、函数修饰符

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.然后它应该增加僵尸的 levelzombies[_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 {

}
发布了270 篇原创文章 · 获赞 534 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/shuiyixin/article/details/104569712