@gooddollar/goodprotocol 1.0.15 → 1.0.16-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hardhat.config.ts +17 -1
- package/package.json +1 -1
- package/scripts/gdx/gdxAirdropCalculation.ts +223 -0
package/hardhat.config.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { task, types } from "hardhat/config";
|
|
|
13
13
|
import { sha3 } from "web3-utils";
|
|
14
14
|
import { config } from "dotenv";
|
|
15
15
|
import { airdrop } from "./scripts/governance/airdropCalculation";
|
|
16
|
-
import { airdrop as gdxAirdrop } from "./scripts/gdx/gdxAirdropCalculation";
|
|
16
|
+
import { airdrop as gdxAirdrop, airdropRecover as gdxAirdropRecover} from "./scripts/gdx/gdxAirdropCalculation";
|
|
17
17
|
import { verify } from "./scripts/verify";
|
|
18
18
|
import { ethers } from "ethers";
|
|
19
19
|
config();
|
|
@@ -197,6 +197,22 @@ task("gdxAirdrop", "Calculates airdrop data")
|
|
|
197
197
|
}
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
task("gdxAirdropRecover", "Calculates new airdrop data for recovery")
|
|
201
|
+
.addParam("action", "calculate/tree/proof")
|
|
202
|
+
.addOptionalPositionalParam("address", "proof for address")
|
|
203
|
+
.addOptionalParam("ethsnapshotblock", "eth block for calculate")
|
|
204
|
+
.setAction(async (taskArgs, hre) => {
|
|
205
|
+
const actions = gdxAirdropRecover(hre.ethers);
|
|
206
|
+
switch (taskArgs.action) {
|
|
207
|
+
case "addition":
|
|
208
|
+
return actions.addCalculationsToPreviousData();
|
|
209
|
+
case "tree":
|
|
210
|
+
return actions.buildMerkleTree();
|
|
211
|
+
default:
|
|
212
|
+
console.log("unknown action use calculate or tree");
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
200
216
|
task("verifyjson", "verify contracts on etherscan").setAction(
|
|
201
217
|
async (taskArgs, hre) => {
|
|
202
218
|
return verify(hre);
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import fs from "fs";
|
|
|
3
3
|
import MerkleTree from "merkle-tree-solidity";
|
|
4
4
|
import stakingContracts from "@gooddollar/goodcontracts/stakingModel/releases/deployment.json";
|
|
5
5
|
import { ethers as Ethers } from "hardhat";
|
|
6
|
+
import { BigNumber } from "ethereum-waffle/node_modules/ethers";
|
|
6
7
|
|
|
7
8
|
type Tree = {
|
|
8
9
|
[key: string]: {
|
|
@@ -21,6 +22,17 @@ const quantile = (sorted, q) => {
|
|
|
21
22
|
return sum;
|
|
22
23
|
};
|
|
23
24
|
|
|
25
|
+
const quantileBN = (sorted, q) => {
|
|
26
|
+
const pos = (sorted.length - 1) * q;
|
|
27
|
+
const base = Math.floor(pos);
|
|
28
|
+
|
|
29
|
+
let sum = BigNumber.from("0");
|
|
30
|
+
for (let i = 0; i < base; i++)
|
|
31
|
+
sum = BigNumber.from(sum).add(BigNumber.from(sorted[i]));
|
|
32
|
+
|
|
33
|
+
return sum.toString();
|
|
34
|
+
};
|
|
35
|
+
|
|
24
36
|
let ETH_SNAPSHOT_BLOCK = 13320531; //first blocka after 12pm Sep-29-2021 12:00:20 PM +UTC
|
|
25
37
|
|
|
26
38
|
export const airdrop = (ethers: typeof Ethers, ethSnapshotBlock) => {
|
|
@@ -198,3 +210,214 @@ export const airdrop = (ethers: typeof Ethers, ethSnapshotBlock) => {
|
|
|
198
210
|
|
|
199
211
|
return { buildMerkleTree, collectAirdropData, getProof };
|
|
200
212
|
};
|
|
213
|
+
|
|
214
|
+
export const airdropRecover = (ethers: typeof Ethers) => {
|
|
215
|
+
|
|
216
|
+
const ZERO = ethers.BigNumber.from("0");
|
|
217
|
+
|
|
218
|
+
const getHoldersInformation = async (newAddresses = {}, newIsContracts = {}) => {
|
|
219
|
+
const provider = new ethers.providers.InfuraProvider();
|
|
220
|
+
|
|
221
|
+
const eventsABI = [
|
|
222
|
+
"event TokenPurchased(address indexed caller,address indexed inputToken,uint256 inputAmount,uint256 actualReturn,address indexed receiverAddress)",
|
|
223
|
+
"event TokenSold(address indexed caller,address indexed outputToken,uint256 gdAmount,uint256 contributionAmount,uint256 actualReturn,address indexed receiverAddress)"
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
let newReserve = await ethers.getContractAt(eventsABI, "0x6C35677206ae7FF1bf753877649cF57cC30D1c42");
|
|
227
|
+
let exchangeHelper = await ethers.getContractAt(eventsABI, "0x0a8c6bB832801454F6CC21761D0A293Caa003296");
|
|
228
|
+
|
|
229
|
+
exchangeHelper = exchangeHelper.connect(provider);
|
|
230
|
+
newReserve = newReserve.connect(provider);
|
|
231
|
+
|
|
232
|
+
const step = 100000;
|
|
233
|
+
const START_BLOCK = 13683748; // Reserve was created
|
|
234
|
+
const END_BLOCK = 14296271; // Following reserve created
|
|
235
|
+
const blocks = range(START_BLOCK, END_BLOCK, step);
|
|
236
|
+
|
|
237
|
+
const reserveTokenPurchasedFilter = newReserve.filters.TokenPurchased();
|
|
238
|
+
const reserveTokenSoldFilter = newReserve.filters.TokenSold();
|
|
239
|
+
|
|
240
|
+
const exchangeHelperTokenPurchasedFilter = exchangeHelper.filters.TokenPurchased();
|
|
241
|
+
const exchangeHelperTokenSoldFilter = exchangeHelper.filters.TokenSold();
|
|
242
|
+
|
|
243
|
+
const populateListOfAddressesAndBalances = async (contractInstance, purchaseFilter, soldFilter) => {
|
|
244
|
+
for (let blockChunk of chunk(blocks, 10)) {
|
|
245
|
+
// Get the filter (the second null could be omitted)
|
|
246
|
+
const processedChunks = blockChunk.map(async bc => {
|
|
247
|
+
// Query the filter (the latest could be omitted)
|
|
248
|
+
const purchaseEvents = await contractInstance
|
|
249
|
+
.queryFilter(purchaseFilter, bc, Math.min(bc + step - 1, END_BLOCK))
|
|
250
|
+
.catch(e => {
|
|
251
|
+
console.log("block transfer logs failed retrying...", bc);
|
|
252
|
+
return contractInstance.queryFilter(
|
|
253
|
+
purchaseFilter,
|
|
254
|
+
bc,
|
|
255
|
+
Math.min(bc + step - 1, END_BLOCK)
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// console.log({purchaseEvents});
|
|
260
|
+
|
|
261
|
+
const soldEvents = await contractInstance
|
|
262
|
+
.queryFilter(soldFilter, bc, Math.min(bc + step - 1, END_BLOCK))
|
|
263
|
+
.catch(e => {
|
|
264
|
+
console.log("block swaphelper logs failed retrying...", bc);
|
|
265
|
+
return contractInstance.queryFilter(
|
|
266
|
+
soldFilter,
|
|
267
|
+
bc,
|
|
268
|
+
Math.min(bc + step - 1, END_BLOCK)
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// console.log(
|
|
273
|
+
// "found transfer logs in block:",
|
|
274
|
+
// { bc },
|
|
275
|
+
// { purchaseEvents: purchaseEvents.length, soldEvents: soldEvents.length }
|
|
276
|
+
// );
|
|
277
|
+
|
|
278
|
+
const isContract = async (log, role) => {
|
|
279
|
+
const possibleCodeStateOfAddress = await contractInstance.provider.getCode(log.args[role])
|
|
280
|
+
.catch(e => "0x");
|
|
281
|
+
return possibleCodeStateOfAddress !== "0x";
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Print out all the values:
|
|
285
|
+
const purchasedEventsMapped = purchaseEvents.map(async log => {
|
|
286
|
+
let balance = newAddresses[log.args.receiverAddress] || ZERO;
|
|
287
|
+
// console.log({balance});
|
|
288
|
+
// console.log(`actualReturn: ${log.args.actualReturn.toString()}`);
|
|
289
|
+
newAddresses[log.args.receiverAddress] =
|
|
290
|
+
balance.add(log.args.actualReturn);
|
|
291
|
+
newIsContracts[log.args.receiverAddress] = await isContract(
|
|
292
|
+
log, "receiverAddress"
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
const soldEventsMapped = soldEvents.map(async log => {
|
|
296
|
+
let balance = newAddresses[log.args.caller] || ZERO;
|
|
297
|
+
newAddresses[log.args.caller] = balance.sub(log.args.gdAmount);
|
|
298
|
+
newIsContracts[log.args.caller] = await isContract(
|
|
299
|
+
log, "caller"
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
await Promise.all([...purchasedEventsMapped, ...soldEventsMapped]);
|
|
304
|
+
});
|
|
305
|
+
await Promise.all(processedChunks);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
await populateListOfAddressesAndBalances(newReserve, reserveTokenPurchasedFilter, reserveTokenSoldFilter);
|
|
310
|
+
await populateListOfAddressesAndBalances(exchangeHelper, exchangeHelperTokenPurchasedFilter, exchangeHelperTokenSoldFilter);
|
|
311
|
+
|
|
312
|
+
// console.log({ newAddresses, newIsContracts });
|
|
313
|
+
return { newAddresses, newIsContracts };
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const buildMerkleTree = () => {
|
|
317
|
+
const { addressesCombined } = JSON.parse(
|
|
318
|
+
fs.readFileSync("airdrop/buyBalancesCombined.json").toString()
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
let toTree: Array<[string, BigNumber]> = Object.entries(addressesCombined).map(
|
|
322
|
+
([addr, gdx]) => {
|
|
323
|
+
return [addr, gdx as BigNumber];
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// console.log(`Before sorting`);
|
|
328
|
+
// toTree.forEach((a,_) => { console.log(`${a[0].toString()}:${ethers.BigNumber.from(a[1]).toString()}\n`)});
|
|
329
|
+
|
|
330
|
+
toTree.sort((a, b) => BigNumber.from(b[1]).gt(a[1]) ? 0 : -1 );
|
|
331
|
+
|
|
332
|
+
// console.log(`After sorting`);
|
|
333
|
+
// toTree.forEach((a,_) => { console.log(`${a[0].toString()}:${ethers.BigNumber.from(a[1]).toString()}\n`)});
|
|
334
|
+
|
|
335
|
+
let totalGDX = ZERO;
|
|
336
|
+
toTree.forEach((a,_) => totalGDX = totalGDX.add(a[1]))
|
|
337
|
+
console.log({
|
|
338
|
+
toTree: toTree.forEach((a,_) => console.log({address: a[0].toString(), balance: BigNumber.from(a[1]).toString()})),
|
|
339
|
+
numberOfAccounts: toTree.length,
|
|
340
|
+
TotalGDX: totalGDX.toString()
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Print statistics
|
|
344
|
+
const sorted = toTree.map(_ => _[1]);
|
|
345
|
+
console.log("GDX Distribution\n");
|
|
346
|
+
[0.001, 0.01, 0.1, 0.5].forEach(q =>
|
|
347
|
+
console.log({
|
|
348
|
+
precentile: q * 100 + "%",
|
|
349
|
+
gdx: quantileBN(sorted, q)
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
const treeData = {};
|
|
354
|
+
const elements = toTree.map(e => {
|
|
355
|
+
const hash = ethers.utils.keccak256(
|
|
356
|
+
ethers.utils.defaultAbiCoder.encode(
|
|
357
|
+
["address", "uint256"],
|
|
358
|
+
[e[0], e[1]]
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
treeData[e[0]] = {
|
|
362
|
+
gdx: e[1],
|
|
363
|
+
hash
|
|
364
|
+
};
|
|
365
|
+
return Buffer.from(hash.slice(2), "hex");
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
console.log(elements);
|
|
369
|
+
const merkleTree = new MerkleTree(elements, false);
|
|
370
|
+
// get the merkle root
|
|
371
|
+
// returns 32 byte buffer
|
|
372
|
+
const merkleRoot = merkleTree.getRoot().toString("hex");
|
|
373
|
+
// generate merkle proof
|
|
374
|
+
// returns array of 32 byte buffers
|
|
375
|
+
const proof = merkleTree.getProof(elements[0]).map(_ => _.toString("hex"));
|
|
376
|
+
console.log({ merkleRoot, proof, sampleProofFor: toTree[0] });
|
|
377
|
+
fs.writeFileSync(
|
|
378
|
+
"airdrop/gdxAirdropRecovery.json",
|
|
379
|
+
JSON.stringify({ treeData, merkleRoot })
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const addCalculationsToPreviousData = async () => {
|
|
384
|
+
// get previous airdrop and turn it into BigNumbers
|
|
385
|
+
const gdxAirdropData = JSON.parse(
|
|
386
|
+
fs.readFileSync("airdrop/gdxairdrop.json").toString()
|
|
387
|
+
);
|
|
388
|
+
const addressesCombined = {}
|
|
389
|
+
for (const [address, balance] of Object.entries(gdxAirdropData.treeData)) {
|
|
390
|
+
addressesCombined[address] = BigNumber.from(balance['gdx']);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// get new holders info and turn into array
|
|
394
|
+
const { newAddresses, } = await getHoldersInformation();
|
|
395
|
+
|
|
396
|
+
let newAddressesArray: Array<[string, BigNumber]> = Object.entries(newAddresses).map(
|
|
397
|
+
([addr, gdx]) => {
|
|
398
|
+
return [addr, gdx as BigNumber];
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
// Unite previous airdrop with current information
|
|
404
|
+
for (const newAddressEntry of newAddressesArray) {
|
|
405
|
+
const address = newAddressEntry[0];
|
|
406
|
+
const addition = newAddressEntry[1];
|
|
407
|
+
|
|
408
|
+
if (addition<=ZERO)
|
|
409
|
+
continue;
|
|
410
|
+
|
|
411
|
+
// console.log({address})
|
|
412
|
+
// console.log({before: BigNumber.from(addressesCombined[address] || "0").toString() })
|
|
413
|
+
// console.log({addition: addition.toString()})
|
|
414
|
+
addressesCombined[address] = ethers.BigNumber.from(addressesCombined[address] || 0).add(addition.toString()).toString();
|
|
415
|
+
// console.log({total: addressesCombined[address].toString()})
|
|
416
|
+
// console.log(`\n`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
fs.writeFileSync("airdrop/buyBalancesCombined.json", JSON.stringify({ addressesCombined }));
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
return { buildMerkleTree, addCalculationsToPreviousData };
|
|
423
|
+
};
|