目錄
Contents [hide]
- 目錄
- 1、什麼是 Mist
- 2、Mist 在哪裡下載下傳?
- 3、Mist 有哪些依賴?
- 4、如何安裝 Mist?
- 4.1、安裝 Mist 依賴工具包
- 4.2、安裝 Mist
- 4.3、啟動 Mist,連接配接到 Geth
- 5、使用 Mist 部署一個簡單的智能合約
- 6、改善代币
- 6.1、如何部署
- 7、進階版的代币功能
- 7.1、去中心化的管理者
- 7.2、挖礦
- 7.3、當機資産
- 7.4、自動交易
- 7.5、全部代碼
- 7.6、使用以太币購買代币
- 7.7、賣出代币
- 8、常見問題
- 8.1、在調試Mist的過程中,建立了很多個合約,如何删除?
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
(use the prefered installation method for your OS)v7.x
- Meteor javascript app framework
- Yarn package manager
- Electron
cross platform desktop app frameworkv1.7.9
- 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 [email protected]
➜ /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
的過程中,建立了很多個合約,如何删除?
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);}
)