以太坊智能合约之批量转币
一直想写这篇教程来着,因为你会发现网络上很少有关于批量转币的详尽的教程,一些提供该工具的网站也并不会将其智能合约代码开源出来。虽然最终我们会发现原来这个批量转币的智能合约原来就这么几行代码就够啦,但是整个流程不明所以的话,给你这个合约代码我相信你也是用不了的,因此对于新手来说想要写出这个功能多少还是有不小的难度的。
批量转币其实也属于空投的一种,所谓空投,笔者的理解是利用程序或者人工的方式,将代币发放至目标账户。
注意:这里所说的代币均指代的是 ERC20 Token,对于更新的代币例如 ERC721 Token 等,由于本身就优化了这方面,因此不需要我们这样折腾。
用途
我们为什么需要批量转币这样的智能合约呢?
- 大大节约转币的资金成本。很明显,如果我们人工一次次的进行转币操作,那么转个几百次所需要的 gas 费用会让你吃一惊。但是使用这个批量转币的智能合约,一般来说,两百次左右可以一次性操作完,那么也就是两百次转币费只需要支付一次转币费即可。
- 大大节约转币的人工成本。和支付费用成本类似,让你手工转个几百次估计怕是要疯掉了。
- 有足够的需求。前面也讲到,新的 ERC721 Token 等不需要我们这样折腾,但是目前 ERC20 Token 几乎是绝大多数代币的模板,因此需求足够大,也就是应用场景足够大。
前置知识点
- 合约调用合约
- 授权转账方法
合约调用合约
要在智能合约中调用其他合约,就需要了解抽象合约的概念,具体细节可以看这里。
以本文所要讲的转币合约为例,我们要在合约中调用 ERC20 Token 的 transferFrom
方法,那么我们首先要先创建一个 ERC20 Token 的抽象合约,代码如下:
/** * @dev ERC20 Token abstract constract. */ contract ERC20Token { function transferFrom(address, address, uint256) public returns (bool); }
因为我们只需要用到代币的 transferFrom
方法,因此我们只需要声明该方法即可,注意是没有函数体的空方法。
那么我们在批量转币的合约中如何进行调用呢?和大多数面向对象的编程语言一样,我们只需要创建这个代币合约,然后调用这个代币合约实例的授权转账方法即可,代码如下:
/** * @dev calls the ERC20 token's transferFrom function * @param _token address The address of ERC20 token. * @param _dsts address The addresses which be air dropped. * @param _values uint256 The token values that each address will receive. */ function transfer(address _token, address[] _dsts, uint256[] _values) public payable { ERC20Token token = ERC20Token(_token); for (uint256 i = 0; i < _dsts.length; i++) { token.transferFrom(msg.sender, _dsts[i], _values[i]); } }
我们的批量转币代码是不是非常简单~可以看到,我们首先创建了 ERC20 Token 的合约实例,然后循环要转币的地址列表,每次遍历中调用目标 ERC20 Token 的 transferFrom
方法即可。
这个函数可以做的更好:对于代码边际判断,我们可以判断一下 _token 以及 _dsts 是否均为合法账户,或者判断一下 _values 是否均为合法正数值,又或者比较一下 _dsts 和 _values 的数组长度是否相等(健壮的代码一定是边际安全的)。。。是否还能完善的更好一些?当然是可以的,比如我们增加一个 Event 事件供调用者监听以便做出相应处理。。。
授权转账方法
那么是不是上面的这些代码就足够了呢?对于批量转币合约来说核心代码就是这些。
但是!有个前提!
我们在使用这个合约转币前,需要授权给这个合约一定的代币数额、授权给这个合约一定的代币数额、授权给这个合约一定的代币数额。
这里就引出了授权转账的概念,还是以批量转币为例:
首先我们需要明确,假如你拥有 1000 个 SIM 代币,那么这些代币就独属于你,没人能拿走(别给我瞎扯什么被盗啊之类的情况,讲例子的时候我们纯真些。。。)那么如果你啥也不做,想要让转币合约能够转走你的 SIM 代币,显然这是不可能的,因为是你拥有这些代币,而不是转币合约拥有。
所以此时你就需要把自己的 SIM 代币授权给转币合约,至于授权多少看你要转多少。一旦你完成授权操作,那么转币合约就有权利将你的代币进行转账操作了(等下,我可以之后突然改主意取消授权吗?可以,你再次授权一次,数目为零即可。)
案例
笔者之前花了点时间做了一个 Web 页面,将批量转币的功能做在了上面,下面我们简单的看下整个流程,配合示例图会更加清晰 :)
第一步主要是获取用户需要批量转账的代币信息,通过用户填写的代币合约地址,然后获取到基本的代币信息。
第二步主要是获取用户转币的数目和账户信息,然后需要让用户授权相应数目的代币给我们的转币合约。
第三步主要是让用户确认信息,并开始空投。
这是批量转 ERC20 代币的交易记录:
Ropsten Transaction 0x630b868e41ecfbd97273371ed33242439230355d9110095b814ae3029b88d829
扩展
这里笔者给新手们留个作业,如果我想要批量转 ETH 怎么办?如果直接使用上面的代码可不好使哦,但是思路是一毛一样的,而且代码上会更简洁~
附上一个批量转 ETH 的交易记录:
Ropsten Transaction 0x4fcfc6245d7ffecfae7c02eb2f983f203806b83b5a1cf2fc0450a61d7c226401
哦,大家可能还会看到另外一种批量转币的合约代码的写法,代码如下:
bytes4 id = bytes4(keccak256("transferFrom(address,address,uint256)")); token_address.call(id, msg.sender, _dsts[i], _valus[i]);
这在低版本的 Solidity 中是可以正常运行的,因为本质上和上面讲的是一样的,但在高版本中废弃了该用法,因此笔者在尝试高版本中使用会出现每次批量转币的最后一次总是会失败。所以推荐的做法是通过抽象合约的方式进行调用其他合约的方法。
最后,希望大家在区块链的世界中玩的开心~