专门用于批量空投的 ETH 智能合约

目录 区块链

2018 年 4 月份,美链 BeautyChain (BEC) 爆出了智能合约漏洞,导致市值 60 亿的代币价格几乎归零,这在币圈里也算是一场大事件了。

那么漏洞主要出在什么地方呢?主要是 BEC 在标准的 ERC20 接口之外,自己添加了一个 batchTransfer 接口。一般而言,添加这种接口主要是为了便于空投时批量转账,可是 BEC 在这个接口设计上犯了两个错误:

  1. 没有使用安全的数值计算,存在数值溢出漏洞。这也是在网上被广泛传播的漏洞分析。
  2. 没有限定 batchTransfer 的使用范围。如果它限制了 batchTransfer 只能合约拥有者调用,即使存在漏洞也不能被人利用了。

BEC 为了便于空投增加的这个接口可谓代价惨重,但如果他们知道还有不需要修改代币主合约就能批量转账的办法,不知道会不会吐一口老血?

批量转账,指的是在一笔 ETH 交易中转多笔代币到不同的账户,一般用于 ERC20 代币项目启动时对用户进行空投(有人叫糖果发放)。

批量空投的好处主要有两个,一是省 GAS 费,但事实上省得不多;二是省时间,这是最主要目的。以太坊是以交易为粒度打包,如果转账只能单对单,即使一次发起多笔单对单的交易,等待这些交易被打包的时间也非常漫长,而且还有笔数上限限制。将多笔转账放到同一个交易中,被打包确认的速度就会非常快。一般 ERC20 代币项目启动时都会大撒币,空投地址动辄都是几万几十万,批量空投接口对效率会有上百倍的提升。

空投合约基本原理:ERC20 可以通过 approve 和 transferFrom 两个接口授权其它地址一定的额度。那既然是这样,我们也可以授权一个合约地址来花自己的代币,如果这个合约支持批量转账,那么就可以通过这个合约来实现批量空投了。

下面是具体实操流程:

假设已经存在一个 ERC20 代币的合约,合约地址为“TOKEN_ADDR”,而你的钱包里已经有了 100 万 TOKEN,你的钱包地址是“WALLET_ADDR”。

STEP1: 用自己的钱包部署支持批量转账的空投合约,假设创建成功后地址为“AD_ADDR”。下面给出最关键的部分,完整合约参考 github 链接 。这里 transferFrom 取的是 msg.sender,理论上来讲不加 onlyOwner 限定这个合约也可以给其它人使用,但为了安全还是加上较为稳妥。

contract Airdropper is Ownable {
    function multisend(address _tokenAddr, address[] dests,
                       uint256[] values) public onlyOwner returns (uint256) {
        uint256 i = 0;
        while (i < dests.length) {
           ERC20(_tokenAddr).transferFrom(msg.sender, dests[i], values[i]);
           i += 1;
        }
        return(i);
    }
}

STEP2: 用自己的钱包授权空投合约地址 AD_ADDR 100 万 TOKEN 额度。即通过代码或者 remix 执行 approve(AD_ADDR, 1000000*精度),注意这是 ERC20 合约里的接口,需要将交易发往 TOKEN_ADDR。

STEP3: 检查 AD_ADDR 是否得到了 100 万 TOKEN 授权。通过代码或者 remix 执行 allowance(WALLET_ADDR, AD_ADDR),如果结果是 100 万 TOKEN,说明空投合约已经得到你的 100万额度授权。

STEP4: 用自己的钱包调用空投合约的 multisend 接口发起批量空投。通过代码或者 remix 执行 multisend(TOKEN_ADDR, [addr1, addr2, ...], [value1, value2, ...]),执行成功即能实现批量转账。这是空投合约里的接口,需要将交易发往 AD_ADDR。

这里最容易混淆的是几个地址,TOKEN_ADDR/WALLET_ADDR/AD_ADDR,在每一步操作中要想明白调用的是哪个合约的接口,参数应该填哪个地址。 第一步只需要操作一次,第四步可以操作很多次,第二步可根据需求随时调整授权额度。

有了这个空投合约,你就可以将自己钱包里任意类型代币都通过批量转账方式空投出去。额度可控,任意账户均可用,还避免了在代币主合约里额外增加非 ERC20 标准接口带来的风险。