值的存取应用3.0 | Web3.0 dApp 开发(五)

值的存取应用最终版

在 2.0 版本中,我们学习了通过require语句给函数添加权限控制。

现在,我们可以给它加上真正的经济机制,让purpose的值由真正的竞价拍卖机制所控制!

0x01 加入 Owner

如何知道合约的所有人owner是一个新手常见的问题。

最简单的方式是设置一个 public 的owner变量,在构造函数里传入_owner参数:

pragma solidity >=0.8.0 <0.9.0;
//SPDX-License-Identifier: MIT

contract PurposeHandler {

  string public purpose = "Building Unstoppable Apps";
  address public owner = owner;
  // 这里填写你自己的地址

  constructor(address _owner) {
    owner = _owner;
  }

  function setPurpose(string memory newPurpose) public {
      // about msg.sender:
      // https://cryptozombies.io/en/lesson/2/chapter/3
      // about require:
      // https://cryptozombies.io/en/lesson/2/chapter/4
      require( msg.sender == owner, "NOT THE OWNER!");

      purpose = newPurpose;
      console.log(msg.sender,"set purpose to",purpose);
  }
}

在生产实践中,一般通过引入ownable.sol来实现。

Openzepplin中的ownable.sol

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol

一个单文件ownable.sol:

pragma solidity ^0.4.25;

  /**
  * @title Ownable
  * @dev The Ownable contract has an owner address, and provides basic authorization control
  * functions, this simplifies the implementation of "user permissions".
  */
  contract Ownable {
    address private _owner;

    event OwnershipTransferred(
      address indexed previousOwner,
      address indexed newOwner
    );

    /**
    * @dev The Ownable constructor sets the original `owner` of the contract to the sender
    * account.
    */
    constructor() internal {
      _owner = msg.sender;
      emit OwnershipTransferred(address(0), _owner);
    }

    /**
    * @return the address of the owner.
    */
    function owner() public view returns(address) {
      return _owner;
    }

    /**
    * @dev Throws if called by any account other than the owner.
    */
    modifier onlyOwner() {
      require(isOwner());
      _;
    }

    /**
    * @return true if `msg.sender` is the owner of the contract.
    */
    function isOwner() public view returns(bool) {
      return msg.sender == _owner;
    }

    /**
    * @dev Allows the current owner to relinquish control of the contract.
    * @notice Renouncing to ownership will leave the contract without an owner.
    * It will not be possible to call the functions with the `onlyOwner`
    * modifier anymore.
    */
    function renounceOwnership() public onlyOwner {
      emit OwnershipTransferred(_owner, address(0));
      _owner = address(0);
    }

    /**
    * @dev Allows the current owner to transfer control of the contract to a newOwner.
    * @param newOwner The address to transfer ownership to.
    */
    function transferOwnership(address newOwner) public onlyOwner {
      _transferOwnership(newOwner);
    }

    /**
    * @dev Transfers control of the contract to a newOwner.
    * @param newOwner The address to transfer ownership to.
    */
    function _transferOwnership(address newOwner) internal {
      require(newOwner != address(0));
      emit OwnershipTransferred(_owner, newOwner);
      _owner = newOwner;
    }
  }

0x02 通过一条 require 判断建立「拍卖机制」

规则即是,如果出价比当前的 price 高,那么就可以修改set-purpose的值;如果不如当前的 price 高,则抛出错误。

pragma solidity >=0.8.0 <0.9.0;
//SPDX-License-Identifier: MIT

contract PurposeHandler {

  string public purpose = "Building Unstoppable Apps";
  address public owner = owner;
  uint256 public price = 0.001 ether;
  // 这里填写你自己的地址

  constructor(address _owner) {
    owner = _owner;
  }

  function setPurpose(string memory newPurpose) payable public {
      require( msg.value > price, "NOT ENOUGH!");
      purpose = newPurpose;
      // update price when guy set 
      price = msg.value;
      console.log(msg.sender,"set purpose to",purpose);
  }

}

0x03 提现机制

通过函数getBalance()`owner可以查看合约中的余额,通过函数getMyMoney可以让合约中的余额提现出来:

pragma solidity >=0.8.0 <0.9.0;
//SPDX-License-Identifier: MIT

contract PurposeHandler {

  string public purpose = "Building Unstoppable Apps";
  address public owner;
  uint256 public price = 0.001 ether;

  constructor(address _owner) {
    owner = _owner;
  }

  function setPurpose(string memory newPurpose) payable public {
      require( msg.value > price, "NOT ENOUGH!");
      purpose = newPurpose;
      // update price when guy set 
      price = msg.value;
  }

  function getBalance() view public returns(uint256) {
      return address(this).balance;
  }

  function getMyMoney(address _to, uint256 amount) public {
        require(msg.sender==owner);
        address payable receiver = payable(_to);
        receiver.transfer(amount);
  }
}

0x04 通过 Event 记录 set purpose 的历史

什么是事件?

区块链是一个区块列表——它们基本上都是由交易构成。 每一笔交易都有一个附加的收据(receipt),其中包含零个或多个日志条目。这些日志条目代表着智能合约中的事件被触发后生成的结果。

在Solidity源代码中,要定义一个事件event,需要在其前面加上event关键字(类似于function关键字的用法)来标记它。 然后,你可以任何你希望生成事件的函数体内调用或触发该事件。 你可以从任何函数中使用emit关键字触发事件。

有人可能会添加关于如何“监听”DAPP中事件的信息。 它使用Web 3.0的过滤功能(filtering functionality of Web 3.0)。

——https://learnblockchain.cn/question/29

我们可以给 setPurpose 函数添加event, 以记录 set purpose 的历史。

pragma solidity >=0.8.0 <0.9.0;
//SPDX-License-Identifier: MIT

contract PurposeHandler {

  event SetPurpose(address sender, string purpose);

  string public purpose = "Building Unstoppable Apps";
  address public owner;
  uint256 public price = 0.001 ether;

  constructor(address _owner) {
    owner = _owner;
  }

  function setPurpose(string memory newPurpose) payable public {
      require( msg.value > price, "NOT ENOUGH!");
      purpose = newPurpose;
      // update price when guy set 
      price = msg.value;
      emit SetPurpose(msg.sender, purpose);
  }

  function getBalance() view public returns(uint256) {
      return address(this).balance;
  }

  function getMyMoney(address _to, uint256 amount) public {
        require(msg.sender==owner);
        address payable receiver = payable(_to);
        receiver.transfer(amount);
  }
}

总结

值的存取系列就到这里了。

在下一篇,我们进入下一章—— NFT dApp 的设计与实现!

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

2 条评论

请先 登录 后评论
李大狗
李大狗

上海对外经贸大学区块链研究中心副主任

64 篇文章, 6717 学分