@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddollar/goodprotocol",
3
- "version": "1.0.15",
3
+ "version": "1.0.16-beta.0",
4
4
  "description": "GoodDollar Protocol",
5
5
  "scripts": {
6
6
  "build": "scripts/build.sh deploy",
@@ -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
+ };