原文:Go-Ethereum 1.7.2 結合 Mist 0.9.2 實現(xiàn)代幣智能合約的實例
作者:迦壹
(注:本文是在原文的基礎上,根據(jù)個人的理解,修改部分內容并添加了一些注釋)
基礎版的代幣合約
下面是一個最簡單的代幣合約代碼遍坟,主要介紹可以看注釋:
pragma solidity 0.4.20;
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變量 */
string public standard = "https://mshk.top";
/*記錄所有余額的映射*/
mapping (address => uint256) public balanceOf;
/* 初始化合約,并且把初始的所有代幣都給這合約的創(chuàng)建者
* @param initialSupply 代幣的總數(shù)
*/
function token (uint256 initialSupply) public {
balanceOf[msg.sender] = initialSupply;
}
/**
* 私有方法從一個帳戶發(fā)送給另一個帳戶代幣
* @param from address 發(fā)送代幣的地址
* @param to address 接受代幣的地址
* @param value uint256 接受代幣的數(shù)量
*/
function _transfer (address from, address to, uint256 value) internal {
//避免轉帳的地址是0x0
require(to != 0x0);
//檢查發(fā)送者是否擁有足夠余額
require(balanceOf[from] >= value);
//檢查是否溢出
require(balanceOf[to] + value > balanceOf[to]);
//保存數(shù)據(jù)用于后面的判斷
uint previousBalances = balanceOf[from] + balanceOf[to];
//從發(fā)送者減掉發(fā)送額
balanceOf[from] -= value;
//給接收者加上相同的量
balanceOf[to] += value;
//判斷買瓦阐、賣雙方的數(shù)據(jù)是否和轉換前一致
assert(balanceOf[from] + balanceOf[to] == previousBalances);
}
/**
* 從主帳戶合約調用者發(fā)送給別人代幣
* @param to address 接受代幣的地址
* @param value uint256 接受代幣的數(shù)量
*/
function transfer (address to, uint256 value) public {
_transfer(msg.sender, to, value);
}
}
接下來我們將上面的合約代碼吭从,通過Mist部署到我們的私有鏈
首先如下圖中踪蹬,點擊合約
->部署新合約
:
然后如下面的兩張圖,選擇創(chuàng)建合約的帳戶臣咖,將上面的代碼貼到SOLIDITY合約原始代碼
處跃捣,在右側選擇要部署的合約token
,在token
的下面Initial Supply
處夺蛇,輸入我們要初始化的金額5000
用于獎勵合約創(chuàng)建者疚漆,然后在頁面的最下面,點擊部署刁赦,的彈出的層中娶聘,輸入創(chuàng)建合約這個帳號的密碼,輸入正確以后截型,合約創(chuàng)建成功趴荸。
創(chuàng)建一個合約以后,再點擊 Mist
右側的合約宦焦,然后在 定制化合約
的下面发钝,可以看到我們剛剛創(chuàng)建的合約 TOKEN
,如下圖:
注意,在合約下面也有一串0x開頭的地址波闹,這個就相當于一個錢包的地址酝豪。在以太坊中,合約也相當于一個帳戶精堕。
點擊 TOKEN
孵淘,進入到合約里。在下面的 Balance Of
處歹篓,輸入剛才創(chuàng)建合約帳戶的地址0xa18e688326ab13b6147ce3ca2213db143a4ec2ee
瘫证,可以看到是有5000
個代幣在里面,如下圖:
在代幣創(chuàng)建的時候庄撮,初始值我們設置的是5000
背捌,所以只有創(chuàng)建帳戶的地址有代幣,而輸入其他帳戶的地址洞斯,如0xc81896af13449a82f22699311df4ec4b48c07718
毡庆,是沒有值的。
接下來烙如,我們向0xc81896af13449a82f22699311df4ec4b48c07718
這個帳戶地址么抗,轉入一些代幣。點擊右側選擇函數(shù)->選擇Transfer
亚铁,在_to
中輸入0xc81896af13449a82f22699311df4ec4b48c07718
蝇刀,在_value中
輸入500
,然后點擊執(zhí)行徘溢,在彈出的層中輸入調用合約帳戶的密碼熊泵,確認操作仰迁。
我們能夠看到實際調用合約的過程中甸昏,會花費一定的
gas
顽分,gas
和以太幣會根據(jù)區(qū)塊的算力有一個計算公式,gas
一般用于獎勵給挖礦者施蜜。
在 transfer
方法中卒蘸,我們設定了,只有合約的調用者msg.sender
才能向指定地址轉移代幣翻默。
/**
* 從主帳戶合約調用者發(fā)送給別人代幣
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
這時缸沃,再次進入合約,在Balance Of
處修械,輸入兩個帳戶的地址趾牧,可以看到,余額都發(fā)生的變化肯污,如下圖:
改善代幣
通過上面的操作翘单,我們已經(jīng)可以將合約代碼,通過 Mist 部署到我們創(chuàng)建的私有鏈中蹦渣,同樣如果部署到生產(chǎn)環(huán)境哄芜,只需要連上以太坊的網(wǎng)絡,同樣的方法也可以將你的合約柬唯,部署到生產(chǎn)環(huán)境中认臊,不過要根據(jù)代碼的大小,花費一些以太幣锄奢。
實際使用過程中失晴,交易的過程,需要通知到客戶端拘央,并且記錄到區(qū)塊中涂屁,我們可以使用event
事件來指定,如下代碼進行聲明:
//在區(qū)塊鏈上創(chuàng)建一個事件堪滨,用以通知客戶端
event Transfer(address indexed from, address indexed to, uint256 value);
設置一些代幣的基本信息
/* 公共變量 */
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 18; //代幣單位胯陋,展示的小數(shù)點后面多少個0,和以太幣一樣后面是是18個0
uint256 public totalSupply; //代幣總量
某些特定的場景中,不允許某個帳戶花費超過指定的上限袱箱,避免大額支出遏乔,我們可以添加一個 approve
方法,來設置一個允許支出最大金額的列表发笔。
注:根據(jù)個人的理解盟萨,approve是其他合約調用此合約,或者是代理了讨,例如交易所捻激,使用你的賬戶時制轰,你可以設置一個代幣數(shù)量。他們只能對你設置的數(shù)量的代幣進行操作胞谭,不知道這樣理解是否準確垃杖。下面是一個對approve、allowance丈屹,以及后面的transferFrom的解釋:
注:approve调俘、transferFrom及allowance解釋:
賬戶A有1000個ETH,想允許B賬戶隨意調用100個ETH旺垒。A賬戶按照以下形式調用approve函數(shù)approve(B,100)彩库。當B賬戶想用這100個ETH中的10個ETH給C賬戶時,則調用transferFrom(A, C, 10)先蒋。這時調用allowance[A][B]可以查看B賬戶還能夠調用A賬戶多少個token骇钦。
mapping (address => mapping (address => uint256)) public allowance;
/**
* 設置帳戶允許支付的最大金額
*
* 一般在智能合約的時候,避免支付過多竞漾,造成風險
*
* @param _spender 帳戶地址
* @param _value 金額
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
同樣在 solidity
中眯搭,合約之間也可以相互調用,我們可以增加一個 approveAndCall
方法畴蹭,用于在設置帳戶最大支出金額后坦仍,可以做一些其他操作。
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
/**
* 設置帳戶允許支付的最大金額
*
* 一般在智能合約的時候叨襟,避免支付過多繁扎,造成風險
*
* @param _spender 帳戶地址
* @param _value 金額
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 設置帳戶允許支付的最大金額
*
* 一般在智能合約的時候,避免支付過多糊闽,造成風險梳玫,加入時間參數(shù),可以在 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;
}
}
我們可以增加一個 burn
方法右犹,用于管理員減去指定帳戶的指定金額提澎。進行該方法操作時,通知客戶端記錄到區(qū)塊鏈中念链。
注:代碼并沒有判斷調用者是否為管理員盼忌。
注:代理B在調用burnFrom刪除A賬戶余額時,也需要刪除A授權給自己的可調用余額數(shù)掂墓。
//減去用戶余額事件
event Burn(address indexed from, uint256 value);
/**
* 減少代幣調用者的余額
*
* 操作以后是不可逆的
*
* @param _value 要刪除的數(shù)量
*/
function burn(uint256 _value) public returns (bool success) {
//檢查帳戶余額是否大于要減去的值
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
//給指定帳戶減去余額
balanceOf[msg.sender] -= _value;
//代幣問題做相應扣除
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 刪除帳戶的余額(含其他帳戶)
*
* 刪除以后是不可逆的
*
* @param _from 要操作的帳戶地址
* @param _value 要減去的數(shù)量
*/
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;
}
完整的代碼如下:
pragma solidity 0.4.20;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變量 */
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 18; //代幣單位谦纱,展示的小數(shù)點后面多少個0,和以太幣一樣后面是是18個0
uint256 public totalSupply; //代幣總量
/*記錄所有余額的映射*/
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/* 在區(qū)塊鏈上創(chuàng)建一個事件,用以通知客戶端*/
event Transfer(address indexed from, address indexed to, uint256 value); //轉帳通知事件
event Burn(address indexed from, uint256 value); //減去用戶余額事件
/* 初始化合約君编,并且把初始的所有代幣都給這合約的創(chuàng)建者
* @param initialSupply 代幣的總數(shù)
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
*/
function token(uint256 initialSupply, string tokenName, string tokenSymbol) public {
//初始化總量
totalSupply = initialSupply * 10 ** uint256(decimals); //以太幣是10^18跨嘉,后面18個0,所以默認decimals是18
//給指定帳戶初始化代幣總量吃嘿,初始化用于獎勵合約創(chuàng)建者
balanceOf[msg.sender] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
/**
* 私有方法從一個帳戶發(fā)送給另一個帳戶代幣
* @param _from address 發(fā)送代幣的地址
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function _transfer(address _from, address _to, uint256 _value) internal {
//避免轉帳的地址是0x0
require(_to != 0x0);
//檢查發(fā)送者是否擁有足夠余額
require(balanceOf[_from] >= _value);
//檢查是否溢出
require(balanceOf[_to] + _value > balanceOf[_to]);
//保存數(shù)據(jù)用于后面的判斷
uint previousBalances = balanceOf[_from] + balanceOf[_to];
//從發(fā)送者減掉發(fā)送額
balanceOf[_from] -= _value;
//給接收者加上相同的量
balanceOf[_to] += _value;
//通知任何監(jiān)聽該交易的客戶端
Transfer(_from, _to, _value);
//判斷買祠乃、賣雙方的數(shù)據(jù)是否和轉換前一致
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 從主帳戶合約調用者發(fā)送給別人代幣
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* 從某個指定的帳戶中梦重,向另一個帳戶發(fā)送代幣
*
* 調用過程,會檢查設置的允許最大交易額
*
* @param _from address 發(fā)送者地址
* @param _to address 接受者地址
* @param _value uint256 要轉移的代幣數(shù)量
* @return success 是否交易成功
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success){
//檢查發(fā)送者是否擁有足夠余額
require(_value <= allowance[_from][msg.sender]); // Check allowance
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;
}
/**
* 設置帳戶允許支付的最大金額
*
* 一般在智能合約的時候,避免支付過多寺庄,造成風險艾蓝,加入時間參數(shù),可以在 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 要刪除的數(shù)量
*/
function burn(uint256 _value) public returns (bool success) {
//檢查帳戶余額是否大于要減去的值
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
//給指定帳戶減去余額
balanceOf[msg.sender] -= _value;
//代幣問題做相應扣除
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 刪除帳戶的余額(含其他帳戶)
*
* 刪除以后是不可逆的
*
* @param _from 要操作的帳戶地址
* @param _value 要減去的數(shù)量
*/
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;
}
}
如上面的部署中斗塘,我們將完整的代碼,貼到 Mist
的 solidity
合約原始代碼處亮靴,在右側選擇 token
馍盟, Initial Supply
輸入初始金額5000
,Token name
輸入我們的代幣名稱 陌上花開
茧吊, Token symbol
代幣符號我們輸入 $$
,然后點擊 部署
贞岭,輸入部署帳戶的密碼。
部署合約以后搓侄,我們能夠在合約頁面看到剛才創(chuàng)建的合約瞄桨。
點擊合約名稱,可以看到合約的一些基本信息讶踪,以及合約和操作函數(shù)
我們能夠在 Mist
上方的 錢包
中的主帳號這里看到有個小圖標芯侥,說明主帳戶已經(jīng)有了代幣,其他帳戶是沒有這個圖標的
點擊進入主帳號以后乳讥,我們就可以看到主帳戶已經(jīng)擁有的代幣和以太幣的數(shù)量柱查,因為我們是參考以太幣進行設置,最小單位是wei云石,所以小數(shù)點后面有18個0唉工。
接下來,我們向另一個帳戶發(fā)送一些 陌上花開
幣汹忠,點擊 Mist
上方的發(fā)送淋硝,輸入發(fā)送的帳戶地址,輸入數(shù)量 500
宽菜,選擇發(fā)送的是 陌上花開
幣谣膳,點擊發(fā)送,如下圖
再次回到錢包中赋焕,我們可以看到参歹,另一個帳戶也有了一個代幣的圖標,說明代幣已經(jīng)轉入成功隆判。
現(xiàn)在你擁有了自己的代幣犬庇,也可以做轉入轉出操作僧界。可以被用于價值交換,或者工作時間追蹤或者其他項目臭挽。
高級版的代幣功能
雖然區(qū)塊鏈是去中心化的捂襟,但是實現(xiàn)對代幣(合約)的管理,也在許多應用中有需求欢峰,為了對代幣進行管理葬荷,首先需要給合約添加一個管理者。
**
* owned 是一個管理者
*/
contract owned {
address public owner;
/**
* 初臺化構造函數(shù)
*/
function owned() {
owner = msg.sender;
}
/**
* 判斷當前合約調用者是否是管理員
*/
modifier onlyOwner {
require (msg.sender == owner);
_;
}
/**
* 指派一個新的管理員
* @param newOwner address 新的管理員帳戶地址
*/
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
上面的代碼是一個非常簡單的合約纽帖,我們可以在后面的代碼中宠漩,使用 繼承
來實現(xiàn)后續(xù)的功能。
/**
* @title 高級版代幣
* 增加凍結用戶懊直、挖礦扒吁、根據(jù)指定匯率購買(售出)代幣價格的功能
*/
contract MyAdvancedToken is owned{}
在 MyAdvancedToken
的所有方法中,可以使用 owned
的變量 owner
和 modifier onlyOwner
室囊。
去中心化的管理者
我們也可以在構造函數(shù)中設置是否需要一個去中心化的管理者雕崩。
/*初始化合約,并且把初始的所有的令牌都給這合約的創(chuàng)建者
* @param initialSupply 所有幣的總數(shù)
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
* @param centralMinter 是否指定其他帳戶為合約所有者,為0是去中心化
*/
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol,
address centralMinter
) {
//設置合約的管理者
if(centralMinter != 0 ) owner = centralMinter;
}
代幣增發(fā)
實現(xiàn)代幣增發(fā)融撞,代幣增發(fā)就如同央行印鈔票一樣盼铁,想必很多人都需要這樣的功能。
/**
* 合約擁有者尝偎,可以為指定帳戶創(chuàng)造一些代幣
* @param target address 帳戶地址
* @param mintedAmount uint256 增加的金額(單位是wei)
*/
function mintToken(address target, uint256 mintedAmount) onlyOwner {
//給指定地址增加代幣饶火,同時總量也相加
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
}
在方法的最后有一個 onlyOwner
,說明 mintToken
是繼承了 onlyOwner
方法冬念,會先調用 modifier onlyOwner
方法趁窃,然后將 mintToken
方法的內容,插入到下劃線 _ 處調用急前。
凍結資產(chǎn)
有的場景中醒陆,某些用戶違反了規(guī)定,需要凍結/解凍帳戶裆针,不想讓他使用已經(jīng)擁有的代幣.可以增加以下代碼來控制:
//是否凍結帳戶的列表
mapping (address => bool) public frozenAccount;
//定義一個事件刨摩,當有資產(chǎn)被凍結的時候,通知正在監(jiān)聽事件的客戶端
event FrozenFunds(address target, bool frozen);
/**
* 增加凍結帳戶名稱
*
* 你可能需要監(jiān)管功能以便你能控制誰可以/誰不可以使用你創(chuàng)建的代幣合約
*
* @param target address 帳戶地址
* @param freeze bool 是否凍結
*/
function freezeAccount(address target, bool freeze) onlyOwner {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
代幣買賣(兌換)
可以自己的貨幣中實現(xiàn)代幣與其他數(shù)字貨幣(ether 或其他tokens)的兌換機制世吨。有了這個功能澡刹,我們的合約就可以在一買一賣中賺利潤了。
//賣出的匯率,一個代幣耘婚,可以賣出多少個以太幣罢浇,單位是wei
uint256 public sellPrice;
//買入的匯率,1個以太幣,可以買幾個代幣
uint256 public buyPrice;
/**
* 設置買賣價格
*
* 如果你想讓ether(或其他代幣)為你的代幣進行背書,以便可以市場價自動化買賣代幣,我們可以這么做。如果要使用浮動的價格,也可以在這里設置
*
* @param newSellPrice 新的賣出價格
* @param newBuyPrice 新的買入價格
*/
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
然后增加買、賣的方法花吟,每一次的交易,都會消耗掉一定的 ether
灾锯。在 Solidity 0.4.0 之后,要接收 ether
的函數(shù)都要加一個 payable
屬性嗅榕,如果你開放的合約顺饮,需要別人轉錢給你,就需要加 payable
凌那。
下面的方法兼雄,不會增加代幣,只是改變調用合約者的代幣數(shù)量案怯,買君旦、賣的價格單位不是 ether
,而是 wei
嘲碱,這是以太幣中最小的單位(就像美元里的美分,比特幣里的聰)。1 ether = 1000000000000000000 wei
局蚀。因此使用 ether
設置價格的時候,在最后加18個0
麦锯。
當創(chuàng)建合約的時候,發(fā)送足夠多的 ether
作為代幣的背書,否則你的合約就是破產(chǎn)的,你的用戶就不能夠賣掉他們的代幣。
/**
* 使用以太幣購買代幣
*/
function buy() payable public {
uint amount = msg.value / buyPrice;
_transfer(this, msg.sender, amount);
}
/**
* @dev 賣出代幣
* @return 要賣出的數(shù)量(單位是wei)
*/
function sell(uint256 amount) public {
//檢查合約的余額是否充足
require(this.balance >= amount * sellPrice);
_transfer(msg.sender, this, amount);
msg.sender.transfer(amount * sellPrice);
}
實現(xiàn)Gas的自動補充
以太坊中的交易時需要gas(支付給礦工的費用琅绅,費用以ether來支付)扶欣。而如果用戶沒有以太幣,只有代幣的情況(或者我們想向用戶隱藏以太坊的細節(jié))千扶,就需要自動補充gas的功能料祠。這個功能將使我們代幣更加好用。
自動補充的邏輯是這樣了澎羞,在執(zhí)行交易之前髓绽,我們判斷用戶的余額(用來支付礦工的費用),如果用戶的余額非常少(低于某個閾值時)可能影響到交易進行妆绞,合約自動售出一部分代幣來補充余額顺呕,以幫助用戶順利完成交易。
先來設定余額閾值:
uint minBalanceForAccounts;
function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
}
finney 是貨幣單位 1 finney = 0.001eth
然后交易中加入對用戶的余額的判斷括饶。
function transfer(address _to, uint256 _value) {
...
if(msg.sender.balance < minBalanceForAccounts)
sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
if(_to.balance<minBalanceForAccounts) // 可選株茶,讓接受者也補充余額,以便接受者使用代幣图焰。
_to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
}
全部代碼
pragma solidity 0.4.20;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
/**
* owned 是一個管理者
*/
contract owned {
address public owner;
/**
* 初臺化構造函數(shù)
*/
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;
}
}
}
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變量 */
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 18; //代幣單位启盛,展示的小數(shù)點后面多少個0,和以太幣一樣后面是是18個0
uint256 public totalSupply; //代幣總量
/*記錄所有余額的映射*/
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/* 在區(qū)塊鏈上創(chuàng)建一個事件,用以通知客戶端*/
event Transfer(address indexed from, address indexed to, uint256 value); //轉帳通知事件
event Burn(address indexed from, uint256 value); //減去用戶余額事件
/* 初始化合約,并且把初始的所有代幣都給這合約的創(chuàng)建者
* @param initialSupply 代幣的總數(shù)
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
*/
function token(uint256 initialSupply, string tokenName, string tokenSymbol) public {
//初始化總量
totalSupply = initialSupply * 10 ** uint256(decimals); //以太幣是10^18僵闯,后面18個0卧抗,所以默認decimals是18
//給指定帳戶初始化代幣總量,初始化用于獎勵合約創(chuàng)建者
//balanceOf[msg.sender] = totalSupply;
balanceOf[this] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
/**
* 私有方法從一個帳戶發(fā)送給另一個帳戶代幣
* @param _from address 發(fā)送代幣的地址
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function _transfer(address _from, address _to, uint256 _value) internal {
//避免轉帳的地址是0x0
require(_to != 0x0);
//檢查發(fā)送者是否擁有足夠余額
require(balanceOf[_from] >= _value);
//檢查是否溢出
require(balanceOf[_to] + _value > balanceOf[_to]);
//保存數(shù)據(jù)用于后面的判斷
uint previousBalances = balanceOf[_from] + balanceOf[_to];
//從發(fā)送者減掉發(fā)送額
balanceOf[_from] -= _value;
//給接收者加上相同的量
balanceOf[_to] += _value;
//通知任何監(jiān)聽該交易的客戶端
Transfer(_from, _to, _value);
//判斷買棍厂、賣雙方的數(shù)據(jù)是否和轉換前一致
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 從主帳戶合約調用者發(fā)送給別人代幣
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* 從某個指定的帳戶中颗味,向另一個帳戶發(fā)送代幣
*
* 調用過程,會檢查設置的允許最大交易額
*
* @param _from address 發(fā)送者地址
* @param _to address 接受者地址
* @param _value uint256 要轉移的代幣數(shù)量
* @return success 是否交易成功
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
//檢查發(fā)送者是否擁有足夠余額
require(_value <= allowance[_from][msg.sender]); // Check allowance
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;
}
/**
* 設置帳戶允許支付的最大金額
*
* 一般在智能合約的時候,避免支付過多张漂,造成風險晶默,加入時間參數(shù),可以在 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 要刪除的數(shù)量
*/
function burn(uint256 _value) public returns (bool success) {
//檢查帳戶余額是否大于要減去的值
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
//給指定帳戶減去余額
balanceOf[msg.sender] -= _value;
//代幣問題做相應扣除
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 刪除帳戶的余額(含其他帳戶)
*
* 刪除以后是不可逆的
*
* @param _from 要操作的帳戶地址
* @param _value 要減去的數(shù)量
*/
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;
}
/**
* 匿名方法航攒,預防有人向這合約發(fā)送以太幣
*/
/*function() {
//return; // Prevents accidental sending of ether
}*/
}
/**
* @title 高級版代幣
* 增加凍結用戶磺陡、挖礦、根據(jù)指定匯率購買(售出)代幣價格的功能
*/
contract MyAdvancedToken is owned, token {
//賣出的匯率,一個代幣漠畜,可以賣出多少個以太幣币他,單位是wei
uint256 public sellPrice;
//買入的匯率,1個以太幣,可以買幾個代幣
uint256 public buyPrice;
//是否凍結帳戶的列表
mapping (address => bool) public frozenAccount;
//定義一個事件憔狞,當有資產(chǎn)被凍結的時候蝴悉,通知正在監(jiān)聽事件的客戶端
event FrozenFunds(address target, bool frozen);
/*初始化合約,并且把初始的所有的令牌都給這合約的創(chuàng)建者
* @param initialSupply 所有幣的總數(shù)
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
* @param centralMinter 是否指定其他帳戶為合約所有者,為0是去中心化
*/
function MyAdvancedToken (
uint256 initialSupply,
string tokenName,
string tokenSymbol,
address centralMinter
) token (initialSupply, tokenName, tokenSymbol) public {
//設置合約的管理者
if(centralMinter != 0 ) owner = centralMinter;
sellPrice = 2; //設置1個單位的代幣(單位是wei)瘾敢,能夠賣出2個以太幣
buyPrice = 4; //設置1個以太幣拍冠,可以買0.25個代幣
}
/**
* 私有方法,從指定帳戶轉出余額
* @param _from address 發(fā)送代幣的地址
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數(shù)量
*/
function _transfer(address _from, address _to, uint _value) internal {
//避免轉帳的地址是0x0
require (_to != 0x0);
//檢查發(fā)送者是否擁有足夠余額
require (balanceOf[_from] > _value);
//檢查是否溢出
require (balanceOf[_to] + _value > balanceOf[_to]);
//檢查 凍結帳戶
require(!frozenAccount[_from]);
require(!frozenAccount[_to]);
//從發(fā)送者減掉發(fā)送額
balanceOf[_from] -= _value;
//給接收者加上相同的量
balanceOf[_to] += _value;
//通知任何監(jiān)聽該交易的客戶端
Transfer(_from, _to, _value);
}
/**
* 合約擁有者簇抵,可以為指定帳戶創(chuàng)造一些代幣
* @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);
}
/**
* 增加凍結帳戶名稱
*
* 你可能需要監(jiān)管功能以便你能控制誰可以/誰不可以使用你創(chuàng)建的代幣合約
*
* @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 要賣出的數(shù)量(單位是wei)
*/
function sell(uint256 amount) public {
//檢查合約的余額是否充足
require(this.balance >= amount * sellPrice);
_transfer(msg.sender, this, amount);
msg.sender.transfer(amount * sellPrice);
}
}
參考之前的方法晃财,在 Mist
中重新部署合約,貼完代碼后焦履,在右側選擇 My Advanced Token
拓劝,Initial Supply
輸入初始金額5000
,Token name
輸入我們的代幣名稱 陌上花開A
嘉裤,Token symbol
代幣符號我們輸入 #
,然后點擊部署郑临,輸入部署帳戶的密碼。
創(chuàng)建成功以后屑宠,我們在合約列表頁厢洞,可以看到剛才創(chuàng)建的新合約陌上花開A。
點擊 Mist
上面的發(fā)送,我們先給帳戶0xd29adaadf3a40fd0b68c83c222c10d3ea637dce0
轉入100個以太幣躺翻。
操作成功以后丧叽,我們能夠在錢包頁面看到Account 4
已經(jīng)有了100以太幣。
使用以太幣購買代幣
接下來公你,我們進入合約頁面踊淳,使用以太幣購買 陌上花開A
代幣,進入合約界面后陕靠,我們能夠看到代幣上的以太幣是 0 ether
迂尝,在右側選擇 Buy
方法,Execut from
選擇 Account 4
剪芥,在 Send ether
輸入 10
個以太幣垄开,點擊 執(zhí)行。
執(zhí)行成功以后税肪,能夠看到當前頁面自動刷新溉躲,合約中已經(jīng)有了10 ether
,代幣的總量不變
再次回到 錢包 頁面益兄,可以看到 Account 4
已經(jīng)從 100 ether
變成了 90 ether
锻梳,并且多了一個代幣圖標。
點擊 Account 4
帳號進去净捅,可以看到一些詳細信息唱蒸,ether的總量是 89,999081514
而不是 90
,是因為執(zhí)行合約的時候灸叼,我們會消費一定的 gas
。我們設置的費率是1:4
,所以 10 ether
庆捺,只可以購買 2.5
個 陌上花開A
代幣,最小單位也是wei古今,所以是 2,500000000000000000
。
賣出代幣
進入合約界面后滔以,我們能夠看到代幣上的以太幣是 10 ether
捉腥,在右側選擇 Sell
方法,在 Amount
處輸入 2000000000000000000
(因為我們剛才購買了2.5
個代幣你画,現(xiàn)在賣出2
個,賣出的最小單位是wei)抵碟,Execut from
選擇 Account 4
,點擊 執(zhí)行坏匪。
執(zhí)行以后拟逮,在代幣的詳情頁面,能夠看到從 10 ether
變成了 6 ether
适滓,因為剛才 Account 4
賣出了 2 個 陌上花開A
代幣敦迄,而我們設置的賣價是 1個代幣 能賣出 2個以太幣。
再次回到 Account 4
的詳情頁面,能夠看到以太幣變成了 93,998273026罚屋,而 陌上花開A 代幣的數(shù)量苦囱,變成了 0,500000000000000000。
注:Account4使用10個以太幣購買了2.5個代幣脾猛,如果交易機制沒有手續(xù)費撕彤,Account4賣出2.5個代幣仍然會獲得10個以太幣。而這里通過設置買價4和賣價2猛拴,Account4賣出2.5個代幣只會獲得5個以太幣羹铅。代幣合約利用手續(xù)費賺取了5個以太幣。
擴展閱讀
參考:實現(xiàn)一個可管理漆弄、增發(fā)睦裳、兌換、凍結等高級功能的代幣
作者:Tiny熊