Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@nomicfoundation/hardhat-chai-matchers": "2.0.6",
"@nomicfoundation/hardhat-ethers": "3.0.5",
"@nomicfoundation/hardhat-verify": "2.0.5",
"@types/mocha": "^10.0.7",
"chai": "4.4.0",
"commander": "12.0.0",
"dotenv": "16.4.5",
Expand Down
54 changes: 42 additions & 12 deletions src/gen_qr_lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,39 @@ const fs = require('fs');
const path = require('path');
const { assert } = require('console');

class DropSettings {
fileLatest = './src/.latest';
pathQr = './src/qr';
pathTestQr = './src/test_qr';
pathZip = './src/gendata';
class AbstractDropSettings {
constructor(flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, chainId, flagNoVersionUpdate = false) {
if (new.target === AbstractDropSettings) {
throw new Error("Cannot instantiate an abstract class.");
}

constructor (flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, chainId, flagNoVersionUpdate = false) {
this.flagSaveQr = flagSaveQr;
this.flagSaveLink = flagSaveLink;
this.flagNoDeploy = flagNoVersionUpdate;
this.codeCounts = codeCounts;
this.codeAmounts = codeAmounts;
this.version = version;
this.chainId = chainId;
this.fileLinks = `./src/gendata/${version}-qr-links.json`;

// Derived paths using the abstract root path
this.fileLatest = `${this.constructor.root}/.latest`;
this.pathQr = `${this.constructor.root}/qr`;
this.pathTestQr = `${this.constructor.root}/test_qr`;
this.pathZip = `${this.constructor.root}/gendata`;
this.fileLinks = `${this.pathZip}/${version}-qr-links.json`;
this.prefix = `https://app.1inch.io/#/${chainId}/qr?`;
}

// Abstract getter for the root path (should be overridden by subclasses)
static get root() {
throw new Error("Subclasses must define a root path.");
}
}

class DropSettings extends AbstractDropSettings {
static get root() {
return './src';
}
}

function keccak128 (input) {
Expand All @@ -45,12 +61,24 @@ function saveFile(filePath, fileContent) {
fs.writeFileSync(filePath, fileContent);
}

function makeDrop (wallets, amounts) {
function makeDrop(wallets, amounts) {
// Create an array of elements by concatenating each wallet address with the corresponding amount
// in hexadecimal format, padded to 64 characters.
const elements = wallets.map((w, i) => w + BigInt(amounts[i]).toString(16).padStart(64, '0'));

// Generate a Merkle Tree leaf by hashing each element with keccak128 and converting it to a hexadecimal string.
const leaves = elements.map(keccak128).map(x => MerkleTree.bufferToHex(x));

// Create a Merkle Tree from the leaves using keccak128 as the hashing function and sort the pairs for consistency.
const tree = new MerkleTree(leaves, keccak128, { sortPairs: true });

// Obtain the Merkle root, which is the top node of the tree.
const root = tree.getHexRoot();

// Generate a proof for each leaf in the Merkle Tree. A proof is used to verify that a leaf is part of the tree.
const proofs = leaves.map(tree.getProof, tree);

// Return an object containing the elements, leaves, Merkle root, and proofs.
return { elements, leaves, root, proofs };
}

Expand Down Expand Up @@ -138,7 +166,8 @@ async function main (settings) {
const COUNTS = settings.codeCounts;
const AMOUNTS = settings.codeAmounts;

const privs = await genPrivs(Number(COUNTS.reduce((s, a) => s + a, 0n)));
const totalCodes = Number(COUNTS.reduce((s, a) => s + a, 0n));
const privs = await genPrivs(totalCodes);
const accounts = privs.map(p => Wallet.fromPrivateKey(Buffer.from(p, 'hex')).getAddressString());
let amounts = [];
for (let i = 0; i < COUNTS.length; i++) {
Expand Down Expand Up @@ -196,13 +225,14 @@ function verifyLink (url, root, prefix) {
return uriDecode(url, root, prefix, true);
}

function createNewDropSettings (flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, flagNoDeploy, chainId) {
const settings = new DropSettings(flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, flagNoDeploy, chainId);
return settings;
function createNewDropSettings (flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, chainId, flagNoDeploy) {
return new DropSettings(flagSaveQr, flagSaveLink, codeCounts, codeAmounts, version, chainId, flagNoDeploy);
}

module.exports = {
generateCodes: main,
verifyLink,
createNewDropSettings,
AbstractDropSettings,
DropSettings,
};
4 changes: 2 additions & 2 deletions src/qrdrop.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const archive = require('./zip_lib.js');

const commander = require('commander');
const { exit } = require('process');
const {DropSettings} = require("./gen_qr_lib");
const program = new commander.Command();

// Example usage: node ./src/qrdrop.js -gqlczv 33 -a 5,10,20,30,40,50 -n 40,70,80,100,70,40
Expand Down Expand Up @@ -92,8 +93,7 @@ async function execute () {
}

if (flagWipe || flagZip) {
const settings = qrdrop.createNewDropSettings();
archive.cleanDirs([settings.pathTestQr, settings.pathQr]);
archive.cleanDirs([DropSettings.pathTestQr, DropSettings.pathQr]);
}
}

Expand Down
68 changes: 68 additions & 0 deletions test/dropSettings.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const {expect} = require('chai');
const {DropSettings} = require('./../src/gen_qr_lib');
const {NFTDropSettings} = require("../src/nft_drop/gen_nft_lib");

describe('DropSettings', function () {
it('should have the correct default pathQr', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathQr).to.equal('./src/qr');
});

it('should have the correct default pathTestQr', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathTestQr).to.equal('./src/test_qr');
});

it('should have the correct default pathZip', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathZip).to.equal('./src/gendata');
});

it('should have the correct fileLatest path', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.fileLatest).to.equal('./src/.latest');
});

it('should correctly generate the fileLinks path', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.fileLinks).to.equal('./src/gendata/v1.0-qr-links.json');
});

it('should have the correct prefix', function () {
const settings = new DropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.prefix).to.equal('https://app.1inch.io/#/1/qr?');
});
});


describe('NFTDropSettings', function () {
it('should have the correct default pathQr', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathQr).to.equal('./src/nft_drop/qr');
});

it('should have the correct default pathTestQr', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathTestQr).to.equal('./src/nft_drop/test_qr');
});

it('should have the correct default pathZip', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.pathZip).to.equal('./src/nft_drop/gendata');
});

it('should have the correct fileLatest path', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.fileLatest).to.equal('./src/nft_drop/.latest');
});

it('should correctly generate the fileLinks path', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.fileLinks).to.equal('./src/nft_drop/gendata/v1.0-qr-links.json');
});

it('should have the correct prefix', function () {
const settings = new NFTDropSettings(true, true, 100, 200, 'v1.0', 1);
expect(settings.prefix).to.equal('https://app.1inch.io/#/1/qr?');
});
});
Loading