前段时间 fcoin 的空投把 eth 堵得不成样,看见好几个空投竟然是一个个地 transfer转账,但是实际上如果用合约实现批量转账,不管是成功率还是效率都会高很多,还省 gas。
整个过程的模型如下:
本文讲解如何用合约实现批量转账,下面直接上合约。
pragma solidity ^0.4.23;
contract Airdrop {
function transfer(address from, address caddress, address[] _tos, uint v) public returns (bool) {
require(_tos.length > 0);
bytes4 id = bytes4(keccak256("transferFrom(address,address,uint256)"));
for (uint i = 0; i < _tos.length; i++) {
require(caddress.call(id, from, _tos[i], v));
}
return true;
}
}
代码不复杂,最关键的一点是用到了 solidity 中对地址的操作,在合约中 call 另一个合约,首先得知道要调用的是哪一个函数,因为是批量转账,这里用 transferFrom 函数,为什么不用 transfer 呢?因为这里发起交易的主体是合约地址,而不是原来的账户地址,我们可以看看 transfer 函数和 transferFrom 函数有什么不同
function transfer(address _recipient, uint256 _value) onlyPayloadSize(2 * 32) public {
require(balances[msg.sender] >= _value && _value > 0);
balances[msg.sender] -= _value;
balances[_recipient] += _value;
Transfer(msg.sender, _recipient, _value);
}
function transferFrom(address _from, address _to, uint256 _value) public {
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0);
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
}
让合约实现代币转账首先需要做得是让合约得到操纵一定量代币的权利,可以看到transfer 函数首先检查地址的余额,意味着要让合约替你转账,你得先转给它一部分代币才行,但让合约直接拥有代币是不太安全的,而 transferFrom 就不同了,它检查的是授信额度,意思是你只需要先授信给合约它能操纵的代币数量,这样的好处是降低风险。
另外,再准备一份代币合约。
pragma solidity ^0.4.23;
import 'zeppelin-solidity/contracts/token/ERC20/MintableToken.sol';
contract XToken is MintableToken {
string public name = "X Token";
string public symbol = "XTH";
uint8 public decimals = 8;
uint public INITIAL_SUPPLY = 10 ** 11; // equal 10 ^ 11, 1000亿
constructor() public {
totalSupply_ = INITIAL_SUPPLY * 10 ** uint256(decimals);
balances[owner] = totalSupply_;
}
/**
* 覆盖 Ownable -> transferOwnership 方法.
*/
function transferOwnership(address _newOwner) public onlyOwner {
balances[_newOwner] = balances[owner].add(balances[_newOwner]); // Owner 余额要转移给 新所有者。
balances[owner] = 0; // 清空旧所有人余额。
super._transferOwnership(_newOwner);
}
}
笔者的合约是由 truffle 构建的,在部署文件中配置好两份合约,并发布到网络中。
var XToken = artifacts.require("XToken");
var Airdrop = artifacts.require("Airdrop");
module.exports = function(deployer) {
deployer.deploy(XToken);
deployer.deploy(Airdrop);
};
发布合约
truffle migrate --reset
编写 node 脚本,执行授权。
#!/usr/bin/env node
const fs = require('fs');
const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545/');
const json = JSON.parse(fs.readFileSync('build/contracts/NPToken.json'));
/**
* Code: approve(c99, Airdrop.sol, 1000 eth)
*/
let code = web3.eth.abi.encodeFunctionCall({
name: 'approve',
type: 'function',
inputs: [{
type: 'address',
name: '_spender'
}, {
type: 'uint256',
name: '_value'
}]
}, ['{airdroip address}', '1000000000000000000000']);
/**
* ETH 合约调用
*/
web3.eth.accounts.signTransaction({
to: '{airdrop address}',
value: '0', // 0 ether
gas: '21000', // 21000 个
gasPrice: '5000000000', // 5 gwei
data: code
}, '{private-key}')
.then(res => {
web3.eth.sendSignedTransaction(res.rawTransaction)
.on('transactionHash', hash => {
console.log('### hash: ' + hash);
})
.on('receipt', receipt => {
console.log('### receipt: ' + JSON.stringify(receipt));
})
.on('confirmation', (confirmationNumber, receipt) => {
console.log('### confirmation: ' + confirmationNumber);
})
.on('error', err => {
console.error('### error: ' + err);
});
});
修改脚本中的 private-key、airdrop-address 等信息,并执行脚本,进行授权。
node airdrop.js
修改上面脚本中的 let code 值为:
let code = web3.eth.abi.encodeFunctionCall({
name: 'transfer',
type: 'function',
inputs: [{
type: 'address',
name: 'from'
}, {
type: 'address',
name: 'caddress'
}, {
type: 'address[]',
name: '_tos'
}, {
type: 'uint256',
name: 'v'
}]
}, ['{XTH-owner-address}','{airdrop-address}',['0x25387B.....fFdEEcBb',
'0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb',
'0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb',
'0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb','0x25387B.....fFdEEcBb',],
'1000000000000000']);
注意修改其中的 XTH-owner-address,airdrop-address,以及后面要批量空投的地址。
执行脚本即可开始批量空投。
下面笔者记录的一组数据。
笔数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
GAS(个数) | 51871 | 85016 | 92168 | 114320 | 136472 | 158624 | 180776 | 202928 | 225080 |
GAS平均 | 51871 | 42508 | 30723 | 28580 | 27294 | 26437 | 25825 | 25366 | 25009 |
绘制出如下图形
经分析可知,虽然以太坊原币交易的标准费用为 21000 gas,但代币交易的费用却要 51871 gas,且当使用批量空投合约时,批量数 > 3 后,每增加一笔,费用增加 22152 gas。
参考资料:https://blog.csdn.net/qq_35513792/article/details/81116241