建立自己的ERC20加密貨幣(可管理、增發、兌換、凍結等高階功能的代幣)
阿新 • • 發佈:2018-12-14
(注:本文是在原文的基礎上,根據個人的理解,修改部分內容並添加了一些註釋)
pragma solidity ^0.4.16; interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; } /** * owned是合約的管理者 */ contract owned { address public owner; /** * 初臺化建構函式 */ function owned () public { owner = msg.sender; } /** * 判斷當前合約呼叫者是否是合約的所有者 */ modifier onlyOwner { require (msg.sender == owner); _; } /** * 合約的所有者指派一個新的管理員 * @param newOwner address 新的管理員帳戶地址 */ function transferOwnership(address newOwner) onlyOwner public { if (newOwner != address(0)) { owner = newOwner; } } } /** * 基礎代幣合約 */ contract TokenERC20 { string public name; //發行的代幣名稱 string public symbol; //發行的代幣符號 uint8 public decimals = 18; //代幣單位,展示的小數點後面多少個0。 uint256 public totalSupply; //發行的代幣總量 /*記錄所有餘額的對映*/ mapping (address => uint256) public balanceOf; mapping (address => mapping (address => uint256)) public allowance; /* 在區塊鏈上建立一個事件,用以通知客戶端*/ //轉帳通知事件 event Transfer(address indexed from, address indexed to, uint256 value); event Burn(address indexed from, uint256 value); //減去使用者餘額事件 /* 初始化合約,並且把初始的所有代幣都給這合約的建立者 * @param initialSupply 代幣的總數 * @param tokenName 代幣名稱 * @param tokenSymbol 代幣符號 */ function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public { //初始化總量 totalSupply = initialSupply * 10 ** uint256(decimals); //給指定帳戶初始化代幣總量,初始化用於獎勵合約建立者 balanceOf[msg.sender] = totalSupply; name = tokenName; symbol = tokenSymbol; } /** * 私有方法從一個帳戶傳送給另一個帳戶代幣 * @param _from address 傳送代幣的地址 * @param _to address 接受代幣的地址 * @param _value uint256 接受代幣的數量 */ function _transfer(address _from, address _to, uint256 _value) internal { //避免轉帳的地址是0x0 require(_to != 0x0); //檢查傳送者是否擁有足夠餘額 require(balanceOf[_from] >= _value); //檢查是否溢位 require(balanceOf[_to] + _value > balanceOf[_to]); //儲存資料用於後面的判斷 uint previousBalances = balanceOf[_from] + balanceOf[_to]; //從傳送者減掉髮送額 balanceOf[_from] -= _value; //給接收者加上相同的量 balanceOf[_to] += _value; //通知任何監聽該交易的客戶端 Transfer(_from, _to, _value); //判斷買、賣雙方的資料是否和轉換前一致 assert(balanceOf[_from] + balanceOf[_to] == previousBalances); } /** * 從主帳戶合約呼叫者傳送給別人代幣 * @param _to address 接受代幣的地址 * @param _value uint256 接受代幣的數量 */ function transfer(address _to, uint256 _value) public { _transfer(msg.sender, _to, _value); } /** * 從某個指定的帳戶中,向另一個帳戶傳送代幣 * 呼叫過程,會檢查設定的允許最大交易額 * @param _from address 傳送者地址 * @param _to address 接受者地址 * @param _value uint256 要轉移的代幣數量 * @return success 是否交易成功 */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { //檢查傳送者是否擁有足夠餘額 require(_value <= allowance[_from][msg.sender]); allowance[_from][msg.sender] -= _value; _transfer(_from, _to, _value); return true; } /** * 設定帳戶允許支付的最大金額 * 一般在智慧合約的時候,避免支付過多,造成風險 * @param _spender 帳戶地址 * @param _value 金額 */ function approve(address _spender, uint256 _value) public returns (bool success) { allowance[msg.sender][_spender] = _value; return true; } /** * 設定帳戶允許支付的最大金額 * 一般在智慧合約的時候,避免支付過多,造成風險,加入時間引數,可以在 tokenRecipient 中做其他操作 * @param _spender 帳戶地址 * @param _value 金額 * @param _extraData 操作的時間 */ function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData); return true; } } /** * 減少代幣呼叫者的餘額 * 操作以後是不可逆的 * @param _value 要刪除的數量 */ function burn(uint256 _value) public returns (bool success) { //檢查帳戶餘額是否大於要減去的值 require(balanceOf[msg.sender] >= _value); //給指定帳戶減去餘額 balanceOf[msg.sender] -= _value; //代幣問題做相應扣除 totalSupply -= _value; Burn(msg.sender, _value); return true; } /** * 刪除帳戶的餘額(含其他帳戶) * 刪除以後是不可逆的 * @param _from 要操作的帳戶地址 * @param _value 要減去的數量 */ function burnFrom(address _from, uint256 _value) public returns (bool success) { //檢查帳戶餘額是否大於要減去的值 require(balanceOf[_from] >= _value); //檢查 其他帳戶 的餘額是否夠使用 require(_value <= allowance[_from][msg.sender]); //減掉代幣 balanceOf[_from] -= _value; allowance[_from][msg.sender] -= _value; //更新總量 totalSupply -= _value; Burn(_from, _value); return true; } } /** * 代幣增發、 * 代幣凍結、 * 代幣自動銷售和購買、 * 高階代幣功能 */ contract MyAdvancedToken is owned, TokenERC20 { //賣出的匯率,一個代幣,可以賣出多少個以太幣,單位是wei uint256 public sellPrice; //買入的匯率,1個以太幣,可以買幾個代幣 uint256 public buyPrice; //是否凍結帳戶的列表 mapping (address => bool) public frozenAccount; //定義一個事件,當有資產被凍結的時候,通知正在監聽事件的客戶端 event FrozenFunds(address target, bool frozen); /*初始化合約,並且把初始的所有的令牌都給這合約的建立者 * @param initialSupply 所有幣的總數 * @param tokenName 代幣名稱 * @param tokenSymbol 代幣符號 */ function MyAdvancedToken( uint256 initialSupply, string tokenName, string tokenSymbol ) TokenERC20(initialSupply, tokenName, tokenSymbol) public {} /** * 私有方法,從指定帳戶轉出餘額 * @param _from address 傳送代幣的地址 * @param _to address 接受代幣的地址 * @param _value uint256 接受代幣的數量 */ function _transfer(address _from, address _to, uint _value) internal { //避免轉帳的地址是0x0 require (_to != 0x0); //檢查傳送者是否擁有足夠餘額 require (balanceOf[_from] > _value); //檢查是否溢位 require (balanceOf[_to] + _value > balanceOf[_to]); //檢查 凍結帳戶 require(!frozenAccount[_from]); require(!frozenAccount[_to]); //從傳送者減掉髮送額 balanceOf[_from] -= _value; //給接收者加上相同的量 balanceOf[_to] += _value; //通知任何監聽該交易的客戶端 Transfer(_from, _to, _value); } /** * 合約擁有者,可以為指定帳戶創造一些代幣 * @param target address 帳戶地址 * @param mintedAmount uint256 增加的金額(單位是wei) */ function mintToken(address target, uint256 mintedAmount) onlyOwner public { //給指定地址增加代幣,同時總量也相加 balanceOf[target] += mintedAmount; totalSupply += mintedAmount; Transfer(0, this, mintedAmount); Transfer(this, target, mintedAmount); } /** * 增加凍結帳戶名稱 * * 你可能需要監管功能以便你能控制誰可以/誰不可以使用你建立的代幣合約 * * @param target address 帳戶地址 * @param freeze bool 是否凍結 */ function freezeAccount(address target, bool freeze) onlyOwner public { frozenAccount[target] = freeze; FrozenFunds(target, freeze); } /** * 設定買賣價格 * * 如果你想讓ether(或其他代幣)為你的代幣進行背書,以便可以市場價自動化買賣代幣,我們可以這麼做。如果要使用浮動的價格,也可以在這裡設定 * * @param newSellPrice 新的賣出價格 * @param newBuyPrice 新的買入價格 */ function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public { sellPrice = newSellPrice; buyPrice = newBuyPrice; } /** * 使用以太幣購買代幣 */ function buy() payable public { uint amount = msg.value / buyPrice; _transfer(this, msg.sender, amount); } /** * @dev 賣出代幣 * @return 要賣出的數量(單位是wei) */ function sell(uint256 amount) public { //檢查合約的餘額是否充足 require(this.balance >= amount * sellPrice); _transfer(msg.sender, this, amount); msg.sender.transfer(amount * sellPrice); } }