Skip to content
This repository was archived by the owner on Oct 8, 2024. It is now read-only.
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ artifacts
.secret*

*.sw*

getERC20Names.csv
getWithdrawable.csv
getWithdrawableTokens.csv
116 changes: 114 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"start": "concurrently \"cd api && npm start\" \"cd app && npm run dev\"",
"deploy": "npx hardhat run scripts/deployArchetype.ts --network localhost",
"network": "npx hardhat node",
"refund": "npx hardhat run scripts/batchSendEth.ts --network mainnet",
"withdraw": "npx hardhat run scripts/getWithdrawable.ts --network mainnet",
"withdrawTokens": "npx hardhat run scripts/getWithdrawableTokens.ts --network mainnet",
"verify": "npx hardhat verify 0xc078Aed674c230cDD7ADD602E077797d08d8f0c2 --network goerli"
},
"repository": {
Expand All @@ -25,8 +28,10 @@
"homepage": "https://github.com/scatter-art/contracts#readme",
"dependencies": {
"concurrently": "6.4.0",
"csv-parse": "^5.5.0",
"ethereumjs-util": "7.1.4",
"ipfsh": "0.0.6",
"readline-sync": "^1.4.10",
"solady": "^0.0.19"
},
"devDependencies": {
Expand Down
119 changes: 119 additions & 0 deletions scripts/batchSendEth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ethers } from "hardhat";
import * as readlineSync from "readline-sync";
import * as fs from "fs";
import { parse } from "csv-parse";
import { ArchetypeBatch } from "../typechain";
const path = require("path");

function parseCsv(csvString: string): Promise<any[]> {
const options = {
columns: true,
skip_empty_lines: true,
};
return new Promise((resolve, reject) => {
parse(csvString, options, (err, output) => {
if (err) {
reject(err);
} else {
resolve(output);
}
});
});
}

async function main() {
// const BATCH_ADDRESS: string = "0x0e1356208CA2eB9Cc4EFaEb42cc8287CB7ED8e1F"; // SEPOLIA
const BATCH_ADDRESS: string = "0x6Bc558A6DC48dEfa0e7022713c23D65Ab26e4Fa7"; // MAINNET

// Check the batch contract
const archetypeBatch: ArchetypeBatch = await ethers.getContractAt(
"ArchetypeBatch",
BATCH_ADDRESS
);
const expectedBytecode = (await ethers.getContractFactory("ArchetypeBatch")).bytecode;
const actualBytecode = await ethers.provider.getCode(archetypeBatch.address);
const matchRate = calculateMatchRate(expectedBytecode, actualBytecode);

console.log("ArchetypeBatch bytecode has a match rate of", matchRate);
if (matchRate > 90) {
console.log("ArchetypeBatch bytecode match passes");
} else {
console.log(
"ArchetypeBatch bytecode match fails, make sure its the correct address. Exiting ..."
);
process.exit(1);
}

const csvContent = fs.readFileSync(path.join(__dirname, "./data/refundList.csv"), "utf-8");
const records = await parseCsv(csvContent);

const balances: { [address: string]: ethers.BigNumber } = {};
let totalValue = ethers.BigNumber.from(0);
for (const record of records) {
try {
const address = ethers.utils.getAddress(record.address);
const value = ethers.utils.parseEther(record.value);

if (!balances[address]) {
balances[address] = value;
} else {
balances[address] = balances[address].add(value);
}
totalValue = totalValue.add(value);
} catch {
continue;
}
}

console.log(balances);
console.log({
totalValue: totalValue,
totalValueFormatted: ethers.utils.formatEther(totalValue),
});

if (!readlineSync.keyInYN("Are the above values correct?")) {
console.log("config not confirmed. Not sending eth.");
process.exit();
}

// Formulate the calls
const targets = Object.keys(balances);
const values = Object.values(balances).map(v => v.toString());
const datas = targets.map(() => "0x");

const tx = await archetypeBatch.executeBatch(targets, values, datas, { value: totalValue });
const receipt = await tx.wait();

console.log(`Transaction hash: ${receipt.transactionHash}`);
}

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});

/*
Uses a sliding window approach, chunkBytecode produces overlapping chunks.
E.g., for "ABCDEF" and chunk size 3, we get ["ABC", "BCD", "CDE", "DEF"].
This ensures better matching, especially when bytecodes have minor offsets.
*/
function calculateMatchRate(bytecode1: string, bytecode2: string): number {
function chunkBytecode(bytecode: string, chunkSize: number): string[] {
const chunks = [];
for (let i = 0; i <= bytecode.length - chunkSize; i++) {
chunks.push(bytecode.slice(i, i + chunkSize));
}
return chunks;
}

const chunkSize = 10;
const chunks1 = chunkBytecode(bytecode1, chunkSize);
const chunks2 = new Set(chunkBytecode(bytecode2, chunkSize));

const commonChunks = chunks1.filter(chunk => chunks2.has(chunk)).length;

const maxPossibleMatches = Math.max(chunks1.length, chunks2.size);
return (commonChunks / maxPossibleMatches) * 100;
}
Loading