如何为4万名订阅者编写自动空投脚本
发布:中币网 时间:2019-11-06 16:20:58 加入收藏 打赏
在本文/教程中,我将介绍编写node.js脚本的过程,该脚本执行自动代币分发/空投到以太坊地址列表。我将使用Polymath代币分发过程编写的代码(这是一个非常标准的ERC20令牌),并检查我处理代币的自动分发而构建的脚本。
最初我打算通过Infura运行此脚本,因此不必在本地运行全节点。这就要求离线签署交易记录,这是我使用最新版本的web3中的几个便捷函数完成的。不幸的是,即使这在testrpc和Ropsten上像魔咒一样工作,在Mainnet上还是一场灾难。交易没有得到处理,还极其缓慢,昂贵且不可靠。
代币和代币分发的合约
我在Polymath的首要任务之一是帮助团队制定代币和代币分发智能合约,我们将在未来几天使用这些合约来推出POLY代币,并向订阅该平台的4万人执行空投活动。
以下是有关polytoken.sol和polydribution.sol智能合约的一些值得一提的内容,这将有助于理解本教程的其余部分:
· PolyToken.sol是POLY代币的合约,这是一个相当固定的标准ERC20代币合约。
· PolyDistribution.sol是将处理代币初始分配的合约。我们将预售投资者,顾问,创始人等的代币分配从空投中分离出来,因为其过程是非常不同的。在我们的案例中,我们将使用1000万个代币(已发行的10亿个代币)进行空投,向40,000人分发250个代币。 对于本教程而言,最重要的功能是airdropTokens(),让我们对其进行回顾:
function airdropTokens(address[] _recipient) public onlyOwnerOrAdmin {
require(now >= startTime);
uint airdropped;
for(uint i = 0; i< _recipient.length; i++)
{
if (!airdrops[_recipient[i]]) {
airdrops[_recipient[i]] = true;
require(POLY.transfer(_recipient[i], 250 * decimalFactor));
airdropped = airdropped.add(250 * decimalFactor);
}
}
AVAILABLE_AIRDROP_SUPPLY = AVAILABLE_AIRDROP_SUPPLY.sub(airdropped);
AVAILABLE_TOTAL_SUPPLY = AVAILABLE_TOTAL_SUPPLY.sub(airdropped);
grandTotalClaimed = grandTotalClaimed.add(airdropped);
}
airdropTokens()的基本作用是将250个POLY代币(通常是ERC20代币)分发(调用ERC20的transfer()函数)到一个地址数组中。 对于我们收到的每个地址,只要他们尚未收到分配,我们就会向他们转移250 POLY。 流程完成后,我们将更新可用的供应并跟踪已经分配了多少代币。
在本教程中,我们将仅关注分发给空投接收者的代币。从上面可以看出,这些代币的分配和转让没有行权期或悬崖期对于其他类型的分配。对于其他类型的分配,情况则不同,那些分配具有一些特殊条件,必须先满足一些特殊条件,才能转让/出售它们。
如果您有兴趣了解其余分配的完成方式,可以查看PolyDistribution.sol中的setAllocation()和transferTokens()函数。
代币分配事件
当代币分配日期到来时,我们的团队需要做的就是将代币分配给每个注册空投的帐户。这些数据是过去几个月从公司网站上收集的,其中包含注册空投并在KYC验证过程中成功验证的每个帐户的地址。空投的过程所需收集的数据存储在CSV文件中,该文件只有1列:每个订阅的以太坊地址。
请注意,可以很容易地修改脚本,使其不仅包含订阅用户的地址,还包含应传输的代币数量。 在这种情况下,由于我们决定将250 POLY分配给每个人,因此这不是必需的,我们选择在分发智能合约中对该数字进行硬编码。
从理论上讲,进行空投非常简单。 我们需要做的就是为我们收集的每个地址调用ERC20令牌的transfer()函数。
如果我们只有很少的订阅者,则可以通过手动执行transfer()函数来完成上述操作,但是潜在的成千上万的人希望在启动时立即拥有其代币,逐个执行上述操作将非常耗时。
通过Node.js脚本自动执行代币分发过程。
在解释了代币和分发合约的工作原理之后,让我们深入研究JS代码。为了使代币分配过程自动化,我们需要做一些事情:
· 我们必须读取CSV文件并对其进行处理,以删除空白或无效的条目。我们假设某些数据会丢失或某些地址可能会错误,因此我们确保在将它们发送到区块链之前将其删除。
· 我们会将地址打包成多个数组,每个数组包含80个地址。为什么是80?经过多次测试,考虑到转让代币的气体成本,这是理想的数字。根据您要对每个条目进行的操作,每次交易可能会花费更多或更少的气体,因此您应该相应地打包条目,以使交易不会耗尽气体并回滚。
· 有了数组集后,我们将每个数组传递给智能合约上的airdropTokens()函数,该函数将遍历数组并为每个订阅者调用transfer()方法向其发送代币。
· 之后,我们将运行另一个过程来获取分发合约生成的所有“转移”事件,以便我们可以检查分发情况是否良好。(我们将对分发的代币进行汇总,该代币应与我们存档的数据相匹配)。
让我们从设置项目开始:
设置
运行以下命令以设置全新项目并安装所需的依赖项:
$ mkdir distributionTutorial
$ npm init
$ truffle init
$ npm install web3 fast-csv truffle-contract ethereumjs-testrpc --save
对于这个项目,我们将使用一些库和框架:
· truffle:它允许我们从javascript轻松编译、迁移和与我们的合约交互。
· Fast-csv:从CSV文件读取和处理数据。
您还应该安装Parity并将其同步到Ropsten(或您喜欢的任何testnet / mainnet)上。 以下命令对我来说效果很好:
parity — chain ropsten — rpcapi “eth,net,web3,personal,parity”
— unlock ACCOUNT YOU WANT TO UNLOCK> — password $HOME/password.file
接下来,将Polymath Distribution智能合约复制到项目的合约文件夹中。 可以在这里找到文件:https://github.com/PolymathNetwork/polymath-token-distribution/tree/master/contracts
打开truffle.js并将其内容替换为以下代码:
module.exports = {
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*', // Match any network id
gas: 3500000,
},
ropsten: {
host: 'localhost',
port: 8545,
network_id: '3', // Match any network id
gas: 3500000,
gasPrice: 50000000000
},
},
solc: {
optimizer: {
enabled: true,
runs: 200,
},
},
};
上面的代码将允许我们运行truffle migrate——network-ropsten来将合同部署到ropsten testnet。 在能够将合约部署到Ropsten之前,我们需要创建用于truffle的部署脚本。 使用以下代码在migrations文件夹中创建一个名为2_deploy_contracts.js的新文件:
var PolyToken = artifacts.require('./PolyToken.sol');
var PolyDistribution = artifacts.require('./PolyDistribution.sol');module.exports = async (deployer, network) => {
let _now = Date.now();
let _fromNow = 60 * 5 * 1000; // Start distribution in 1 hour
let _startTime = (_now + _fromNow) / 1000;
await deployer.deploy(PolyDistribution, _startTime);
console.log(`
---------------------------------------------------------------
--------- POLYMATH (POLY) TOKEN SUCCESSFULLY DEPLOYED ---------
---------------------------------------------------------------
- Contract address: ${PolyDistribution.address}
- Distribution starts in: ${_fromNow/1000/60} minutes
- Local Time: ${new Date(_now + _fromNow)}
---------------------------------------------------------------
`);
};
上面的代码将在执行truffle migrate--network ropsten时运行它将把PolyDistribution合约部署到Ropsten(它也处理POLY Token合约的部署),将startTime设置为5分钟后确保已正确设置了“starttime”变量,并且在到达“starttime”后尝试空投,否则执行将失败。我们正在使用starttime防止人们在代币分发事件开始之前撤回代币。
继续运行truffle migrate--network ropsten如果一切顺利,您应该会在控制台上看到类似的输出:
TX哈希值和合约地址对您来说会有所不同。
如果看不到此输出或出现错误,请确保您正在运行Parity并且已完全同步。 另外,请确保帐户中有足够的以太币,用于在Ropsten测试网上部署合约。
记下我们刚刚部署的Poly Distribution合约的地址,稍后我们将使用它。
读取CSV文件
让我们开始编写脚本,该脚本将自动将poly代币分配到注册空投的地址。
首先,创建一个名为scripts的新文件夹,并在该文件夹中创建一个名为csv_allocation.js的新文件。该文件将包含用于运行分配过程的所有代码。
在继续读取和处理CSV文件的代码之前,让我们将文件添加到项目中。 我们需要一个名为airdrop.csv的1列CSV文件,该文件的每个地址都有一个接收代币的地址。 创建此文件并将其添加到scripts / data文件夹。
如果要轻松测试空投,可以使用您控制的“随机”地址自己生成此文件。 一种简单的方法是运行testrpc并指定要创建的帐户数,如下所示:
testrpc -m "word1 word2 word3..." -a 300
上面的命令将从您提供的助记符中生成300个帐户。 将地址复制到airdrop.csv。
回到我们的csv_allocation.js脚本中,让我们添加必要的代码以能够读取airdrop.csv。 将以下代码添加到csv_allocation.js中:
var fs = require('fs');
var csv = require('fast-csv');
var BigNumber = require('bignumber.js');let polyDistributionAddress = process.argv.slice(2)[0];
let BATCH_SIZE = process.argv.slice(2)[1];
if(!BATCH_SIZE) BATCH_SIZE = 80;
let distribData = new Array();
let allocData = new Array();function readFile() {
var stream = fs.createReadStream("scripts/data/airdrop.csv");let index = 0;
let batch = 0;console.log(`
--------------------------------------------
--------- Parsing distrib.csv file ---------
--------------------------------------------******** Removing beneficiaries without address data
`);var csvStream = csv()
.on("data", function(data){
let isAddress = web3.utils.isAddress(data[0]);
if(isAddress && data[0]!=null && data[0]!='' ){
allocData.push(data[0]);index++;
if(index >= BATCH_SIZE)
{
distribData.push(allocData);
allocData = [];
index = 0;
}}
})
.on("end", function(){
//Add last remainder batch
distribData.push(allocData);
allocData = [];
setAllocation();
}); stream.pipe(csvStream);
}if(polyDistributionAddress){
console.log("Processing airdrop. Batch size is",BATCH_SIZE, "accounts per transaction");
readFile();
}else{
console.log("Please run the script by providing the address of the PolyDistribution contract");
}
您现在可以执行以下操作来运行脚本:
$ node scripts/csv_allocation.js 0x0... 80
// Where 0x0... is the address of the PolyDistribution contract we previously deployed to Ropsten.
// 80 is the batch size we want to process. (How many accounts per array we want to process and send to the airdropTokens function) Can be omitted, defaults to 80.
让我们回顾一下代码:
首先,我们导入允许我们读取文件和处理csv文件的库。
然后,如果您查看代码的最后几行,将会看到我们正在访问运行脚本时传递的参数,并且如果有PolyDistribution合约的地址,我们将调用readFile()函数。
readFile()函数的作用是访问airdrop.csv文件,并逐行读取它。 在每一行中,我们确保该值不为null或为空,并且还使用web3的isAddress()函数来验证传递的地址是否有效。 如果地址正确,我们会将其添加到一个数组中,该数组包含用于构建每个以太坊交易的已处理数据。
数据全部处理完毕并到达文件末尾后,我们将调用该函数,我们调用函数来获取80个地址的每个数组并对它们进行处理。
请注意,此函数非常简单,可以进一步改进,可以检测超过poly供应量的代币数量、重复地址等。所有这些情况仍在合约中处理,但如果我们可以节省一些对ethereum的事务调用,那就太好了。
处理代币分配
现在我们已经将数据处理到一个数组中了-我们应该将名为distribData的数组包含几个数组,每个数组最多具有80个地址addresses-我们将从智能合约中为每个数组调用airdropTokens()函数 。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));async function setAllocation() {console.log(`
--------------------------------------------
---------Performing allocations ------------
--------------------------------------------
`);let accounts = await web3.eth.getAccounts();let polyDistribution = await PolyDistribution.at(polyDistributionAddress); for(var i = 0;i< distribData.length;i++){try{
let gPrice = 50000000000;
console.log("Attempting to allocate 250 POLYs to accounts:",distribData[i],"\n\n");
let r = await polyDistribution.airdropTokens(distribData[i],{from:accounts[0], gas:4500000, gasPrice:gPrice});
console.log("---------- ---------- ---------- ----------");
console.log("Allocation + transfer was successful.", r.receipt.gasUsed, "gas used. Spent:",r.receipt.gasUsed * gPrice,"wei");
console.log("---------- ---------- ---------- ----------\n\n")
} catch (err){
console.log("ERROR:",err);
} }
}
让我们仔细看看这个函数。 JS脚本中的setAllocation()所做的只是遍历用CSV文件中处理后的数据填充的distribData数组,然后对于每个条目数组,我们继续在智能合约上执行airdropTokens(),并传递该数组。
对于我们处理的每一批地址,我们都检索事件日志并打印花费了多少气体,以确保过程成功。
每批气体消耗量应大致相同。 如果有一批天然气的成本更低,则意味着该批中的某些地址未转移代币,或者可能是因为它们之前已经转移了代币。
从ERC20令牌读取转移事件以验证交易
在每天调用它之前,我们可以做的最后一件事是访问erc20 poly token transfer()函数的事件日志,以便我们可以快速检查有多少帐户获得了代币。
在setAllocation()函数的末尾添加以下行:
console.log("Distribution script finished successfully.")
console.log("Waiting 2 minutes for transactions to be mined...")
await delay(90000);
console.log("Retrieving logs to inform total amount of tokens distributed so far. This may take a while...")let polytokenAddress = await polyDistribution.POLY({from:accounts[0]});
let polyToken = await PolyToken.at(polytokenAddress);var sumAccounts = 0;
var sumTokens = 0;var events = await polyToken.Transfer({from: polyDistribution.address},{fromBlock: 0, toBlock: 'latest'});
events.get(function(error, log) {
event_data = log;
//console.log(log);
for (var i=0; i<event_data.length;i++){
//let tokens = event_data[i].args.value.times(10 ** -18).toString(10);
//let addressB = event_data[i].args.to;
sumTokens += event_data[i].args.value.times(10 ** -18).toNumber();
sumAccounts +=1;
//console.log(`Distributed ${tokens} POLY to address ${addressB}`);}
console.log(`A total of ${sumTokens} POLY tokens have been distributed to ${sumAccounts} accounts so far.`);
});
上面的代码添加了一个超时函数,因此我们给事务留出一些时间来完成对事务的确认,然后我们获得POLY代币的Transfer()事件,并通过作为PolyDistribution合约的from字段过滤事件。
然后我们计算事件以及分配了多少大笔。我们可以使用该数据将其与原始文件进行比较。如果我们想花哨的话,我们也可以列出每个获得代币的地址,或者添加一个将CSV文件与事件日志数据进行比较的函数。
脚本执行
就这样! 让我们尝试一下脚本。 运行以下命令:
$ node scripts/csv_allocation.js 0x0...
// Replace 0x0... with the address of the PolyDistribution contract you deployed to Ropsten
如果一切顺利,您应该在控制台上看到以下内容:
而且,如果您转到Etherscan并输入已部署的PolyDistribution合同的地址,则应该看到类似以下内容:
如果您可以看到csv文件中每个帐户的transfer()事件,那么恭喜!
你的空投成功了!
到此就结束了,感谢你的耐心阅读!
来源:区块链研究实验室
来源:中币网 https://www.zhongbi.net/news/blocknews/178222.html 声明:登载此文仅出于分享区块链知识,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不构成投资建议。投资者据此操作,风险自担。 此文如侵犯到您的合法权益,请联系我们3111859717@qq.com,我们将第一时间处理。