目錄
目錄
1、什麼是 Mist
Mist
是以太坊官方的線上錢包管理工具。通過 Mist
我們可以很方便的連線上我們的私有網路,從而更好的開發、除錯、測試我們的智慧合約。既可以連線生產網路、測試網路,更加可以通過設定引數的方式,連線我們自己的私有網路。
Mist
在通過 geth.ipc
檔案連線後,就和 Geth
所建立的網路完全契合在一起了,在 Mist
上部署的合約,實際上也就是部署在了 Geth
網路上。Geth
網路上新建賬號,也可以在 Mist
這個工具上看到。
通過 Mist
,我們向大家更詳細的講解了以太坊的核心概念,包括:區塊、Transaction、Gas、賬戶、合約、合約中的建構函式,變數以及方法。
2、Mist 在哪裡下載?
開發版的 Mist
在這裡下載:https://github.com/ethereum/mist
如果要在生產環境上操作,可以直接在以太坊官網下載錢包工具:https://ethereum.org/
3、Mist 有哪些依賴?
需要有以下元件:
- Node.js
v7.x
(use the prefered installation method for your OS) - Meteor javascript app framework
- Yarn package manager
- Electron
v1.7.9
cross platform desktop app framework - Gulp build and automation system
4、如何安裝 Mist?
在安裝 Mist
前,要先安裝依賴的工具包。
4.1、安裝 Mist 依賴工具包
➜ /Users/lion >curl https://install.meteor.com/
➜ /Users/lion >brew install yarn
➜ /Users/lion >yarn global add electron@1.7.9
➜ /Users/lion >yarn global add gulp
yarn其他平臺的安裝方法,可以參考:https://yarnpkg.com/zh-Hans/docs/install
4.2、安裝 Mist
➜ /Users/lion/my_project/_eth >git clone https://github.com/ethereum/mist.git
➜ /Users/lion/my_project/_eth >cd mist
➜ /Users/lion/my_project/_eth/mist git:(develop) >git checkout v0.9.2
➜ /Users/lion/my_project/_eth/mist git:(369713b) >yarn
4.3、啟動 Mist,連線到 Geth
新開一個視窗,用以下命令執行 Mist
的後臺程式:
➜ /Users/lion/my_project/_eth/mist git:(369713b) >cd interface
➜ /Users/lion/my_project/_eth/mist/interface git:(369713b) >meteor --no-release-check
[[[[[ ~/my_project/_eth/mist/interface ]]]]]
=> Started proxy.
=> Started MongoDB.
=> Started your app.
=> App running at: http://localhost:3000/
=> Client modified -- refreshing
第一次執行會慢一些,會啟動proxy、MongoDB等程式,同時下載一些依賴元件
在啟動 Mist
之前,我們要先啟動 Geth
,參考:使用 Go-Ethereum 1.7.2搭建以太坊私有鏈
我們啟用以太坊私有鏈以後,在 ./chain
目錄上會建立私有鏈的一些資料,裡面有一個 geth.ipc
檔案。
➜ /Users/lion/my_project/_eth/test >ll chain
total 88
drwxr-xr-x 7 lion staff 224 Oct 24 12:21 geth
srw------- 1 lion staff 0 Oct 24 12:24 geth.ipc
-rw------- 1 lion staff 43213 Oct 24 12:08 history
drwx------ 4 lion staff 128 Oct 22 14:57 keystore
在原來的視窗中執行以下命令,用 Mist
連線我們用 Geth
啟動的私有鏈:
➜ /Users/lion/my_project/_eth/mist git:(369713b) >yarn dev:electron --rpc /Users/lion/my_project/_eth/test/chain/geth.ipc
如果正常交易以太坊的以太幣,可以在官網直接下載以太坊錢包使用:https://ethereum.org/
如果在另一臺機器是使用RPC方式執行,也可以使用下面的方法連線到 Geth
:
➜ /Users/lion/my_project/_eth/mist git:(369713b) >yarn dev:electron --rpc http://localhost:8545
執行完以後,會開啟一個比較像App的網頁,如下圖:
在之前的文章中我們建立的帳戶,經過
Mist
連線後,也可以在視覺化的介面中看到
在 Mist
的介面中,點選傳送,可以從一個帳戶地址,向另一個帳戶地址,轉移以太幣。
5、使用 Mist 部署一個簡單的智慧合約
在 Mist
上點選右側的合約。首先要選擇一個帳戶來生成合約,用於支付部署合約的費用,以後是誰呼叫誰來支付費用。(如果在公有鏈上部署智慧合約,需要花費一定的以太幣)。
下面是一個最簡單的合約程式碼,主要介紹可以看註釋:
pragma solidity 0.4.16;
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變數 */
string public standard = 'https://mshk.top';
/*記錄所有餘額的對映*/
mapping (address => uint256) public balanceOf;
/* 初始化合約,並且把初始的所有代幣都給這合約的建立者
* @param initialSupply 代幣的總數
*/
function token(uint256 initialSupply) {
//給指定帳戶初始化代幣總量,初始化用於獎勵合約建立者
balanceOf[msg.sender] = initialSupply;
}
/**
* 私有方法從一個帳戶傳送給另一個帳戶代幣
* @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;
//判斷買、賣雙方的資料是否和轉換前一致
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 從主帳戶合約呼叫者傳送給別人代幣
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
}
接下來我們將上面的合約程式碼,通過Mist部署到我們的私有鏈的。
首先如下圖中,點選合約
->部署新合約
:
然後如下面的兩張圖,選擇建立合約的帳戶,將上面的程式碼貼到SOLIDITY合約原始程式碼
處,在右側選擇要部署的合約token
,在token
的下面Initial Supply
處,輸入我們要初始化的金額5000
用於獎勵合約建立者,然後在頁面的最下面,點選部署,的彈出的層中,輸入建立合約這個帳號的密碼,輸入正確以後,合約建立成功。
建立一個合約以後,再點選 Mist
右側的合約,然後在 定製化合約
的下面,可以看到我們剛剛建立的合約 TOKEN
,如下圖:
注意,在合約下面也有一串
0x
開頭的地址,這個就相當於一個錢包的地址。在以太坊中,合約也相當於一個帳戶
點選 TOKEN
,進入到合約裡。在下面的 Balance Of
處,輸入剛才建立合約帳戶的地址0xa18e688326ab13b6147ce3ca2213db143a4ec2ee
,可以看到是有5000個代幣在裡面,如下圖:
在代幣建立的時候,初始值我們設定的是5000,所以只有建立帳戶的地址有代幣,而輸入其他帳戶的地址,如0xc81896af13449a82f22699311df4ec4b48c07718
,是沒有值的。
接下來,我們向0xc81896af13449a82f22699311df4ec4b48c07718
這個帳戶地址,轉入一些代幣。點選右側選擇函式
->選擇Transfer
,在_to
中輸入0xc81896af13449a82f22699311df4ec4b48c07718
,在_value
中輸入500
,然後點選執行,在彈出的層中輸入呼叫合約帳戶的密碼,確認操作。
我們能夠看到實際呼叫合約的過程中,會花費一定的
gas
,gas
和以太幣會根據區塊的算力有一個計算公式,gas
一般用於獎勵給挖礦者
在 transfer
方法中,我們設定了,只有合約的呼叫者msg.sender
才能向指定地址轉移代幣。
/**
* 從主帳戶合約呼叫者傳送給別人代幣
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
這時,再次進入合約,在Balance Of
處,輸入兩個帳戶的地址,可以看到,餘額都發生的變化,如下圖:
6、改善代幣
通過上面的操作,我們已經可以將合約程式碼,通過 Mist
部署到我們建立的私有鏈中,同樣如果部署到生產環境,只需要連上以太坊的網路,同樣的方法也可以將你的合約,部署到生產環境中,不過要根據程式碼的大小,花費一些以太幣。
實際使用過程中,交易的過程,需要通知到客戶端,並且記錄到區塊中,我們可以使用event
事件來指定,如下程式碼進行宣告:
//在區塊鏈上建立一個事件,用以通知客戶端
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; //代幣單位,展示的小數點後面多少個0,和以太幣一樣後面是是18個0
uint256 public totalSupply; //代幣總量
某些特定的場景中,不允許某個帳戶花費超過指定的上限,避免大額支出,我們可以新增一個 approve
方法,來設定一個允許支出最大金額的列表。
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;
}
/**
* 設定帳戶允許支付的最大金額
*
* 一般在智慧合約的時候,避免支付過多,造成風險,加入時間引數,可以在 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
方法,用於管理員減去指定帳戶的指定金額。進行該方法操作時,通知客戶端記錄到區塊鏈中。
//減去使用者餘額事件
event Burn(address indexed from, uint256 value);
/**
* 減少代幣呼叫者的餘額
*
* 操作以後是不可逆的
*
* @param _value 要刪除的數量
*/
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 要減去的數量
*/
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.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變數 */
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 18; //代幣單位,展示的小數點後面多少個0,和以太幣一樣後面是是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 token(uint256 initialSupply, string tokenName, string tokenSymbol) {
//初始化總量
totalSupply = initialSupply * 10 ** uint256(decimals); //以太幣是10^18,後面18個0,所以預設decimals是18
//給指定帳戶初始化代幣總量,初始化用於獎勵合約建立者
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) returns (bool success) {
//檢查傳送者是否擁有足夠餘額
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;
}
/**
* 設定帳戶允許支付的最大金額
*
* 一般在智慧合約的時候,避免支付過多,造成風險,加入時間引數,可以在 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); // Check if the sender has enough
//給指定帳戶減去餘額
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;
}
}
6.1、如何部署
如上面的部署中,我們將完整的程式碼,貼到 Mist
的 solidity合約原始程式碼
處,在右側選擇 token
, Initial Supply
輸入初始金額5000
,Token name
輸入我們的代幣名稱 陌上花開
, Token symbol
代幣符號我們輸入 $$
,然後點選 部署
,輸入部署帳戶的密碼。
部署合約以後,我們能夠在合約頁面看到剛才建立的合約。
點選合約名稱,可以看到合約的一些基本資訊,以及合約和操作函式
我們能夠在 Mist
上方的 錢包
中的主帳號這裡看到有個小圖示,說明主帳戶已經有了代幣,其他帳戶是沒有這個圖示的
點選進入主帳號以後,我們就可以看到主帳戶已經擁有的代幣和以太幣的數量,因為我們是參考以太幣進行設定,最小單位是wei,所以小數點後面有18個0。
接下來,我們向另一個帳戶傳送一些 陌上花開
幣,點選 Mist
上方的傳送,輸入傳送的帳戶地址,輸入數量 500
,選擇傳送的是 陌上花開
幣,點選傳送,如下圖
再次回到錢包中,我們可以看到,另一個帳戶也有了一個代幣的圖示,說明代幣已經轉入成功。
現在你擁有了自己的代幣,也可以做轉入轉出操作。可以被用於價值交換,或者工作時間追蹤或者其他專案。
7、高階版的代幣功能
一般的代幣可以不設定管理者,就是所謂的去中心化。實際使用過程中,可能需要給予挖礦等功能,讓別人能夠購買你的代幣,那麼我們就需要設定一個帳戶地址做為這個代幣合約的管理者。
**
* owned 是一個管理者
*/
contract owned {
address public owner;
/**
* 初臺化建構函式
*/
function owned() {
owner = msg.sender;
}
/**
* 判斷當前合約呼叫者是否是管理員
*/
modifier onlyOwner {
require (msg.sender == owner);
_;
}
/**
* 指派一個新的管理員
* @param newOwner address 新的管理員帳戶地址
*/
function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}
上面的程式碼是一個非常簡單的合約,我們可以在後面的程式碼中,使用 繼承
來實現後續的功能。
/**
* @title 高階版代幣
* 增加凍結使用者、挖礦、根據指定匯率購買(售出)代幣價格的功能
*/
contract MyAdvancedToken is owned{}
在 MyAdvancedToken
的所有方法中,可以使用 owned
的變數 owner
和 modifier onlyOwner
。
7.1、去中心化的管理者
我們也可以在建構函式中設定是否需要一個去中心化的管理者。
/*初始化合約,並且把初始的所有的令牌都給這合約的建立者
* @param initialSupply 所有幣的總數
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
* @param centralMinter 是否指定其他帳戶為合約所有者,為0是去中心化
*/
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol,
address centralMinter
) {
//設定合約的管理者
if(centralMinter != 0 ) owner = centralMinter;
}
7.2、挖礦
有的時候需要更多的代幣流通,可以增加 mintToken
方法,創造更多的代幣。
/**
* 合約擁有者,可以為指定帳戶創造一些代幣
* @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
方法的內容,插入到下劃線 _
處呼叫。
7.3、凍結資產
有的場景中,某些使用者違反了規定,需要凍結/解凍帳戶,不想讓他使用已經擁有的代幣.可以增加以下程式碼來控制:
//是否凍結帳戶的列表
mapping (address => bool) public frozenAccount;
//定義一個事件,當有資產被凍結的時候,通知正在監聽事件的客戶端
event FrozenFunds(address target, bool frozen);
/**
* 增加凍結帳戶名稱
*
* 你可能需要監管功能以便你能控制誰可以/誰不可以使用你建立的代幣合約
*
* @param target address 帳戶地址
* @param freeze bool 是否凍結
*/
function freezeAccount(address target, bool freeze) onlyOwner {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
7.4、自動交易
到了現在,代幣的功能很完善,大家也相信你的代幣是有價值的,但你想要使用以太幣 ether
或者其他代幣來購買,讓代幣市場化,可以真實的交易,我們可以設定一個價格
//賣出的匯率,一個代幣,可以賣出多少個以太幣,單位是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
的函式都要加一個 payable
屬性,如果你開放的合約,需要別人轉錢給你,就需要加 payable
。
下面的方法,不會增加代幣,只是改變呼叫合約者的代幣數量,買、賣的價格單位不是 ether
,而是 wei
,這是以太幣中最小的單位(就像美元裡的美分,比特幣裡的聰)。1 ether = 1000000000000000000 wei。因此使用 ether
設定價格的時候,在最後加18個0。
當建立合約的時候,傳送足夠多的 ether
作為代幣的背書,否則你的合約就是破產的,你的使用者就不能夠賣掉他們的代幣。
/**
* 使用以太幣購買代幣
*/
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);
}
7.5、全部程式碼
把所有的特性加上,完整的程式碼如下:
pragma solidity 0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
/**
* owned 是一個管理者
*/
contract owned {
address public owner;
/**
* 初臺化建構函式
*/
function owned() {
owner = msg.sender;
}
/**
* 判斷當前合約呼叫者是否是管理員
*/
modifier onlyOwner {
require (msg.sender == owner);
_;
}
/**
* 指派一個新的管理員
* @param newOwner address 新的管理員帳戶地址
*/
function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}
/**
* @title 基礎版的代幣合約
*/
contract token {
/* 公共變數 */
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 18; //代幣單位,展示的小數點後面多少個0,和以太幣一樣後面是是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 token(uint256 initialSupply, string tokenName, string tokenSymbol) {
//初始化總量
totalSupply = initialSupply * 10 ** uint256(decimals); //以太幣是10^18,後面18個0,所以預設decimals是18
//給指定帳戶初始化代幣總量,初始化用於獎勵合約建立者
//balanceOf[msg.sender] = totalSupply;
balanceOf[this] = 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) returns (bool success) {
//檢查傳送者是否擁有足夠餘額
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;
}
/**
* 設定帳戶允許支付的最大金額
*
* 一般在智慧合約的時候,避免支付過多,造成風險,加入時間引數,可以在 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); // Check if the sender has enough
//給指定帳戶減去餘額
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;
}
/**
* 匿名方法,預防有人向這合約傳送以太幣
*/
/*function() {
//return; // Prevents accidental sending of ether
}*/
}
/**
* @title 高階版代幣
* 增加凍結使用者、挖礦、根據指定匯率購買(售出)代幣價格的功能
*/
contract MyAdvancedToken is owned, token {
//賣出的匯率,一個代幣,可以賣出多少個以太幣,單位是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 代幣符號
* @param centralMinter 是否指定其他帳戶為合約所有者,為0是去中心化
*/
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol,
address centralMinter
) token (initialSupply, tokenName, tokenSymbol) {
//設定合約的管理者
if(centralMinter != 0 ) owner = centralMinter;
sellPrice = 2; //設定1個單位的代幣(單位是wei),能夠賣出2個以太幣
buyPrice = 4; //設定1個以太幣,可以買0.25個代幣
}
/**
* 私有方法,從指定帳戶轉出餘額
* @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 {
//給指定地址增加代幣,同時總量也相加
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 {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
/**
* 設定買賣價格
*
* 如果你想讓ether(或其他代幣)為你的代幣進行背書,以便可以市場價自動化買賣代幣,我們可以這麼做。如果要使用浮動的價格,也可以在這裡設定
*
* @param newSellPrice 新的賣出價格
* @param newBuyPrice 新的買入價格
*/
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
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);
}
}
參考之前的方法,在 Mist
中重新部署合約,貼完程式碼後,在右側選擇 My Advanced Token
,Initial Supply
輸入初始金額5000,Token name
輸入我們的代幣名稱 陌上花開A
,Token symbol
代幣符號我們輸入 #
,然後點選部署,輸入部署帳戶的密碼。
建立成功以後,我們在合約列表頁,可以看到剛才建立的新合約陌上花開A
。
點選 Mist
上面的傳送,我們先給帳戶0xd29adaadf3a40fd0b68c83c222c10d3ea637dce0
轉入100個以太幣。
操作成功以後,我們能夠在錢包頁面看到Account 4
已經有了100
以太幣。
7.6、使用以太幣購買代幣
接下來,我們進入合約頁面,使用以太幣購買 陌上花開A
代幣,進入合約介面後,我們能夠看到代幣上的以太幣是 0 ether
,在右側選擇 Buy
方法,Execut from
選擇 Account 4
,在 Send ether
輸入 10
個以太幣,點選 執行
。
執行成功以後,能夠看到當前頁面自動重新整理,合約中已經有了10 ether
,代幣的總量不變
再次回到 錢包
頁面,可以看到 Account 4
已經從 100 ether
變成了 90 ether
,並且多了一個代幣圖示。
點選 Account 4
帳號進去,可以看到一些詳細資訊,ether的總量是 89,999081514
而不是 90
,是因為執行合約的時候,我們會消費一定的 gas
。我們設定的費率是1:4,所以 10 ether
,只可以購買 2.5
個 陌上花開A
代幣,最小單位也是wei,所以是 2,500000000000000000
。
7.7、賣出代幣
進入合約介面後,我們能夠看到代幣上的以太幣是 10 ether
,在右側選擇 Sell
方法,在 Amount
處輸入 2000000000000000000
(因為我們剛才購買了2.5個代幣,現在賣出2個,賣出的最小單位是wei),Execut from
選擇 Account 4
,點選 執行
。
執行以後,在代幣的詳情頁面,能夠看到從 10 ether
變成了 6 ether
,因為剛才 Account 4
賣出了 2
個 陌上花開A
代幣,而我們設定的賣價是 1個代幣
能賣出 2個以太幣
。
再次回到 Account 4
的詳情頁面,能夠看到以太幣變成了 93,998273026
,而 陌上花開A
代幣的數量,變成了 0,500000000000000000
。
8、常見問題
8.1、在除錯Mist
的過程中,建立了很多個合約,如何刪除?
In the Ethereum Wallet (Mist) menu, click on Develop -> Toggle Developer Tools -> Wallet UI. Click on the Console tab
CustomContracts.find().fetch().map(
function(m) { CustomContracts.remove(m._id);}
)
博文作者:迦壹
部落格地址:Go-Ethereum 1.7.2 結合 Mist 0.9.2 實現代幣智慧合約的例項
轉載宣告:可以轉載, 但必須以超連結形式標明文章原始出處和作者資訊及版權宣告,謝謝合作!