400-638-8808
|
微信公眾號(hào)





穩(wěn)定可靠 永不間斷

海外收發(fā) 暢通無阻

協(xié)同辦公 資源管理

超大郵件 超級(jí)功能

智能反垃圾郵件技術(shù)
易管理 免維護(hù)

微信掃一掃 關(guān)注"天下數(shù)據(jù)"商品一律九折
![]()
搜索"朗玥科技"關(guān)注,了解最新優(yōu)惠

本文參考 ethereum token ,以發(fā)行數(shù)字貨幣為案例,介紹solidity語言和以太坊編程。教大家如何在以太坊上發(fā)行數(shù)字貨幣?

一、數(shù)字貨幣
在以太坊,Token可以用來表示各種可以交易的商品: 數(shù)字貨幣、積分、證書、IOU、游戲虛擬物品等。這些Token在實(shí)現(xiàn)上有一些共性,可以提煉成通用接口,這樣以太坊錢包和其他的一些應(yīng)用也可以兼容使用這些共性。
1.1 Token的一個(gè)極簡實(shí)現(xiàn)
要實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的合約,會(huì)相當(dāng)復(fù)雜。我們由淺入深,先實(shí)現(xiàn)一個(gè)非常簡單的合約。
pragma solidity ^0.4.20;
contract MyToken {
/* Map對(duì)象用來記錄賬戶的余額, key是賬戶地址,value是余額 */
mapping (address => uint256) public balanceOf;
/* 構(gòu)造函數(shù),執(zhí)行初始化操作,給虛幣發(fā)行人,即默認(rèn)為當(dāng)前代碼的發(fā)布人,設(shè)置一定的初始虛幣數(shù)量。 */
function MyToken(
uint256 initialSupply
) public {
balanceOf[msg.sender] = initialSupply; // 初始虛幣數(shù)據(jù)全部授予發(fā)行者
}
/* 轉(zhuǎn)賬 */
function transfer(address _to, uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // 檢查發(fā)送者的賬戶的余額是否足夠
require(balanceOf[_to] + _value >= balanceOf[_to]); // 確認(rèn)接受者的賬戶余額不會(huì)溢出。嚴(yán)謹(jǐn)!
balanceOf[msg.sender] -= _value; // 扣減發(fā)送者的余額
balanceOf[_to] += _value; // 增加接受者的余額
return true;
}
}
1.2 閱讀代碼
我們從最簡單的合約代碼開始。
contract MyToken {
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
}
這里定義了一個(gè)余額Map,注意的要點(diǎn):
address: 是指賬戶地址,16進(jìn)制格式的。
uint256: 2^256 = 10^77 無符號(hào)整數(shù),指賬戶余額,基本能滿足各路壕的需求。
public: 所有賬戶余額都是公開的。 鏈上任何人都可以查詢其他人的賬戶余額。
如果這個(gè)合約能夠正常發(fā)布出來,它就可以正常的工作,但是沒什么用。 只是一個(gè)能夠查詢余額的合約,其他什么事都做不了,包括獲取一個(gè)數(shù)字貨幣。 所有賬戶余額都返回0。 所以我們需要在啟動(dòng)的時(shí)候,預(yù)置一些數(shù)字貨幣進(jìn)來。 于是有這么幾行代碼:
function MyToken() public {
balanceOf[msg.sender] = 21000000;
}
這是一個(gè)構(gòu)造函數(shù),方法名和類名是一樣的。 構(gòu)造函數(shù)僅僅在這個(gè)合約被部署到網(wǎng)絡(luò)上的時(shí)候運(yùn)行一次。它將給msg.sender,即發(fā)布這個(gè)合約的人,設(shè)置余額。 21000000是一個(gè)任意設(shè)定的數(shù)字。 當(dāng)然,也可以將這個(gè)數(shù)字作為參數(shù)來傳進(jìn)來。
function MyToken(uint256 initialSupply) public {
balanceOf[msg.sender] = initialSupply;
}
這樣在部署的時(shí)候,比如使用Ethereum Wallet來部署,就他會(huì)讓你選擇這個(gè)初始參數(shù)的值。
現(xiàn)在至少已經(jīng)有一個(gè)賬戶擁有初始數(shù)字貨幣余額了。接下來就需要添加一個(gè)方法,讓這些數(shù)字貨幣能夠交易。
/* Send coins */
function transfer(address _to, uint256 _value) public {
/* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
這是一個(gè)相當(dāng)直觀的方法:
_to: 收款人的賬戶地址;
_value: 送出的金額。
實(shí)現(xiàn)也很簡單,就是增減收付款人的賬戶余額。 但這個(gè)實(shí)現(xiàn)是有問題的,如果出款人賬戶余額不足怎么辦? 還有就是如果收款人是個(gè)土豪,再接收一筆巨款,就把賬戶打爆了,怎么辦? 所以我們需要增加一個(gè)校驗(yàn)。 校驗(yàn)的代碼并不復(fù)雜,問題是,如果校驗(yàn)發(fā)現(xiàn)參數(shù)確實(shí)有問題,那應(yīng)該怎么辦? 在以太坊上,終止程序執(zhí)行有兩種方法:
直接通過return來返回某個(gè)錯(cuò)誤信息。 這會(huì)節(jié)省一點(diǎn)gas,但也有個(gè)頭疼的問題, 已經(jīng)執(zhí)行的代碼產(chǎn)生的任何變更,都會(huì)被保存到鏈上。
通過throw 來拋出異常, 這會(huì)將本次產(chǎn)生的所有變更都回滾,但也會(huì)導(dǎo)致發(fā)送者的gas都被消耗。 不過Wallet能夠檢測(cè)出來可能拋的異常,他會(huì)顯示一個(gè)警告,避免以太幣被浪費(fèi)掉。
function transfer(address _to, uint256 _value) public {
/* Check if sender has balance and for overflows */
require(balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]);
/* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
這個(gè)合約本身還需要一些基本信息。 以后我們會(huì)通過Token注冊(cè)表來處理這些合約,現(xiàn)在我們先直接上代碼吧。
string public name;
string public symbol;
uint8 public decimals;
修正下構(gòu)造函數(shù),添加上述合約信息。
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) public {
balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
decimals = decimalUnits; // Amount of decimals for display purposes
}
最后,我們需要設(shè)置一些“事件”。 這是以太坊特有的,它可以讓以太坊錢包這樣的應(yīng)用來跟蹤合約上發(fā)生的活動(dòng)。 所有事件的第一個(gè)字符必須是大寫的,并且放在合約代碼的起始部分。
event Transfer(address indexed from, address indexed to, uint256 value);
這里我們?cè)O(shè)置了一個(gè)轉(zhuǎn)賬的事件。然后在轉(zhuǎn)賬代碼實(shí)現(xiàn)的最后,發(fā)出這個(gè)事件。
And then you just need to add these two lines inside the “transfer” function:
/* Notify anyone listening that this transfer took place */
emit Transfer(msg.sender, _to, _value);
這樣,一個(gè)合約就開發(fā)完成了。
1.3 關(guān)于注釋
和java一樣,solidity也支持注釋標(biāo)簽,比如@notice和@param。這些注釋可以讓以太坊錢包等應(yīng)用可以將注釋的內(nèi)容顯示給用戶。 這里有關(guān)于注釋的詳細(xì)說明。
二、改進(jìn)一下
現(xiàn)在這個(gè)加密貨幣可以上線了?晌覀兛傄垓v,把這個(gè)貨幣做的更有意思一點(diǎn)。
2.1 完善基礎(chǔ)功能
在基礎(chǔ)功能上,我們還有很多事情要做,比如approve, sendFrom等功能。
考慮這么一個(gè)場(chǎng)景: 把數(shù)字貨幣賣給交易所,不可能就把數(shù)字貨幣發(fā)送到交易所給的賬號(hào)就完事了, 他們還得知道是誰發(fā)的數(shù)字貨幣,也不會(huì)去訂閱這些事件。 所以,我們需要增加如下支持:
增加批準(zhǔn)approve功能,批準(zhǔn)交易所從你的賬戶里面扣款。
或者更進(jìn)一步,提供approveAndCall功能,在approve之后,通知交易所做后續(xù)的事情。
在這些待實(shí)現(xiàn)的功能里面, 轉(zhuǎn)賬是最基本的公用功能。 我們把它修改為內(nèi)部函數(shù),供其他方法實(shí)現(xiàn)時(shí)調(diào)用。
/* Internal transfer, can only be called by this contract */
function _transfer(address _from, address _to, uint _value) internal {
require (_to != 0x0); // Prevent transfer to 0x0 address. Use burn() instead
require (balanceOf[_from] >= _value); // Check if the sender has enough
require (balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
require(!frozenAccount[_from]); // Check if sender is frozen
require(!frozenAccount[_to]); // Check if recipient is frozen
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
emit Transfer(_from, _to, _value);
}
這是一個(gè)非常危險(xiǎn)的方法,一不小心錢就沒了。 所以調(diào)用這個(gè)transfer方法需要非常謹(jǐn)慎,確保要經(jīng)過許可才可以。
2.2 集中管理
所有的Dapps默認(rèn)是非集中式管理。 這并非意味著不能做集中管理。 如果需要控制能夠獲取數(shù)字貨幣的人, 控制數(shù)字貨幣的使用,這就得需要集中式的管理。 我們可以用一個(gè)賬戶,或者一個(gè)合約來控制, 使用的策略,可以是民主的投票,也可以采用限制數(shù)字貨幣所有者的權(quán)力的方式。 針對(duì)這些場(chǎng)景,我們需要使用到合約的一個(gè)非常有用的屬性: inheritance。 合約的繼承屬性使得它可以得到父合約的所有特性,而且不需要重新定義。
contract owned {
address public owner;
function owned() {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}
這里創(chuàng)建一個(gè)非常簡單的,申明數(shù)字貨幣owner的合約。 然后就把自己發(fā)行的數(shù)字貨幣關(guān)聯(lián)過來:
contract MyToken is owned {
/* the rest of the contract as usual */
這意味著在MyToken里面所有的方法都可以使用owner這個(gè)屬性和可以繼承的方法onlyOwner。 并且可以調(diào)用transferOwnership這個(gè)方法來修改所有者。 注意一下onlyOwner的實(shí)現(xiàn),有一個(gè)“-”標(biāo)識(shí),子類需要覆蓋實(shí)現(xiàn)這個(gè)方法時(shí),子類的函數(shù)體將放在這里。 可以在MyToken的構(gòu)造函數(shù)里面增加這個(gè)參數(shù)來設(shè)置owner:
function MyToken(
uint256 initialSupply,
string tokenName,
uint8 decimalUnits,
string tokenSymbol,
address centralMinter
) {
if(centralMinter != 0 ) owner = centralMinter;
}
2.3 印鈔造幣
在現(xiàn)實(shí)中,貨幣的總量不是恒定的,往往會(huì)隨著經(jīng)濟(jì)的發(fā)展而不斷地增加。 央行會(huì)不斷注入流動(dòng)性, 印鈔廠開足馬力印刷鈔票… 就數(shù)字貨幣而言,我們也希望能夠像央行那樣進(jìn)行管控,根據(jù)價(jià)格的增減而控制貨幣總量。 我們來看看這個(gè)怎么做。
首先,我們需要有一個(gè)字段來存儲(chǔ)總供應(yīng)鏈:totalSupply, 并且在構(gòu)造函數(shù)中初始化:
contract MyToken {
uint256 public totalSupply;
function MyToken(...) {
totalSupply = initialSupply;
...
}
...
}
添加一個(gè)方法來印鈔造幣:
function mintToken(address target, uint256 mintedAmount) onlyOwner {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
emit Transfer(0, owner, mintedAmount);
emit Transfer(owner, target, mintedAmount);
}
注意,這個(gè)方法后面的onlyOwner, 表示它將覆蓋實(shí)現(xiàn)父類中的 modifier onlyOwner 這個(gè)方法。 這是和其他語言不一樣的地方,可以對(duì)覆蓋的方法重新命名,甚至修改輸入輸出參數(shù)。 在編譯時(shí),這個(gè)方法的實(shí)現(xiàn)會(huì)替換到onlyOwner的”_“位置。
2.4 凍結(jié)資產(chǎn)
在實(shí)踐中,有時(shí)候需要對(duì)數(shù)字貨幣做一些管控,凍結(jié)或者解凍賬戶就是常見的操作。
mapping (address => bool) public frozenAccount;
event FrozenFunds(address target, bool frozen);
function freezeAccount(address target, bool freeze) onlyOwner {
frozenAccount[target] = freeze;
emit FrozenFunds(target, freeze);
}
在這個(gè)實(shí)現(xiàn)中,我們假定所有賬戶剛開始都是未凍結(jié)狀態(tài)。 之后,可以通過freezeAccount指令來凍結(jié)賬戶,或者解凍賬戶。 此外,我們還需要修改下transfer方法,轉(zhuǎn)賬之前,需要確認(rèn)賬戶是否沒有凍結(jié)。
function transfer(address _to, uint256 _value) {
require(!frozenAccount[msg.sender]);
這樣,凍結(jié)后, 賬戶的余額是不變的,但無法執(zhí)行轉(zhuǎn)賬操作。 這種方式是默認(rèn)賬戶都是未凍結(jié)狀態(tài),可以轉(zhuǎn)賬。 還有一個(gè)場(chǎng)景是默認(rèn)賬戶都是凍結(jié)的,只有白名單中的賬戶才可以轉(zhuǎn)賬。這種情況下,我們只要把frozenAccount方法替換成approvedAccount,并將上述實(shí)現(xiàn)替換成驗(yàn)證賬戶:
require(approvedAccount[msg.sender]);
2.5 買賣貨幣
到目前為止,我們這個(gè)新幣種還是自己內(nèi)部循環(huán)使用,體現(xiàn)不出價(jià)值來。 最簡單的給數(shù)字貨幣定價(jià)的方法,就是和以太幣掛鉤起來。 提供一個(gè)方法,可以在市場(chǎng)上以市場(chǎng)價(jià)自動(dòng)買賣,就和本外幣交換一樣。
首先,我們需要定一個(gè)買賣的牌價(jià):
uint256 public sellPrice;
uint256 public buyPrice;
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
對(duì)于幣值波動(dòng)不大的貨幣,這種做法是可以接受的。畢竟每一次調(diào)整幣值都需要耗費(fèi)一定的以太幣。 如果要設(shè)置固定的幣值, 可以考慮研究下 standard data feeds 然后實(shí)現(xiàn)下貨幣買賣的功能:
function buy() payable returns (uint amount){
amount = msg.value / buyPrice; // calculates the amount
_transfer(this, msg.sender, amount);
return amount;
}
function sell(uint amount) returns (uint revenue){
require(balanceOf[msg.sender] >= amount); // checks if the sender has enough to sell
balanceOf[this] += amount; // adds the amount to owners balance
balanceOf[msg.sender] -= amount; // subtracts the amount from sellers balance
revenue = amount * sellPrice;
msg.sender.transfer(revenue); // sends ether to the seller: its important to do this last to prevent recursion attacks
Transfer(msg.sender, this, amount); // executes an event reflecting on the change
return revenue; // ends function and returns
}
注意,這種方式并不會(huì)創(chuàng)建新的數(shù)字貨幣, 只是在原始賬戶和數(shù)字貨幣owner之間做了轉(zhuǎn)賬。 合約既可以持有自己的token,也可以持有以太幣。 但不管是設(shè)置幣值,還是投放新幣,都無法接觸真正的銀行發(fā)行的貨幣,或者以太幣。 合約能做的事情就是在自己的貨幣體系中轉(zhuǎn)移貨幣。
注意, 幣值并非以Ether為單位來設(shè)置,一般是使用wei單位,即以太幣的最小單位來設(shè)置。 一個(gè)ether等于10^18個(gè)wei,所以如果貨幣是以ether來定價(jià),那需要加18個(gè)0轉(zhuǎn)成wei定價(jià)。 在創(chuàng)建合約的時(shí)候,注意要湊集足夠的以太幣作為準(zhǔn)備金來應(yīng)對(duì)可能的兌換,否則這個(gè)貨幣會(huì)由于無法兌換而沒人買了。 這個(gè)例子描述了一個(gè)簡單的中心化的交易中心來買賣數(shù)字貨幣。一個(gè)更有意思的市場(chǎng)應(yīng)該是任何人都可以出不同的價(jià)格來買賣貨幣。
2.6 Gas自動(dòng)支付
在以太坊體系中,每一次交易或者合約都需要支付一定的費(fèi)用(Gas), 以后有可能會(huì)調(diào)整這個(gè)策略。 到目前為止,挖礦費(fèi)用還只能通過以太幣來支付,這樣所有的數(shù)字貨幣用戶就需要使用以太幣來完成合約或者交易。 這樣的話,用戶體驗(yàn)就不好了。我們希望數(shù)字貨幣用戶在交易時(shí),也能夠直接使用數(shù)字貨幣,不需要去考慮以太幣、區(qū)塊鏈以及如何獲取以太幣。 一個(gè)可行的方法是需要自動(dòng)監(jiān)測(cè)賬戶余額,如果余額不足,就禁止交易。
這樣,需要首先創(chuàng)建一個(gè)閾值變量,并提供一個(gè)方法來修改這個(gè)變量。 這個(gè)變量初始值設(shè)置為5 finney (0.005 Ether),即約等于一筆交易用的Gas。
uint public minBalanceForAccounts;
function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
}
之后,修改transfer方法, 對(duì)售幣用戶做充值。
/* Send coins */
function transfer(address _to, uint256 _value) {
...
if(msg.sender.balance < minBalanceForAccounts)
sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
}
或者直接把費(fèi)用支付給sender。
/* Send coins */
function transfer(address _to, uint256 _value) {
...
if(_to.balance<minBalanceForAccounts)
_to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
}
這就確保了這些賬戶不會(huì)由于余額不足而導(dǎo)致交易失敗。
2.7 Proof of Work
在工作量證明體系中(POW, Proof of Work), 一個(gè)簡單的方法是實(shí)現(xiàn)和以太幣一起“合并挖礦”,即礦工不僅獲得以太幣簡歷,也會(huì)同時(shí)得到新數(shù)字貨幣的獎(jiǎng)勵(lì)。 參考這里special keyword coinbase 來了解如何獲取某個(gè)入鏈區(qū)塊的礦工賬戶。
function giveBlockReward() {
balanceOf[block.coinbase] += 1;
}
當(dāng)然,也可以嘗試去設(shè)置一些數(shù)據(jù)難題,任何人解決這個(gè)問題就可以獲取數(shù)字貨幣獎(jiǎng)勵(lì)。 在下一個(gè)例子中,你需要計(jì)算一個(gè)挑戰(zhàn)數(shù)據(jù)的立方根, 計(jì)算成功后,獲取獎(jiǎng)勵(lì),并設(shè)置下一個(gè)挑戰(zhàn)數(shù)據(jù)。
uint public currentChallenge = 1; // 需要計(jì)算出這個(gè)數(shù)據(jù)的立方根?
function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {
require(answerToCurrentReward**3 == currentChallenge); // If answer is wrong do not continue
balanceOf[msg.sender] += 1; // Reward the player
currentChallenge = nextChallenge; // Set the next challenge
}
當(dāng)然,計(jì)算立方根,對(duì)人來說可能比較難,但對(duì)計(jì)算機(jī)來說是很容易的事情。 由于最后一個(gè)贏家可以選擇下一個(gè)挑戰(zhàn)數(shù)據(jù),他們可以選擇一個(gè)對(duì)自己有利的數(shù)據(jù),這樣對(duì)其他礦工來說是不公平的。 這種POW的計(jì)算公式,最好是選擇對(duì)計(jì)算機(jī)來說,有計(jì)算難度,但是很容易驗(yàn)證計(jì)算結(jié)果的公式。 比特幣和以太坊使用的計(jì)算哈希值的方式可以說是目前最好的數(shù)據(jù)難題了。 如果我們要在新的幣種中使用哈希計(jì)算作為數(shù)據(jù)難題, 可以參考如下代碼:
bytes32 public currentChallenge; // The coin starts with a challenge
uint public timeOfLastProof; // Variable to keep track of when rewards were given
uint public difficulty = 10**32; // Difficulty starts reasonably low
function proofOfWork(uint nonce){
bytes8 n = bytes8(sha3(nonce, currentChallenge)); // Generate a random hash based on input
require(n >= bytes8(difficulty)); // Check if its under the difficulty
uint timeSinceLastProof = (now - timeOfLastProof); // Calculate time since last reward was given
require(timeSinceLastProof >= 5 seconds); // Rewards cannot be given too quickly
balanceOf[msg.sender] += timeSinceLastProof / 60 seconds; // The reward to the winner grows by the minute
difficulty = difficulty * 10 minutes / timeSinceLastProof + 1; // Adjusts the difficulty
timeOfLastProof = now; // Reset the counter
currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number - 1)); // Save a hash that will be used as the next proof
}
需要修改下構(gòu)造函數(shù), 添加初始值:
timeOfLastProof = now;
一旦這個(gè)合約開始上線運(yùn)行, 需要選擇一個(gè)工作量證明公式, 并設(shè)置合適的 nonce 值。 如果收到 “Data cant be execute” , 則說明 nounce設(shè)置不合理,重新調(diào)整下。 直到賬戶能夠每分鐘收到1個(gè)數(shù)字貨幣為止,之后計(jì)算難度會(huì)每10分鐘自動(dòng)調(diào)整一次。 這就是挖礦的過程。
2.8 改進(jìn)發(fā)行機(jī)制
最終版本的代碼如下:
pragma solidity ^0.4.16;
contract owned {
address public owner;
function owned() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
/******************************************/
/* ERC20標(biāo)準(zhǔn)的虛幣 */
/******************************************/
contract TokenERC20 {
string public name; // public屬性,數(shù)字貨幣名稱
string public symbol;// 數(shù)字貨幣的符號(hào)
uint8 public decimals = 18; // 18 decimals is the strongly suggested default, avoid changing it
uint256 public totalSupply; //貨幣總供應(yīng)量
mapping (address => uint256) public balanceOf; // 賬戶-余額 映射關(guān)系
mapping (address => mapping (address => uint256)) public allowance; // 二維數(shù)據(jù)[a][b]=amount,指賬戶a授權(quán)給b可動(dòng)用的金額為amount。
/**
*
* 轉(zhuǎn)賬的區(qū)塊鏈?zhǔn)录?/p>
*
**/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
*
* 批準(zhǔn)轉(zhuǎn)賬申請(qǐng)的區(qū)塊鏈?zhǔn)录?/p>
*
**/
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
/**
*
* 發(fā)行更多貨幣的區(qū)塊鏈?zhǔn)录?/p>
*
**/
event Burn(address indexed from, uint256 value);
/**
* 構(gòu)造函數(shù),用來執(zhí)行初始化操作。
*
* 初始化合約,并給合約的創(chuàng)建者提供一定數(shù)額的數(shù)字貨幣
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount
balanceOf[msg.sender] = totalSupply; // 給創(chuàng)建者所有初始貨幣;
name = tokenName; // 設(shè)置本數(shù)字貨幣的顯示名稱。
symbol = tokenSymbol; // 設(shè)置本數(shù)據(jù)貨幣的顯示符號(hào)。
}
/**
* Internal transfer, only can be called by this contract
*/
function _transfer(address _from, address _to, uint _value) internal {
// 避免轉(zhuǎn)賬到0x0的空地址。
require(_to != 0x0);
// 檢查出款賬戶余額是否足夠
require(balanceOf[_from] >= _value);
// 檢查收款賬戶是否會(huì)溢出。
require(balanceOf[_to] + _value > balanceOf[_to]);
// 記錄下轉(zhuǎn)賬前的兩個(gè)賬戶總額,需要確保和轉(zhuǎn)賬后是一致的。
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// 出款賬戶扣款
balanceOf[_from] -= _value;
// 收款賬戶收款
balanceOf[_to] += _value;
// 發(fā)送轉(zhuǎn)賬事件
emit Transfer(_from, _to, _value);
// 確保轉(zhuǎn)賬前后兩個(gè)賬戶總和是一樣的。
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 轉(zhuǎn)賬
*
* 從當(dāng)前賬戶轉(zhuǎn)出給定金額到收款人賬戶上
*
* @param _to 收款人賬戶地址
* @param _value 金額,指當(dāng)前貨幣單位
*/
function transfer(address _to, uint256 _value) public returns (bool success) {
_transfer(msg.sender, _to, _value);
return true;
}
/**
* 轉(zhuǎn)賬
*
* 從出款人賬戶轉(zhuǎn)出給定金額到收款人賬戶
*
* @param _from 出款人賬戶地址
* @param _to 收款人賬戶地址
* @param _value 轉(zhuǎn)賬金額
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // 檢查_from賬戶中可以由發(fā)起人動(dòng)用的金額。
allowance[_from][msg.sender] -= _value; // 扣減授權(quán)金額
_transfer(_from, _to, _value); //執(zhí)行轉(zhuǎn)賬
return true;
}
/**
* 授權(quán)某賬戶允許它從出款人賬戶上扣款。
*
*
* @param _spender 允許扣款的賬戶
* @param _value 允許扣除的最高金額。
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* 批準(zhǔn)并執(zhí)行從收款賬戶發(fā)起的、從本賬戶扣款并轉(zhuǎn)賬的申請(qǐng)。
* 允許 收款賬戶可以得到比預(yù)定金額更多以的幣值, 并通知對(duì)方。
*
* @param _spender 收款賬戶
* @param _value 授權(quán)收款賬戶能夠接收的最大幣值。
* @param _extraData 發(fā)送給合約批準(zhǔn)方的額外信息。
*/
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;
}
}
/**
* 銷毀貨幣
*
* 從當(dāng)前賬戶中銷毀 `_value` 額度的貨幣,本操作不可逆轉(zhuǎn)。
*
* @param _value 待銷毀的貨幣額
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // 檢查當(dāng)前賬戶的余額是否足夠。
balanceOf[msg.sender] -= _value; // 扣減當(dāng)前賬戶余額
totalSupply -= _value; // 更新貨幣總供應(yīng)量
emit Burn(msg.sender, _value);
return true;
}
/**
* 從特定賬戶中銷毀一定額度的貨幣
*
* 從特定賬戶_from中銷毀 `_value` 額度的貨幣,本操作不可逆轉(zhuǎn)。
*
* @param _from 貨幣持有者的賬戶
* @param _value 待銷毀的貨幣額
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // 檢查目標(biāo)賬戶的余額是否足夠。
require(_value <= allowance[_from][msg.sender]); // 檢查授權(quán)給當(dāng)前用戶可動(dòng)用的余額是否足夠。
balanceOf[_from] -= _value; // 扣減目標(biāo)賬戶余額
allowance[_from][msg.sender] -= _value; // 扣減授權(quán)余額。
totalSupply -= _value; // 更新總供應(yīng)量。
emit Burn(_from, _value);
return true;
}
}
/******************************************/
/* 高級(jí)特性的虛幣 */
/******************************************/
contract MyAdvancedToken is owned, TokenERC20 {
uint256 public sellPrice;
uint256 public buyPrice;
mapping (address => bool) public frozenAccount;
/* 賬戶凍結(jié)的事件 */
event FrozenFunds(address target, bool frozen);
/* 初始化賬戶余額和虛幣信息 */
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}
/* 內(nèi)部的轉(zhuǎn)賬接口,只允許內(nèi)部調(diào)用 */
function _transfer(address _from, address _to, uint _value) internal {
require (_to != 0x0); // 禁止向0x0賬戶轉(zhuǎn)賬。
require (balanceOf[_from] >= _value); // 檢查出款賬戶余額是否足夠
require (balanceOf[_to] + _value >= balanceOf[_to]); // 檢查收款賬戶余額避免溢出。
require(!frozenAccount[_from]); // 檢查出款賬戶是否被凍結(jié)
require(!frozenAccount[_to]); // 檢查收款賬戶是否被凍結(jié)
balanceOf[_from] -= _value; // 扣減出款賬戶余額
balanceOf[_to] += _value; // 增加收款賬戶余額
emit Transfer(_from, _to, _value);
}
/// @notice 給目標(biāo)賬戶發(fā)送 挖礦津貼。
/// @param target 目標(biāo)賬戶
/// @param mintedAmount 挖礦津貼
function mintToken(address target, uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
emit Transfer(0, this, mintedAmount);
emit Transfer(this, target, mintedAmount);
}
/// @notice 凍結(jié)/解凍賬戶,凍結(jié)后,賬戶就無法收發(fā)虛幣了。
/// @param target 待凍結(jié)的賬戶
/// @param freeze 凍結(jié)/解凍
function freezeAccount(address target, bool freeze) onlyOwner public {
frozenAccount[target] = freeze;
emit FrozenFunds(target, freeze);
}
/// @notice 設(shè)置虛幣對(duì)以太幣的買賣匯率
/// @param newSellPrice 賣出價(jià)
/// @param newBuyPrice 買入價(jià)
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
/// @notice 買入。注意這里使用的是payable接口,調(diào)用時(shí)候,需要輸入買入金額(以太幣wei為單位),買入成功后,當(dāng)前賬戶增加了amount個(gè)虛幣,所使用的以太幣存入到當(dāng)前合約所在的地址中(不是owner賬戶)。
function buy() payable public {
uint amount = msg.value / buyPrice; // calculates the amount
_transfer(owner, msg.sender, amount); // makes the transfers
}
/// @notice 賣出。
/// @param amount 賣出的數(shù)量
function sell(uint256 amount) public {
address myAddress = this;
require(myAddress.balance >= amount * sellPrice); // 檢查是否有足夠的賣出余額
_transfer(msg.sender, owner, amount); // 轉(zhuǎn)賬
msg.sender.transfer(amount * sellPrice); // 必須在最后一步執(zhí)行以太幣的轉(zhuǎn)賬操作,防止recursion attacks。注意,這里是從當(dāng)前合約所在的地址上轉(zhuǎn)出資金給賣出者。
}
天下數(shù)據(jù)IDC提供香港服務(wù)器、美國服務(wù)器等全球海外服務(wù)器租用托管,是區(qū)域鏈、數(shù)字貨幣、加密貨幣、直銷、流媒體、外貿(mào)、游戲等服務(wù)器解決方案首選品牌。天下數(shù)據(jù)已為多家企業(yè)提供區(qū)塊鏈服務(wù)器租用托管解決方案,為他們的區(qū)塊鏈安全提供支持!具體詳詢?cè)诰客服!
產(chǎn)品與服務(wù)
香港服務(wù)器 香港高防服務(wù)器 美國服務(wù)器 韓國服務(wù)器 新加坡服務(wù)器 日本服務(wù)器 臺(tái)灣服務(wù)器云服務(wù)器
香港云主機(jī) 美國云主機(jī) 韓國云主機(jī) 新加坡云主機(jī) 臺(tái)灣云主機(jī) 日本云主機(jī) 德國云主機(jī) 全球云主機(jī)高防專線
海外高防IP 海外無限防御 SSL證書 高防CDN套餐 全球節(jié)點(diǎn)定制 全球?qū)>GPLC關(guān)于我們
關(guān)于天下數(shù)據(jù) 數(shù)據(jù)招商加盟 天下數(shù)據(jù)合作伙伴 天下數(shù)據(jù)團(tuán)隊(duì)建設(shè) 加入天下數(shù)據(jù) 媒體報(bào)道 榮譽(yù)資質(zhì) 付款方式關(guān)注我們
微信公眾賬號(hào)
新浪微博
天下數(shù)據(jù)手機(jī)站 關(guān)于天下數(shù)據(jù) 聯(lián)系我們 誠聘英才 付款方式 幫助中心 網(wǎng)站備案 解決方案 域名注冊(cè) 網(wǎng)站地圖
天下數(shù)據(jù)18年專注海外香港服務(wù)器、美國服務(wù)器、海外云主機(jī)、海外vps主機(jī)租用托管以及服務(wù)器解決方案-做天下最好的IDC服務(wù)商
《中華人民共和國增值電信業(yè)務(wù)經(jīng)營許可證》 ISP證:粵ICP備07026347號(hào)
朗信天下發(fā)展有限公司(控股)深圳市朗玥科技有限公司(運(yùn)營)聯(lián)合版權(quán)
深圳總部:中國.深圳市南山區(qū)深圳國際創(chuàng)新谷6棟B座10層 香港總部:香港上環(huán)蘇杭街49-51號(hào)建安商業(yè)大廈7樓
7×24小時(shí)服務(wù)熱線:4006388808香港服務(wù)電話:+852 67031102
本網(wǎng)站的域名注冊(cè)業(yè)務(wù)代理北京新網(wǎng)數(shù)碼信息技術(shù)有限公司的產(chǎn)品