ERC20 Token流通代码实现

在上一篇文章中我们掌握了如何发行自己的ERC20 Token。如果只是单纯发一个Token是没有任何意义的,Token应该是可以跟我们的产品运营结合,用于用户激励、产品推广等方面。就像交易所平台的平台币一样,既可以用来抵消交易费用,又可以用于投票等。这也是目前token真正有使用到的方式吧,就是当做平台的一种资产,这种资产是基于区块链的,无法串改的。而平台的运行还是中心化的运行方式,就像币乎一样。

那么在平台上的Token到底是怎么流通的呢?以币乎为例,币乎的key是基于以太坊的ERC20 Token。我们知道在币乎上用户点赞会有奖励,评论点赞也有奖励,如果这种结算都放在了以太坊上来做的话,一个是速度可能会很慢,另一个结算的费用也会非常高。所以我们可以看到币乎里面有还未开放的充币,提币功能。这就是说,你只有提币的时候,币乎才会把你有的币转到你的以太坊钱包地址上。充币的话,币乎会给你一个以太坊地址,你把币转到这个地址上就行。这个做法应该是跟交易所的做法一样。那么前面提到的点赞奖励是怎么处理的呢?我的理解是,币乎在结算的时候会在你币乎账号key总数加上奖励给你的key,只是简单的数据库修改下数据,并不会涉及token的转让流通。

那么站在程序角度来看,我们应该怎么用代码来实现Token的充token、提token操作呢?

以太坊提供了非常好用的工具来跟以太坊通信,web3.js 就是其中之一。下面我就介绍如果使用 web3.js实现token的充token,提token。

下面的内容假设您已经了解以太坊、智能合约、solidity以及web3.js。不懂的话,建议先去了解下,网上已经有非常好的文章介绍了,我这里就不重复说明了。

首先你得在本地安装好 node.js(这个也不多做介绍)。

首先介绍下ganache。 ganache是一个基于内存的以太坊链,用于本地测试,而不用去连接测试网络,省去同步以太坊区块的麻烦。安装也很简单,命令行执行 npm install -g ganache-cli

安装完成后,执行ganache-cli,启动网络。

之前的文章,把Token发布到了ropsten测试网络上,这次我们选择本地环境,把token发布到我们本地网络上。首先在metamask中网络选择自定义网络,如下图:


image.png

确定后,在把我们的MFC Token重新发布一遍(参见上一篇文章)。

好了,我们已经把Token发布到了本地网络了。接下来使用web3.js来调用本地测试网络。

为了方便测试,我是直接在页面上调用本地测试网络,真正的做法应该是调用服务器接口,服务器内有相应的服务调用以太坊网络。这里只是为了方便展示才这样做的,大家不要以这样的方式上线。

我们可以总结出几个必须有的功能: 1 查询余额, 2 Token提现,3 充Token,4 账户之间转以太。

首先实现如何查询账户余额的功能,包括以太坊余额和MFC余额。看如下代码。

// 首先在页面引入我们需要使用的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"));
}
// 使用本地测试网络的第一个账户为默认账户
web3.eth.defaultAccount = web3.eth.accounts[0];

// 从remix compile tab页中MyFreeCoin的 detail中复制 abi信息到 contract() 方法内,不懂remix的请看前面那边文章
var mfcContract = web3.eth.contract([{...}]);

//  从remix run tab页发布的合约上复制合约地址
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。 实现代码如下:

// 获取提token的以太坊地址
var reciver = document.getElementById('reciver').value;
// 提token数量
var amount = document.getElementById('amount').value;
if(!web3.isAddress(reciver)){ // 检测地址是否合法
    alert("reciver: Invalid address!");
    return ;
}
// 发送token,注意这里默认是从本地测试网络的第一个账户转token过去,因为我们的默认账户就是它 web3.eth.defaultAccount = web3.eth.accounts[0]; 同时在remix发布合约的时候,我们是使用第一个账户发布的,也就是说第一个账户开始拥有所有的MFC Token
contract.transfer(reciver, web3.toWei(amount), function(err, resp){
    if(!err){
        console.log(resp);
        alert('success');
    }else{
        console.log(err);
    }
})

这就实现了从发布合约的默认账户转token到用户地址的功能。但是这么做是比较危险的,因为您把所有的token都放到了一个联网的账户中,如果平台被黑客入侵,就有可能把您所有的Token转走,风险会比较大
。所以正常的做法应该是,把包含大量token的账户保存在冷钱包里面,只在需要使用的时候才从冷钱包里面提Token到运营的账户地址中。那就是说我们需要实现两个地址之间转token的功能,不能像上面那样默认使用发Token的账户来转Token了。那么我们应该怎么来实现呢?看下面的代码


// 假设这个是我们的运营钱包地址
var from = '0x009ddafb6dd10f2ed72dd0d0c7f291b5a0cea9eb';
// 转token的数量
var amount = document.getElementById('mAmount').value;
// 提token的地址
var to = document.getElementById('mTo').value;

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

// 获取当前交易数,当nonce使用
web3.eth.getTransactionCount(from, function(err, count){
    if(!err){
        var tcount = count;
        // 先检查当前运营账户的余额够不够转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()
              };
              
              // 运营钱包地址的私钥,需要用它来签名交易
              var privKey = new Buffer("0x811502c101ae015b658dccbdb6626840b734c40ba229ce78877826c27ca07513", 'hex');
              // 对交易签名
              var tx = new ethereumjs.Tx(rawTransaction);
              tx.sign(privKey);
              var serializedTx = tx.serialize();
              
              // 发送交易到以太坊
              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);
    }

这样就可以实现不同账户之间的转Token操作了。

充Token。首先我们要理解下充Token,是用户从他钱包地址转Token到我们平台的账户地址。所以首先我们得给用户提供一个跟他对应的钱包地址,同时要监控用户的转Token事件,当用户转Token过来了,我们需要记录,同时在数据库中把用户的Token余额增加。所以我们得先搞定给用户提供一个钱包地址,这里我们使用到了Ethereum-Wallet来帮我们生成地址,公钥,私钥。代码如下:

// 上面已经引入了Ethereum-Wallet相应的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();

但是还有一个问题,当用户转token过来,我们怎么知道他有转呢?在我们的Token只能合约里面每次转Token的都会触发Transfer事件(solidity知识),我们只需要监听这个事件就可以了,代码如下:

// 监听 转token事件
contract.Transfer( function(err, res) {
    if(!err){
        console.log(res);
        // 发送Token的地址
        var from =  res.args._from;
        // 接受Token的地址 所以我们只需要对比下 接受Token的地址,是我们自己的地址,那就可以给相应的用户增加充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);
    }
})

账户之间发送eth。有一些新生成的账户开始是没有eth的,我们需要给它转的eth,以供它使用,代码如下:

// 接受eth地址
var etherAdress = document.getElementById('etherAdress').value;
if(!web3.isAddress(etherAdress)){
    alert('Invalid address');
    return ;
}
// 发送交易, 转eth,这里使用的默认账户转eth,如果想使用另外的地址转也跟 转token一样,使用私钥来签名交易即可
web3.eth.sendTransaction({
    to: etherAdress,
    value: '1000000000000000000'
},function(err, result){
    if(!err){
        console.log(result);
    }else{
        console.log(err);
    }
})

这样Token之间的流通代码实现已经完成了,具体的代码实现可以在github上查看。

上面提到的肯定还有不足的地方,可能我理解也是错误的,也请大家指出错误的地方。大家共同进步。

Token已经知道了如何流通了,但是艾西欧又是怎么一回事呢?为何给一个地址转eth就可以得到Token呢?如何发布一个艾西欧合约呢?这也是后面我会继续研究的,自己发行一个艾西欧来玩玩,当然是在测试环境!


2018/03/17 更新

有同学指出账户间的转账有问题,经过我的测试发现确实是存在一个问题,就是账户之间转账的时候,默认转的还是web3.eth.defaultAccout 账户的token,而没有转经过私钥签名的账户。

后面查看web3.js 的文档,文档的web3.js 对应的版本是1.0,但是我们使用的还是0.x版本。 而且1.0版本的api 跟之前的版本变化非常大。

我重新使用web3.js 1.x 的版本重新实现了一遍。 测试用户间的转账是没有问题的。修改后的代码已经上传到github, 参考文件 mfc2.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 在最美丽的青春遇见了你,3年了,不久也不短,我一直想找一个与你不相同的人,但到最后我发现我自己变成了另一个你。你,...
    TyumenWu阅读 132评论 0 2
  • 5月23日 星期四 晴转雨 今天早上,到9:00了,珠心算老师拿了两个小奖杯,...
    曾博韬阅读 307评论 2 5
  • “沾沾自喜”是用来形容格局小的人, “欲求不满”是用来形容格局大的人。 我认识的所谓名牌大学的人都说自己是个渣渣,...
    一饼张阅读 215评论 4 1