solidity学习以太坊代币篇

399 阅读5分钟

这是个人solidity学习笔记整理

以太坊上的代币

如果你对以太坊的世界有一些了解,你很可能听过人们聊到代币——尤其是 ERC20 代币.

一个  代币  在以太坊基本上就是一个遵循一些共同规则的智能合约——即它实现了所有其他代币合约共享的一组标准函数

例如 transfer(address _to, uint256 _value) 和 balanceOf(address _owner).

在智能合约内部,通常有一个映射, mapping(address => uint256) balances,用于追踪每个地址还有多少余额

所以基本上一个代币只是一个追踪谁拥有多少该代币的合约,和一些可以让那些用户将他们的代币转移到其他地址的函数。

另一个代币标准更适合加密收藏品——它们被称为ERC721 代币. ERC721 代币能互换的,因为每个代币都被认为是唯一且不可分割的。 你只能以整个单位交易它们,并且每个单位都有唯一的 ID。

实现一个代币合约

在实现一个代币合约的时候,我们首先要做的是将接口复制到它自己的 Solidity 文件并导入它,import "./erc721.sol";。 接着,让我们的合约继承它,然后我们用一个函数定义来重写每个方法。

你的合约可以继承自多个合约

image.png

balanceOf

function balanceOf(address _owner) public view returns (uint256 _balance);

这个函数只需要一个传入 address 参数,然后返回这个 address 拥有多少代币。

ownerOf

function ownerOf(uint256 _tokenId) public view returns (address _owner);

这个函数需要传入一个代币 ID 作为参数 (我们的情况就是一个僵尸 ID),然后返回该代币拥有者的 address

同样的,因为在我们的 DApp 里已经有一个 mapping (映射) 存储了这个信息,所以对我们来说这个实现非常直接清晰。我们可以只用一行 return 语句来实现这个函数。

ERC721: 转移标准

ERC721 规范有两种不同的方法来转移代币

function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;

  1. 第一种方法是代币的拥有者调用transfer 方法,传入他想转移到的 address 和他想转移的代币的 _tokenId
  2. 第二种方法是代币拥有者首先调用 approve,然后传入与以上相同的参数。接着,该合约会存储谁被允许提取代币,通常存储到一个 mapping (uint256 => address) 里。然后,当有人调用 takeOwnership 时,合约会检查 msg.sender 是否得到拥有者的批准来提取代币,如果是,则将代币转移给他。

transfer和 takeOwnership 都将包含相同的转移逻辑,只是以相反的顺序。 (一种情况是代币的发送者调用函数;另一种情况是代币的接收者调用它)。

ERC721: 批准

approve

使用 approve 或者 takeOwnership 的时候,转移有2个步骤:

  1. 你,作为所有者,用新主人的 address 和你希望他获取的 _tokenId 来调用 approve
  2. 新主人用 _tokenId 来调用 takeOwnership,合约会检查确保他获得了批准,然后把代币转移给他。 因为这发生在2个函数的调用中,所以在函数调用之间,我们需要一个数据结构来存储什么人被批准获取什么。

ERC721: takeOwnership

函数 takeOwnership, 应该只是简单地检查以确保 msg.sender 已经被批准来提取这个代币或者僵尸。若确认,就调用 _transfer

预防溢出

合约安全增强: 溢出和下溢

什么是  溢出  (overflow)?

假设我们有一个 uint8, 只能存储8 bit数据。这意味着我们能存储的最大数字就是二进制 11111111 (或者说十进制的 2^8 - 1 = 255).

uint8 number = 255;
number++;

我们导致了溢出 — 虽然我们加了1, 但是 number 出乎意料地等于 0了。 (如果你给二进制 11111111 加1, 它将被重置为 00000000,就像钟表从 23:59 走向 00:00)。

下溢(underflow)也类似,如果你从一个等于 0的uint8 减去 1, 它将变成 255 (因为 uint 是无符号的,其不能等于负数)

使用 SafeMath

为了防止这些情况,OpenZeppelin 建立了一个叫做 SafeMath 的  (library),默认情况下可以防止这些问题。

一个  是 Solidity 中一种特殊的合约。其中一个有用的功能是给原始数据类型增加一些方法。

我们来看看 add 的源代码看 SafeMath 做了什么

image.png 基本上 add 只是像 + 一样对两个uint 相加, 但是它用一个 assert 语句来确保结果大于 a。这样就防止了溢出

assert和 require 相似,若结果为否它就会抛出错误。 assert和 require 区别在于,require 若失败则会返还给用户剩下的 gas, assert 则不会。所以大部分情况下,你写代码的时候会比较喜欢 require,assert 只在代码可能出现严重错误的时候使用,比如 uint 溢出。

注释

@title(标题)

@author (作者)

@notice (须知)向 用户 解释这个方法或者合约是做什么的

@dev (开发者) 是向开发者解释更多的细节。

@param (参数)用来描述这个方法需要传入什么参数

@return (返回) 用来描述这个方法需要传入返回什么值