2018 年 4 月份,美链 BeautyChain (BEC) 爆出了智能合约漏洞,导致市值 60 亿的代币价格几乎归零,这在币圈里也算是一场大事件了。
那么漏洞主要出在什么地方呢?主要是 BEC 在标准的 ERC20 接口之外,自己添加了一个 batchTransfer 接口。一般而言,添加这种接口主要是为了便于空投时批量转账,可是 BEC 在这个接口设计上犯了两个错误:
- 没有使用安全的数值计算,存在数值溢出漏洞。这也是在网上被广泛传播的漏洞分析。
- 没有限定 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 标准接口带来的风险。
您好,请问你那个批量转账1000个成功后转账数量才0.000000000000001个 ["1000", "1000", "1000"]
所有直接调用合约接口的地方都要考虑精度问题。你这个币的精度应该是 18 位,所以你必须 1000*精度才是真正转账金额。
你的钱可能是十八位,然后需要再多加18个零
您好,请问一下,这个我用remix 执行 总报错啊,没办法空投出去呢?
我之前用 remix 也错过几次,你需要仔细检查:1. 地址有没有弄错。这里有代币合约地址、空投合约地址、持币账户地址,弄错一个肯定就会出问题。2. 精度有没有弄错。因为都是调用ABI接口,需要自己乘上精度,很容易会忘记。
你好,第一步部署合约以后。如果关闭了remix,以后想要再次调用这个合约,是怎样操作的呢?