ERC20 Token流通代碼實現(xiàn)

在上一篇文章中我們掌握了如何發(fā)行自己的ERC20 Token。如果只是單純發(fā)一個Token是沒有任何意義的,Token應(yīng)該是可以跟我們的產(chǎn)品運(yùn)營結(jié)合,用于用戶激勵、產(chǎn)品推廣等方面。就像交易所平臺的平臺幣一樣,既可以用來抵消交易費(fèi)用,又可以用于投票等。這也是目前token真正有使用到的方式吧,就是當(dāng)做平臺的一種資產(chǎn),這種資產(chǎn)是基于區(qū)塊鏈的,無法串改的。而平臺的運(yùn)行還是中心化的運(yùn)行方式,就像幣乎一樣。

那么在平臺上的Token到底是怎么流通的呢?以幣乎為例,幣乎的key是基于以太坊的ERC20 Token。我們知道在幣乎上用戶點贊會有獎勵,評論點贊也有獎勵,如果這種結(jié)算都放在了以太坊上來做的話,一個是速度可能會很慢,另一個結(jié)算的費(fèi)用也會非常高。所以我們可以看到幣乎里面有還未開放的充幣,提幣功能。這就是說,你只有提幣的時候,幣乎才會把你有的幣轉(zhuǎn)到你的以太坊錢包地址上。充幣的話,幣乎會給你一個以太坊地址,你把幣轉(zhuǎn)到這個地址上就行。這個做法應(yīng)該是跟交易所的做法一樣。那么前面提到的點贊獎勵是怎么處理的呢?我的理解是,幣乎在結(jié)算的時候會在你幣乎賬號key總數(shù)加上獎勵給你的key,只是簡單的數(shù)據(jù)庫修改下數(shù)據(jù),并不會涉及token的轉(zhuǎn)讓流通。

那么站在程序角度來看,我們應(yīng)該怎么用代碼來實現(xiàn)Token的充token、提t(yī)oken操作呢?

以太坊提供了非常好用的工具來跟以太坊通信,web3.js 就是其中之一。下面我就介紹如果使用 web3.js實現(xiàn)token的充token,提t(yī)oken。

下面的內(nèi)容假設(shè)您已經(jīng)了解以太坊、智能合約、solidity以及web3.js。不懂的話,建議先去了解下,網(wǎng)上已經(jīng)有非常好的文章介紹了,我這里就不重復(fù)說明了。

首先你得在本地安裝好 node.js(這個也不多做介紹)。

首先介紹下ganache。 ganache是一個基于內(nèi)存的以太坊鏈,用于本地測試,而不用去連接測試網(wǎng)絡(luò),省去同步以太坊區(qū)塊的麻煩。安裝也很簡單,命令行執(zhí)行 npm install -g ganache-cli。

安裝完成后,執(zhí)行ganache-cli,啟動網(wǎng)絡(luò)。

之前的文章,把Token發(fā)布到了ropsten測試網(wǎng)絡(luò)上,這次我們選擇本地環(huán)境,把token發(fā)布到我們本地網(wǎng)絡(luò)上。首先在metamask中網(wǎng)絡(luò)選擇自定義網(wǎng)絡(luò),如下圖:


image.png

確定后,在把我們的MFC Token重新發(fā)布一遍(參見上一篇文章)。

好了,我們已經(jīng)把Token發(fā)布到了本地網(wǎng)絡(luò)了。接下來使用web3.js來調(diào)用本地測試網(wǎng)絡(luò)。

為了方便測試,我是直接在頁面上調(diào)用本地測試網(wǎng)絡(luò),真正的做法應(yīng)該是調(diào)用服務(wù)器接口,服務(wù)器內(nèi)有相應(yīng)的服務(wù)調(diào)用以太坊網(wǎng)絡(luò)。這里只是為了方便展示才這樣做的,大家不要以這樣的方式上線。

我們可以總結(jié)出幾個必須有的功能: 1 查詢余額, 2 Token提現(xiàn),3 充Token,4 賬戶之間轉(zhuǎn)以太。

首先實現(xiàn)如何查詢賬戶余額的功能,包括以太坊余額和MFC余額??慈缦麓a。

// 首先在頁面引入我們需要使用的js
<script src="./web3.min.js"></script>
<script src="./ethereumjs-wallet-0.6.0.min.js"></script>

<script>
// 初始化web3 對象
if (typeof web3 !== 'undefined') {
  web3 = new Web3(web3.currentProvider);
} else {
  // set the provider you want from Web3.providers
  web3 = new Web3(new  Web3.providers.HttpProvider("http://localhost:8545"));
}
// 使用本地測試網(wǎng)絡(luò)的第一個賬戶為默認(rèn)賬戶
web3.eth.defaultAccount = web3.eth.accounts[0];

// 從remix compile tab頁中MyFreeCoin的 detail中復(fù)制 abi信息到 contract() 方法內(nèi),不懂remix的請看前面那邊文章
var mfcContract = web3.eth.contract([{...}]);

//  從remix run tab頁發(fā)布的合約上復(fù)制合約地址
var contractAddress = '0x09901f8fdf2265d9be48c7877161a2f6c2e503e8';
// 初始化我們的合約對象
var contract = mfcContract.at(contractAddress);


// 獲取我們需要查詢的以太坊地址
var address = document.getElementById('address').value;
if(!web3.isAddress(address)){ // 檢測地址是合法的以太坊地址
    alert("Invalid address!");
    return ;
}
// 查詢以太坊地址的 eth余額
web3.eth.getBalance(address, function(err, resp){
    if(!err){
        eth.innerHTML = web3.fromWei(resp, 'ether').toNumber();
    }else{
        console.log(err);
    }

});
// 查詢MFC的余額
contract.balanceOf(address, (err, tkns) => {
    if (!err) {
      mfc.innerHTML = web3.fromWei(tkns, 'ether').toNumber()
    }else{
        console.log(err)
    }
    
  })
</script>

提Token。 提Token的操作是,我們提供自己的以太坊地址給平臺,平臺給這個地址打 Token。 實現(xiàn)代碼如下:

// 獲取提t(yī)oken的以太坊地址
var reciver = document.getElementById('reciver').value;
// 提t(yī)oken數(shù)量
var amount = document.getElementById('amount').value;
if(!web3.isAddress(reciver)){ // 檢測地址是否合法
    alert("reciver: Invalid address!");
    return ;
}
// 發(fā)送token,注意這里默認(rèn)是從本地測試網(wǎng)絡(luò)的第一個賬戶轉(zhuǎn)token過去,因為我們的默認(rèn)賬戶就是它 web3.eth.defaultAccount = web3.eth.accounts[0]; 同時在remix發(fā)布合約的時候,我們是使用第一個賬戶發(fā)布的,也就是說第一個賬戶開始擁有所有的MFC Token
contract.transfer(reciver, web3.toWei(amount), function(err, resp){
    if(!err){
        console.log(resp);
        alert('success');
    }else{
        console.log(err);
    }
})

這就實現(xiàn)了從發(fā)布合約的默認(rèn)賬戶轉(zhuǎn)token到用戶地址的功能。但是這么做是比較危險的,因為您把所有的token都放到了一個聯(lián)網(wǎng)的賬戶中,如果平臺被黑客入侵,就有可能把您所有的Token轉(zhuǎn)走,風(fēng)險會比較大
。所以正常的做法應(yīng)該是,把包含大量token的賬戶保存在冷錢包里面,只在需要使用的時候才從冷錢包里面提Token到運(yùn)營的賬戶地址中。那就是說我們需要實現(xiàn)兩個地址之間轉(zhuǎn)token的功能,不能像上面那樣默認(rèn)使用發(fā)Token的賬戶來轉(zhuǎn)Token了。那么我們應(yīng)該怎么來實現(xiàn)呢?看下面的代碼


// 假設(shè)這個是我們的運(yùn)營錢包地址
var from = '0x009ddafb6dd10f2ed72dd0d0c7f291b5a0cea9eb';
// 轉(zhuǎn)token的數(shù)量
var amount = document.getElementById('mAmount').value;
// 提t(yī)oken的地址
var to = document.getElementById('mTo').value;

if(!web3.isAddress(to)){
    alert("Invalid to address");
    return ;
}

// 獲取當(dāng)前交易數(shù),當(dāng)nonce使用
web3.eth.getTransactionCount(from, function(err, count){
    if(!err){
        var tcount = count;
        // 先檢查當(dāng)前運(yùn)營賬戶的余額夠不夠轉(zhuǎn)token
        contract.balanceOf(from, (err, tkns) => {
            if (!err) {
               var balance = web3.fromWei(tkns, 'ether').toNumber();
               if(balance < amount){
                    alert('You do not get enough mfc token!');
                    return ;
               }
               amount = web3.toWei(amount);
               // 交易信息
               var rawTransaction = {
                  "from": from,
                  "nonce": "0x" + tcount.toString(16),
                  "gasPrice": "0x003B9ACA00",
                  "gasLimit": "0x250CA",
                  "to": contractAddress,
                  "value": "0x0",
                  "data": contract.transfer(to, amount).encodeABI()
              };
              
              // 運(yùn)營錢包地址的私鑰,需要用它來簽名交易
              var privKey = new Buffer("0x811502c101ae015b658dccbdb6626840b734c40ba229ce78877826c27ca07513", 'hex');
              // 對交易簽名
              var tx = new ethereumjs.Tx(rawTransaction);
              tx.sign(privKey);
              var serializedTx = tx.serialize();
              
              // 發(fā)送交易到以太坊
              web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'),function(err, result){
                if(!err){
                    console.log(result);
                }else{
                    console.log(err);
                }
              });
            }else{
                console.log(err)
            }
            
        })
    }else{
        console.log(error);
    }

這樣就可以實現(xiàn)不同賬戶之間的轉(zhuǎn)Token操作了。

充Token。首先我們要理解下充Token,是用戶從他錢包地址轉(zhuǎn)Token到我們平臺的賬戶地址。所以首先我們得給用戶提供一個跟他對應(yīng)的錢包地址,同時要監(jiān)控用戶的轉(zhuǎn)Token事件,當(dāng)用戶轉(zhuǎn)Token過來了,我們需要記錄,同時在數(shù)據(jù)庫中把用戶的Token余額增加。所以我們得先搞定給用戶提供一個錢包地址,這里我們使用到了Ethereum-Wallet來幫我們生成地址,公鑰,私鑰。代碼如下:

// 上面已經(jīng)引入了Ethereum-Wallet相應(yīng)的js

// 用戶生成地址的密碼
var password = document.getElementById('password').value;
// 初始化錢包對象
var wallet = ethereumjs.Wallet.generate(password);

// 獲取私鑰
document.getElementById('gPrikey').innerHTML = "private key:" + wallet.getPrivateKeyString();
//獲取公鑰
document.getElementById('gPubkey').innerHTML = "public key:" + wallet.getPublicKeyString();
// 獲取地址
document.getElementById('gAddress').innerHTML = "address:" + wallet.getAddressString();

但是還有一個問題,當(dāng)用戶轉(zhuǎn)token過來,我們怎么知道他有轉(zhuǎn)呢?在我們的Token只能合約里面每次轉(zhuǎn)Token的都會觸發(fā)Transfer事件(solidity知識),我們只需要監(jiān)聽這個事件就可以了,代碼如下:

// 監(jiān)聽 轉(zhuǎn)token事件
contract.Transfer( function(err, res) {
    if(!err){
        console.log(res);
        // 發(fā)送Token的地址
        var from =  res.args._from;
        // 接受Token的地址 所以我們只需要對比下 接受Token的地址,是我們自己的地址,那就可以給相應(yīng)的用戶增加充token記錄了。
        var to = res.args._to;
        var amount = web3.fromWei(res.args._value).toNumber();
        var html = '<p>From: '+ from+';To: '+ to+';Amount: '+amount+'</p>';
        document.getElementById('logs').insertAdjacentHTML('beforeend', html);
    }else{
        console.log(err);
    }
})

賬戶之間發(fā)送eth。有一些新生成的賬戶開始是沒有eth的,我們需要給它轉(zhuǎn)的eth,以供它使用,代碼如下:

// 接受eth地址
var etherAdress = document.getElementById('etherAdress').value;
if(!web3.isAddress(etherAdress)){
    alert('Invalid address');
    return ;
}
// 發(fā)送交易, 轉(zhuǎn)eth,這里使用的默認(rèn)賬戶轉(zhuǎn)eth,如果想使用另外的地址轉(zhuǎn)也跟 轉(zhuǎn)token一樣,使用私鑰來簽名交易即可
web3.eth.sendTransaction({
    to: etherAdress,
    value: '1000000000000000000'
},function(err, result){
    if(!err){
        console.log(result);
    }else{
        console.log(err);
    }
})

這樣Token之間的流通代碼實現(xiàn)已經(jīng)完成了,具體的代碼實現(xiàn)可以在github上查看。

上面提到的肯定還有不足的地方,可能我理解也是錯誤的,也請大家指出錯誤的地方。大家共同進(jìn)步。

Token已經(jīng)知道了如何流通了,但是艾西歐又是怎么一回事呢?為何給一個地址轉(zhuǎn)eth就可以得到Token呢?如何發(fā)布一個艾西歐合約呢?這也是后面我會繼續(xù)研究的,自己發(fā)行一個艾西歐來玩玩,當(dāng)然是在測試環(huán)境!


2018/03/17 更新

有同學(xué)指出賬戶間的轉(zhuǎn)賬有問題,經(jīng)過我的測試發(fā)現(xiàn)確實是存在一個問題,就是賬戶之間轉(zhuǎn)賬的時候,默認(rèn)轉(zhuǎn)的還是web3.eth.defaultAccout 賬戶的token,而沒有轉(zhuǎn)經(jīng)過私鑰簽名的賬戶。

后面查看web3.js 的文檔,文檔的web3.js 對應(yīng)的版本是1.0,但是我們使用的還是0.x版本。 而且1.0版本的api 跟之前的版本變化非常大。

我重新使用web3.js 1.x 的版本重新實現(xiàn)了一遍。 測試用戶間的轉(zhuǎn)賬是沒有問題的。修改后的代碼已經(jīng)上傳到github, 參考文件 mfc2.html。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在最美麗的青春遇見了你,3年了,不久也不短,我一直想找一個與你不相同的人,但到最后我發(fā)現(xiàn)我自己變成了另一個你。你,...
    TyumenWu閱讀 153評論 0 2
  • 5月23日 星期四 晴轉(zhuǎn)雨 今天早上,到9:00了,珠心算老師拿了兩個小獎杯,...
    曾博韜閱讀 355評論 2 5
  • “沾沾自喜”是用來形容格局小的人, “欲求不滿”是用來形容格局大的人。 我認(rèn)識的所謂名牌大學(xué)的人都說自己是個渣渣,...
    一餅張閱讀 261評論 4 1

友情鏈接更多精彩內(nèi)容