Skip to content

Commit

Permalink
feat: Add a script to call the massImport method
Browse files Browse the repository at this point in the history
  • Loading branch information
alainncls committed Oct 11, 2023
1 parent 2a56f53 commit 2d1c002
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
2 changes: 2 additions & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"deploy:post:goerli": "npx hardhat run --network linea-goerli script/deploy/postDeployment.ts",
"encode": "npx hardhat run script/encode.ts",
"lint": "pnpm solhint \"{script,src,test}/**/*.sol\"",
"massImport": "npx hardhat run --network linea script/massImport/massImport.ts",
"massImport:goerli": "npx hardhat run --network linea-goerli script/massImport/massImport.ts",
"reimport": "npx hardhat run --network linea script/recreateNetworkFile.ts",
"reimport:goerli": "npx hardhat run --network linea-goerli script/recreateNetworkFile.ts",
"test": "forge test",
Expand Down
167 changes: 167 additions & 0 deletions contracts/script/massImport/massImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { AbiCoder, Contract, formatEther, formatUnits, parseUnits } from "ethers";
import { ethers } from "hardhat";
import source from "./source.json";

const MAX_GAS_PRICE = parseUnits("0.7", "gwei"); // Set your maximum value here
const BLOCK_LIMIT_FACTOR = 80;
const BLOCK_TIME = 12; // Average block generation time in Ethereum, in seconds
const PORTAL_ADDRESS = "0xb3c0e57d560f36697f5d727c2c6db4e0c8f87bd8";
const BATCH_LENGTH = 100;

let lastBlockNumber: number | null = null;

interface AttestationPayload {
schemaId?: string;
expirationDate?: number;
subject: string;
attestationData: string;
}

async function callMassImport(batches: AttestationPayload[][], attestationRegistry: Contract) {
if (batches.length === 0) {
return;
}

// TODO: check batches length is decreasing regularly

const batch = batches.pop();

if (!batch) {
return;
}

// Retrieve information about the last block
const lastBlock = await ethers.provider.getBlock("latest");

try {
if (!lastBlock) {
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
return;
}

// Check if a transaction has already been sent in this block, or the previous block
if (
lastBlockNumber !== null &&
(lastBlockNumber === lastBlock.number || lastBlockNumber === lastBlock.number - 1)
) {
console.log(`Waiting for a new block to send the transaction.`);
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
return;
}

// Retrieve the current gas price
const gasPrice = (await ethers.provider.getFeeData()).gasPrice;

// Check if the gas price is defined
if (!gasPrice) {
console.log(`Gas price is unknown. Aborting transaction.`);
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
return;
}

// Check if the gas price is acceptable
if (gasPrice > MAX_GAS_PRICE) {
console.log(`Gas price of ${formatUnits(gasPrice, "gwei")} gwei is too high. Aborting transaction.`);
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
return;
}

// Calculate the gas limit based on the last block
const maxGas = (lastBlock.gasLimit * BigInt(BLOCK_LIMIT_FACTOR)) / BigInt(100);

const gasEstimated = await attestationRegistry.massImport.estimateGas(batch, PORTAL_ADDRESS);

if (gasEstimated > maxGas) {
console.log(
`Transaction estimated gas is ${formatUnits(gasEstimated, "wei")}, higher than the max (${formatUnits(
maxGas,
"wei",
)}). Aborting transaction.`,
);
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
return;
}

console.log(
`Sending a transaction with a gas price of ${formatUnits(
gasPrice.toString(),
"gwei",
)} gwei and an estimated gas of ${formatUnits(gasEstimated, "gwei")} for an estimated total of ${formatEther(
gasEstimated * gasPrice,
)} ETH`,
);

// Call the contract method
const txResponse = await attestationRegistry.massImport(batch, PORTAL_ADDRESS, {
gasPrice: gasPrice,
});

console.log(`Transaction sent with hash: ${txResponse.hash}`);

// Wait for the transaction receipt
const receipt = await txResponse.wait();

console.log(`Transaction successfully confirmed in block ${receipt.blockNumber}`);

// Update the number of the last block
lastBlockNumber = lastBlock.number;

console.log(`There are ${batches.length} batches left`);

// Recursively call the function for the next transaction
callMassImport(batches, attestationRegistry);
} catch (error: unknown) {
assertIsError(error);
console.log(`Transaction failed with error: ${error.message}. Retrying...`);
batches.unshift(batch); // Put the batch back in the list
setTimeout(callMassImport, BLOCK_TIME * 1000, batches, attestationRegistry); // Wait for a block generation time before retrying
}
}

async function main() {
const proxyAddress = process.env.ATTESTATION_REGISTRY_ADDRESS ?? "";
const attestationRegistry = await ethers.getContractAt("AttestationRegistry", proxyAddress);

const abiCoder = new AbiCoder();

const rawPayloads: AttestationPayload[] = source.map((item) => ({
...item,
subject: abiCoder.encode(["address"], [item.subject]),
attestationData: abiCoder.encode(["uint8"], [item.attestationData]),
}));

const attestationPayloads: AttestationPayload[] = [];

for (let i = 0; i < 1000; i++) {
attestationPayloads.push(...rawPayloads);
}

console.log(`${attestationPayloads.length} total payloads to attest`);

const batches: AttestationPayload[][] = Array.from(
{ length: Math.ceil(attestationPayloads.length / BATCH_LENGTH) },
(v, i) => attestationPayloads.slice(i * BATCH_LENGTH, i * BATCH_LENGTH + BATCH_LENGTH),
).reverse();

console.log(`${batches.length} batches of payloads to attest`);

await callMassImport(batches, attestationRegistry);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

function assertIsError(error: unknown): asserts error is Error {
if (!(error instanceof Error)) {
throw error;
}
}
68 changes: 68 additions & 0 deletions script/massImport/source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0xf1f5881ebc8b1bcb8df89faae642cbaba83f4940",
"attestationData": 1
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x7f60b39986383551002e7bb54b6bc7a73c4b4ee8",
"attestationData": 2
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x547b324b3f9e1f9f436fede6e88ae1ca816db6f3",
"attestationData": 3
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0xcb859f99f84ab770a50380680be94ad9331bcec5",
"attestationData": 4
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x59cf6818b9e90cd73b2120ac621c0b54a99c8340",
"attestationData": 5
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x591339da9cebef23f161710b1385d53cae1f3c6a",
"attestationData": 1
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x1ead1b980c754b69b2ab59f1abb6bca900c2073a",
"attestationData": 2
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0xed6c7974d8a9ec60644c4f49861dc3bb752ee123",
"attestationData": 3
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0xd1b59274c6682e8d6201976e95e971b8affaccd0",
"attestationData": 4
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x19383854824e6ed0270eedf7c3d56896e8c6e32e",
"attestationData": 5
},
{
"schemaId": "0xd1664d97bd195df77e3d5fe78c1737ab3adaa38bbe52a680d1aa30fa51f186ba",
"expirationDate": 1793835110,
"subject": "0x5cf6bb1765f5c1a3a12953deb26ff82ea4043acc",
"attestationData": 1
}
]

0 comments on commit 2d1c002

Please sign in to comment.