如何为4万名订阅者编写自动令牌空投脚本
我很高兴地宣布,我最近加入了Polymath团队,担任Solidity Engineer,负责领导基于区块链的证券代币新标准的开发。 ????????????
在本文/教程中,我将介绍编写一个node.js脚本的过程,该脚本执行自动令牌分发/空投到以太坊地址列表。 我将使用为Polymath令牌分发过程编写的代码(这是一个非常标准的ERC20令牌),然后遍历为处理令牌的自动分发而构建的脚本。
最初,我打算通过Infura运行此脚本,因此不必在本地运行全节点。 这就要求离线签署交易记录,这是我使用最新版本的web3中的几个便捷功能完成的。 不幸的是,即使这在testrpc和Ropsten上像魔咒一样工作,在Mainnet上还是一场灾难。 交易没有得到处理,这是极其缓慢,昂贵且不可靠的。
如果仍然要检查它, 可以参考此早期commit 。
代币和代币分配合同
我在Polymath的首要任务之一是帮助团队淘汰Token和Token Distribution智能合约,我们将在接下来的几天中使用这些合约来启动POLY Token,并向4万名订阅该平台的人员进行发射空投。
我不会详细介绍这些合同的代码,但是您可以查看一下它们,因为它们已在Polymath的Github存储库中公开。
这是关于PolyToken.sol
值得一提的几件事 和PolyDistribution.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令牌transfer()
分发(调用ERC20的transfer()
函数)到地址数组中。 对于我们收到的每个地址,只要他们尚未收到分配,我们就会将250 POLY转移给他们。 流程完成后,我们将更新可用的供应并跟踪已经分配了多少令牌。
在本教程中, 我们将仅着重于分发给空投接收者的令牌 。 从上面可以看出,这些令牌的分配和转让没有归属或悬崖期。 对于其他类型的分配,情况则不同,那些分配具有一些特殊条件,必须先满足一些特殊条件,才能转让/出售它们。
如果你有兴趣看如何分配的剩余部分都做了,你可以查看 setAllocation()
和 transferTokens()
函数从 PolyDistribution.sol
。
代币分配事件
当令牌分配日期到来时,我们的团队需要做的就是将令牌分配给每个注册空投的帐户。 这些数据是过去几个月从公司网站上收集的,其中包含注册空投并在KYC验证过程中成功验证的每个帐户的地址。 空投过程所需的收集数据存储在CSV文件中,该文件只有1列:每个订户的以太坊地址。
请注意,可以轻松修改脚本,以不仅包含订户的地址,还应包含应转让的令牌数量。 在这种情况下,由于我们决定将250 POLY分配给每个人,因此这不是必需的,因此我们选择在分发智能合约中对该数字进行硬编码。
从理论上讲,进行空投非常简单。 我们需要做的就是为我们收集的每个地址调用ERC20令牌的transfer()
函数。
如果我们只有很少的订阅者,则可以通过手动执行transfer()
函数来完成上述操作,但是潜在的成千上万的人希望在启动时就拥有自己的令牌,并逐一进行,将非常耗时。
通过Node.js脚本自动执行令牌分发过程。
在解释了令牌和分发合同的工作原理之后,让我们深入研究JS代码。 为了使令牌分配过程自动化,我们需要做一些事情:
- 我们必须读取CSV文件并对其进行处理,以删除空白或无效的条目。 我们假设某些数据将丢失或某些地址可能是错误的,因此我们确保在将它们发送到区块链之前将其删除。
- 我们将地址打包成多个数组,每个数组包含80个地址。 为什么是80? 经过多次测试,考虑到转让代币的气体成本,这是理想的数字。 根据您尝试对每个条目执行的操作,每笔交易可能会花费更多或更少的汽油,因此您应该相应地打包条目,以使交易不会耗尽汽油并回滚。
- 有了数组集后,我们会将每个数组传递给智能合约上的
airdropTokens()
函数,该函数将遍历该数组并为每个订阅者调用transfer()
方法向其发送令牌。 - 然后,我们将运行另一个过程来获取分发合同生成的所有
Transfer
事件,以便我们可以检查分发是否顺利。 (我们将对分发的令牌进行求和,该令牌应与我们存档的数据相匹配)。
让我们从设置项目开始:
如果您想跳过整个教程而只运行脚本,可以在此处找到完整的源代码 。
设定
运行以下命令以设置全新项目并安装所需的依赖项:
$ mkdir distributionTutorial
$ npm init
$ truffle init
$ npm install web3 fast-csv truffle-contract ethereumjs-testrpc --save
对于这个项目,我们将使用一些库和框架:
- 松露:这使我们能够轻松地从JavaScript编译,迁移和与我们的合同进行交互。
- Fast-csv:从CSV文件读取和处理数据。
您还应该安装Parity并将其同步到Ropsten(或您喜欢的任何testnet / mainnet)上。 以下命令对我来说效果很好:
parity — chain ropsten — rpcapi “eth,net,web3,personal,parity” — unlock <THE ACCOUNT YOU WANT TO UNLOCK> — password $HOME/password.file
接下来,将Polymath Distribution智能合约复制到项目的contracts
文件夹中。 可以在这里找到文件: 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之前,我们需要创建用于松露的部署脚本。 使用以下代码在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令牌合同的部署),设定_startTime
从现在到五分钟。 确保正确设置了_startTime
变量,并且一旦达到_startTime
尝试进行空投,否则执行将失败。 我们正在使用_startTime
来防止人们在令牌分发事件开始之前提取令牌。
继续并运行truffle migrate --network ropsten
如果一切顺利,您应该在控制台上看到类似以下的输出:
如果看不到此输出或出现错误,请确保您正在运行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
文件,并airdrop.csv
读取它。 在每一行中,我们确保该值不为null或为空,并且还使用web3的isAddress()
函数来验证传递的地址是否有效。 如果地址正确,我们将其添加到一个数组中,该数组包含用于构建每个以太坊交易的已处理数据。
数据全部处理完毕并到达文件末尾后,我们将调用该函数,该函数将获取80个地址的每个数组并进行处理。
请注意,此功能非常简单,可以进一步改进以检测超出POLY供应量,重复地址等的令牌数量。所有这些情况在合同方仍然可以解决,但是如果我们能够为我们节省了一些以太坊的交易电话。
处理代币分配
现在我们已经将数据处理到一个数组中-我们应该将名为distribData
的数组包含几个数组,每个数组最多具有80个地址-我们将要从智能合约中为每个数组调用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令牌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()
事件,那么恭喜您!
您已成功执行空投!
感谢您阅读本教程,希望您像阅读它一样喜欢阅读它。 请继续关注与我的冒险有关的更多文章,教程和故事,这些故事为构建区块链上的代币化证券的下一代平台大冒险!
如果您自己进行空投并且需要建议,请随时在下面的评论部分与我联系,或给我发电子邮件给我[at] blobruiz [co],我将尽力立即答复。
From: https://hackernoon.com/how-to-script-an-automatic-token-airdrop-for-40k-subscribers-e40c8b1a02c6