From 7dd4a65746635cde0d8ddfcf1d015c9c31c64da5 Mon Sep 17 00:00:00 2001 From: cwsnt Date: Tue, 18 Mar 2025 07:57:53 +0700 Subject: [PATCH] init: ossov snapshot script --- package-lock.json | 14 + package.json | 3 + scripts/ossov/config.json | 3 + scripts/ossov/results/progress_log.json | 7 + .../staking_rewards_snapshot_block.csv | 101 +++++ scripts/ossov/snapshot-ossov.ts | 418 ++++++++++++++++++ 6 files changed, 546 insertions(+) create mode 100644 scripts/ossov/config.json create mode 100644 scripts/ossov/results/progress_log.json create mode 100644 scripts/ossov/results/staking_rewards_snapshot_block.csv create mode 100644 scripts/ossov/snapshot-ossov.ts diff --git a/package-lock.json b/package-lock.json index 4d37dd0a6..86dca4d18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "sovrynsmartcontracts", "version": "1.0.0", "license": "Apache-2.0", + "dependencies": { + "csv-writer": "^1.6.0" + }, "devDependencies": { "@defi-wonderland/smock": "^2.3.4", "@nomicfoundation/hardhat-foundry": "^1.0.2", @@ -13403,6 +13406,12 @@ "node": ">= 10" } }, + "node_modules/csv-writer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz", + "integrity": "sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==", + "license": "MIT" + }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -56360,6 +56369,11 @@ "minimist": "^1.2.0" } }, + "csv-writer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz", + "integrity": "sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==" + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", diff --git a/package.json b/package.json index 2f9323108..0827f85f9 100644 --- a/package.json +++ b/package.json @@ -108,5 +108,8 @@ "pre-commit": "yarn prettier", "pre-push": "yarn lint && yarn prettier-check" } + }, + "dependencies": { + "csv-writer": "^1.6.0" } } diff --git a/scripts/ossov/config.json b/scripts/ossov/config.json new file mode 100644 index 000000000..e3e1b8dd7 --- /dev/null +++ b/scripts/ossov/config.json @@ -0,0 +1,3 @@ +{ + "addresses": [] +} diff --git a/scripts/ossov/results/progress_log.json b/scripts/ossov/results/progress_log.json new file mode 100644 index 000000000..68be54dfd --- /dev/null +++ b/scripts/ossov/results/progress_log.json @@ -0,0 +1,7 @@ +{ + "targetBlock": 14551520, + "totalStakers": 1121, + "processedCount": 100, + "lastProcessedIndex": 99, + "lastUpdateTime": "2025-03-14T15:11:59.963Z" +} diff --git a/scripts/ossov/results/staking_rewards_snapshot_block.csv b/scripts/ossov/results/staking_rewards_snapshot_block.csv new file mode 100644 index 000000000..6964499fc --- /dev/null +++ b/scripts/ossov/results/staking_rewards_snapshot_block.csv @@ -0,0 +1,101 @@ +address,osSOV_balance,osSOV_unclaimed +0x2bD2201BFE156A71EB0D02837172ffc237218505,0,70373076923076923 +0xaDbEE234896B1A53a80D113A7eceF5f2B4AeAED6,0,35186538461538461 +0x3fF003142742d54E7c6A0c8dA2861dEAE392Dcce,0,0 +0x639c1170066c025B2Aca4361D9Ec7De6998c2576,0,0 +0x78f9ddE9838b5F6d58e9134B2D30dfc9d13550c1,0,2213066157129478748 +0x99679f6c76d4bdb3082Bd4b944437ECee93D596A,0,0 +0xC857a07Ca20babEdA3f76E9Be6B1B2701A29fC11,0,1926544606183631920 +0x68911e58f65062066abc81559a6DEf797402f345,0,250639587181134990291 +0x3e39235C3822730C0d9F62599D3fDA5cA1366e37,0,69881219924369558204 +0xE3f1121A1ddC59572Fa764334Dd007403EACaDdb,0,0 +0xBBFd9988eA52EE8C88f7BA28d1db827e73955ae8,0,0 +0x9227dFf3a69caC5bc42984256588c88d1581237B,0,662660411071459007 +0xC2AF342A3A3b861064792070849cFb33A55552E2,0,0 +0x3ce20d8d94Fe41e71190361C2d424CffaFe401Ce,0,0 +0x351B2237DF1427AD44b8192ca5EFF13c15f3F26e,0,0 +0x2Bd4045d7f9444643C4506C0d8df181392eCbEbF,0,0 +0x3c821bB57ea467d6A0fCccE2c853C8C4117Bca84,0,0 +0x5e117276A540DfFE52f7406FA453359631B3a6C5,0,0 +0x5F8A304af1311EB5630625Bf2042c2ACE00489c3,0,797884615384615384615 +0x2E8A5909Ac98c5913a7BA4f8A6Ec2cd32CedE625,0,0 +0xA6adEdD1aDc8237aC8c5d3009cC5D4B90Fc0a8CF,0,2177625186448418636 +0xb401AbB70432d13e46d65aB4DEe47232437D28d0,0,406976580264285131 +0x7579265244a4CB64C4BEa197161A449E16FD28D0,0,0 +0xb0cc4ed0CC3c2231581eaf6feA6f422928A23F4F,0,7255852375396518329 +0x4B0cf52b56E664b377cb588Cd300Be94B5062B1c,0,12394445842574742712 +0x5f9fEbf1F2A99fe11Edad119462DB23f28A6dDBb,0,472500000000000000 +0xa8e98bD2D3DA823C7503FE6f4cf2FCD01CF6BE4C,0,270179527241119297683 +0xE48440c1f17235038C2B9E535C50fC64c6F8703A,0,63873320840255767027 +0x121Acf78D8B71eeC95Da4e5FFBEFbc07674A64Cd,0,0 +0xc5fd654bA7c9C9d6b5710Fc347c1306e855Ec582,0,0 +0xf9A0c6eEa16E4F4c5ef40D835Eb1A2Fc489F4ACD,0,0 +0x77535aEF7F300d9ce6d9dE85A7E813F8DcF23Bc5,0,20786538461538461 +0x8d4190772bA1C59138303679c2Ac89E3d00736A7,0,0 +0xA9deEDBa572029155090DE0eea714c14015c2043,0,0 +0x41a350d22d432C66c2A4E06cDc54A1683782B2Bd,0,0 +0xd5C96ACf8004fF1f3c59f284a92F0d964655599a,0,0 +0x7a45C487784ea72Dce85D48A9E3F5Ad5EDc34Ee2,0,3740216444480538777 +0x922f2928f4d244611e8beF9e8dAD88A5B6E2B59C,0,12028846153846153846 +0xba710BE407751FD2c4c8eD7e95c452d11c2E4A85,0,75384069603443427 +0x1d0b10032bd425BEA8f2CDe0D52a280243C66965,0,633013306991096971 +0xb8128b1063e86C821B287778ed500b36033Ae02B,0,9195353367384434812 +0xBa986c82A78db4Cc36D05fd99836C795bece1663,0,0 +0x843857D513aB6E360c3E85ce8D85694d2b19450a,0,10293576923076923076 +0xA99B5cb190582aE0966778C26188eABbf0feFfb8,0,150569705575226228260 +0x8BE13377A580653551c593BD3752241CD807dee0,0,2850220931814240225 +0xC1307678f788ec362124090207e81486D06fA4b0,0,0 +0x94c74DFA070500E317DddE4d64007FE26ae8aB9A,0,0 +0xA70bceaa99dA8CF2EDAe5b17DA79607cBF6286Dd,0,47655153557748300655 +0xC4ecb7bA7C8894465Aa3DE607B50bFECde581875,0,6229959598852057396 +0x1FA4D89f1d044dCa763610F30E51AAda92C6c38c,0,0 +0x0257E54fB88E143511CD2e4B94aaE8e1FA6DE520,0,356368870325081920980 +0x90fDF1b91a06e59b736E3a6FbFc5F0D06b4E928f,0,68061082659827435446 +0xf63a521ad77803a3b5f92EDdb4613D719d32448a,0,27070983087573544674 +0xDfd12D539448385a1A49797e7E72Ece104448546,0,6478129451499409018 +0xEAcDD6b4B80Fcb241A4cfAb7f46e886F19c89340,0,0 +0xbE01Ac936a0567cE6328524ae50a2aD036f5Cd07,0,0 +0x2b5B3c98AD9b7C6b814eDc5e63DD487e6BC5f454,0,27366684015956229185 +0x6e4d7FdD7Cae2A616B37a44f0E090A281120D1C1,0,977649665971543642 +0xD2FaE24701e3a5B862Fe6EE2F386EC40A872c68b,0,0 +0xF50f458AC4961185FAe7C9b5599A119EAcaD009b,0,16052113019360800904 +0xBeD7482409659EAB8B9B6EfD746f8a53699bDda6,0,93460938148688639 +0x9b06Ce7455D3350D00994a886F77CAD77B5724b6,0,0 +0x380321D5ef1596a15B78ff63a7B268399968769C,0,20250000000000000000 +0x93624C41A99e17089D0856a974E6c018cBbEE93D,0,0 +0xdFcAd7b53504ada98D7a93F02ff049b18Ff2B18C,0,0 +0x7214E37eF397Dd58b95888C6B7eBCA5629bd83A5,0,124615384615384 +0x285695009860f910f78401345ff353BA6d0cfE58,0,18000000000000000 +0x954095D2801Cc6c0526ED8816CA4d99Fbb8a0330,0,0 +0x342A085E69aC726B9965AE6Ca0E6f23a23256E03,0,0 +0xf50c7B18C127B43CF0De88Ed753f3f8D0235eF8a,0,0 +0x442269e9E7cB11AEfD08EB6981Ee7DDEA1Fc938E,0,33267597205150657 +0x396dF64508673753BD43fE1465D745CB2Cabc079,0,70857692307692307 +0x42Ca6b4266179c47Cd1e19a054214965DF28da78,0,0 +0xb3fbDA6D402a4874DB14ffF4495e6dFbD34b6ff4,0,131191536350945898 +0x73d9Ac8CDB2E0c7aE1e743CE7078Ab2ea4b08481,0,0 +0xf33350b3778985A606468906908eb002644c2D59,0,113249270433969323 +0xE93709A5d4AFA46ed6A2D1B4D93dd13137bAbcB9,0,0 +0xCbfeaA6D42d5D5A15Cb5D499dd5905e1231aF74a,0,1177316173221223612 +0xB9bE759044e13F545225173b286C70410a5ACA31,0,0 +0x79432Cc61878Bc3ed352ea311761359a2625AF8F,0,0 +0x538aB12CDa2a972c9D37828A7A9239fb97b863b6,0,319086787820585517 +0x2d3117E30D36D561f878640B82BE8209fC69eDc0,0,0 +0xBeF54c3b0898d005fB500C6931DED87579d7a3A8,0,899569723065547088711 +0x907f6BA8cbbc4528D262c69633062B5f8FA95136,0,11809828723107367562 +0xaf6Da5C20cd40F43ED023Ee1EA9F9fcbFf724743,0,29402100000000000000 +0x709371EBe78323999dD3e1D2d6DE46b1D57b082c,0,0 +0x9a32D0389D3910683F5A1851cf3D60564Acb26E2,0,309085504041778035 +0xF4A50Ca186DABe0c3A17DE7F88A415488AAf011e,0,127107692307692307692 +0xD805Aa04c701049B282a94eCBf6dD6481BFEC162,0,0 +0x20ad091f64EB31267C248F6200775561BcA17168,0,0 +0x9ccB7EcF70D0481538aD6632BB5D8dd0E909Fcf8,0,0 +0x1d1C383D839F4F7426cCdADC7307a2344e66e252,0,6808680000000000 +0x52882EC01fEafbF9de48115D10df6BB75CA3912e,0,1062562020939700111 +0x49eb2a1eF5bbfD234e4F50c78bCf78177C0F50E7,0,2548609327532716213 +0xD3362E86aaBCaf3f3508A6B878Ab41Fcb82F2Bd5,0,37607916951281549 +0xC8D69403c231A31a77fD5e6e87367Fc8f12b0c5B,0,143446153846153846 +0x0244f3ba246aEE6F6214ea1439bf64084A323FD2,0,253641573715981180 +0x08893092E474F58aDaB42eDd09C37A31a13aA8e4,0,0 +0x2150e80DAE8359592D2F7A3f83D9DFA4Cd343c71,0,9210302329223794 +0x4Ba38d76D13Fd9cb2691DA53490085708D1E5053,0,0 diff --git a/scripts/ossov/snapshot-ossov.ts b/scripts/ossov/snapshot-ossov.ts new file mode 100644 index 000000000..4ea6a10ed --- /dev/null +++ b/scripts/ossov/snapshot-ossov.ts @@ -0,0 +1,418 @@ +import { ethers } from "ethers"; +import { createObjectCsvWriter } from "csv-writer"; +import dotenv from "dotenv"; +import fs from "fs"; +import path from "path"; + +dotenv.config(); + +interface StakingData { + address: string; + osSOV_balance: string; + osSOV_unclaimed: string; +} + +interface TokensStakedEvent { + args: [string, bigint, bigint, bigint] & { + staker: string; + amount: bigint; + lockedUntil: bigint; + totalStaked: bigint; + }; +} + +const STAKING_ABI = [ + "event TokensStaked(address indexed staker, uint256 amount, uint256 lockedUntil, uint256 totalStaked)", +]; + +const REWARDS_ABI = [ + "function getArbitraryStakerCurrentReward(bool considerMaxDuration, uint256 startTime, address staker) view returns (uint256 nextWithdrawTimestamp, uint256 amount)", + "function getOsSOV() view returns (address)", + "function getStaking() view returns (address)", +]; + +const ERC20_ABI = [ + "function balanceOf(address account) view returns (uint256)", +]; + +const ADDRESSES = { + REWARDS: "0xFdC57Cb52264209afd1559E7E3Db0F28351E9422", +} as const; + +const CHUNK_SIZE = 10000000; + +async function getStakingData(params: { + startFromBlock?: number; + endBlock?: number; + specificAddresses?: string[]; +}) { + console.log("Starting snapshot process..."); + + const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); + if (!provider) throw new Error("Failed to initialize provider"); + + // define target block + let { startFromBlock, endBlock, specificAddresses } = params; + if (!startFromBlock) startFromBlock = 0; + const targetBlock = endBlock || (await provider.getBlockNumber()); + console.log(`Using block number: ${targetBlock}`); + console.log(`Starting from block: ${startFromBlock}`); + + const rewardsContract = new ethers.Contract( + ADDRESSES.REWARDS, + REWARDS_ABI, + provider, + ); + const stakingContractAddress = await rewardsContract.getStaking(); + const stakingContract = new ethers.Contract( + stakingContractAddress, + STAKING_ABI, + provider, + ); + + // Get osSOV token address and contract + const osSOVAddress = await rewardsContract.getOsSOV(); + const osSOVContract = new ethers.Contract(osSOVAddress, ERC20_ABI, provider); + console.log(`osSOV token address: ${osSOVAddress}`); + + let stakers: string[] = []; + + // If specific addresses are provided, use them directly + if (specificAddresses && specificAddresses.length > 0) { + console.log( + `Using ${specificAddresses.length} provided addresses instead of querying events`, + ); + stakers = specificAddresses; + } else { + // Otherwise fetch all staking events in chunks + console.log("Fetching staking events..."); + const filter = stakingContract.filters.TokensStaked(); + + // Process in chunks of 10,000 blocks + let allEvents: TokensStakedEvent[] = []; + + for ( + let fromBlock = startFromBlock; + fromBlock <= targetBlock; + fromBlock += CHUNK_SIZE + ) { + const toBlock = Math.min(fromBlock + CHUNK_SIZE - 1, targetBlock); + console.log(`Querying blocks ${fromBlock} to ${toBlock}...`); + + try { + const chunkEvents = ( + await stakingContract.queryFilter(filter, fromBlock, toBlock) + ).map((event) => { + return { + args: event.args as unknown as [string, bigint, bigint, bigint] & { + staker: string; + amount: bigint; + lockedUntil: bigint; + totalStaked: bigint; + }, + } as TokensStakedEvent; + }); + + allEvents = [...allEvents, ...chunkEvents]; + console.log( + `Found ${chunkEvents.length} events in this chunk. Total: ${allEvents.length}`, + ); + + // Add delay between chunks to avoid rate limiting + if (fromBlock + CHUNK_SIZE <= targetBlock) { + console.log("Waiting before next chunk..."); + await sleep(1000); + } + } catch (error) { + console.error(`Error querying blocks ${fromBlock}-${toBlock}:`, error); + // Continue to next chunk + } + } + + // Get unique stakers + stakers = [...new Set(allEvents.map((event) => event.args.staker))]; + console.log(`Found ${stakers.length} unique stakers`); + } + + // Create results directory if it doesn't exist + const resultsDir = path.resolve(__dirname, "./results"); + if (!fs.existsSync(resultsDir)) { + fs.mkdirSync(resultsDir, { recursive: true }); + } + + // Define CSV file path + const csvFilePath = path.join( + resultsDir, + `staking_rewards_snapshot_block.csv`, + ); + + // Check if file exists and load existing data + let existingData: Record = {}; + if (fs.existsSync(csvFilePath)) { + console.log(`Found existing CSV file. Loading data...`); + const csvContent = fs.readFileSync(csvFilePath, "utf8"); + const rows = csvContent.split("\n").slice(1); // Skip header + + for (const row of rows) { + if (!row.trim()) continue; + const [address, osSOV_balance, osSOV_unclaimed] = row.split(","); + if (address) { + existingData[address.toLowerCase()] = { + address, + osSOV_balance, + osSOV_unclaimed, + }; + } + } + console.log(`Loaded ${Object.keys(existingData).length} existing records`); + + // Filter out already processed addresses + stakers = stakers.filter( + (staker) => + !existingData[staker.toLowerCase()] || + specificAddresses?.includes(staker), + ); + console.log(`Remaining addresses to process: ${stakers.length}`); + } + + // Process all of the unique stakers + const results: StakingData[] = []; + let successCount = 0; + let errorCount = 0; + const failedAddresses: string[] = []; + + // Create progress log file + const progressLogPath = path.join(resultsDir, `progress_log.json`); + let progressData = { + targetBlock, + totalStakers: stakers.length, + processedCount: 0, + lastProcessedIndex: -1, + lastUpdateTime: new Date().toISOString(), + }; + + // Load existing progress if available + if (fs.existsSync(progressLogPath)) { + try { + progressData = JSON.parse(fs.readFileSync(progressLogPath, "utf8")); + console.log(`Resuming from index ${progressData.lastProcessedIndex + 1}`); + } catch (error) { + console.error(`Error reading progress log:`, error); + } + } + + // Process in smaller chunks and save progress regularly + const PROCESS_CHUNK_SIZE = 20; // Process 20 addresses at a time + + for (let i = progressData.lastProcessedIndex + 1; i < stakers.length; i++) { + const staker = stakers[i]; + if (!staker) continue; + + try { + // Add delay to avoid rate limiting + await sleep(100); + + const [balance, [, unclaimed]] = await Promise.all([ + osSOVContract.balanceOf(staker, { blockTag: targetBlock }), + rewardsContract.getArbitraryStakerCurrentReward(false, 0, staker, { + blockTag: targetBlock, + }), + ]); + + results.push({ + address: staker, + osSOV_balance: balance.toString(), + osSOV_unclaimed: unclaimed.toString(), + }); + + successCount++; + progressData.processedCount++; + progressData.lastProcessedIndex = i; + + if (successCount % 10 === 0) { + console.log( + `Processed ${successCount}/${stakers.length} stakers successfully`, + ); + } + } catch (error) { + console.error(`Error processing staker ${staker}:`, error); + errorCount++; + failedAddresses.push(staker); + } + + // Save progress and results every PROCESS_CHUNK_SIZE addresses + if ((i + 1) % PROCESS_CHUNK_SIZE === 0 || i === stakers.length - 1) { + // Update progress log + progressData.lastUpdateTime = new Date().toISOString(); + fs.writeFileSync(progressLogPath, JSON.stringify(progressData, null, 2)); + + // Append to CSV if we have new results + if (results.length > 0) { + // Combine new results with existing data + const allResults: StakingData[] = [ + ...Object.values(existingData), + ...results, + ]; + + // Write to CSV + const csvWriter = createObjectCsvWriter({ + path: csvFilePath, + header: [ + { id: "address", title: "address" }, + { id: "osSOV_balance", title: "osSOV_balance" }, + { id: "osSOV_unclaimed", title: "osSOV_unclaimed" }, + ], + }); + + await csvWriter.writeRecords(allResults); + console.log( + `Saved progress: ${progressData.processedCount}/${stakers.length} addresses processed`, + ); + + // Update existing data with new results to avoid duplicates in next chunk + for (const result of results) { + existingData[result.address.toLowerCase()] = result; + } + + // Clear results array for next chunk + results.length = 0; + } + } + } + + console.log("\nSnapshot completed!"); + console.log( + `Successfully processed: ${successCount}/${stakers.length} stakers`, + ); + console.log(`Errors encountered: ${errorCount} stakers`); + console.log(`Total records in CSV: ${Object.keys(existingData).length}`); + console.log(`Results saved to: ${csvFilePath}`); + + // If there are failed addresses, try to process them again + if (failedAddresses.length > 0) { + console.log( + `\nAttempting to process ${failedAddresses.length} failed addresses again...`, + ); + + // Save failed addresses to a file for future reference + const failedAddressesPath = path.join( + resultsDir, + `failed_addresses_${targetBlock}.json`, + ); + fs.writeFileSync( + failedAddressesPath, + JSON.stringify(failedAddresses, null, 2), + ); + console.log(`Failed addresses saved to: ${failedAddressesPath}`); + + // Wait a bit longer before retrying + await sleep(5000); + + // Retry with increased delay + const retryResults: StakingData[] = []; + let retrySuccessCount = 0; + const stillFailedAddresses: string[] = []; + + for (const staker of failedAddresses) { + try { + // Longer delay for retries + await sleep(500); + + const [balance, [, unclaimed]] = await Promise.all([ + osSOVContract.balanceOf(staker, { blockTag: targetBlock }), + rewardsContract.getArbitraryStakerCurrentReward(false, 0, staker, { + blockTag: targetBlock, + }), + ]); + + retryResults.push({ + address: staker, + osSOV_balance: balance.toString(), + osSOV_unclaimed: unclaimed.toString(), + }); + + retrySuccessCount++; + console.log(`Retry successful for ${staker}`); + } catch (error) { + console.error(`Retry failed for staker ${staker}:`, error); + stillFailedAddresses.push(staker); + } + } + + // If we recovered some addresses, update the CSV + if (retrySuccessCount > 0) { + const finalResults = [...Object.values(existingData), ...retryResults]; + + const csvWriter = createObjectCsvWriter({ + path: csvFilePath, + header: [ + { id: "address", title: "address" }, + { id: "osSOV_balance", title: "osSOV_balance" }, + { id: "osSOV_unclaimed", title: "osSOV_unclaimed" }, + ], + }); + + await csvWriter.writeRecords(finalResults); + + console.log(`\nRetry completed!`); + console.log( + `Successfully recovered: ${retrySuccessCount}/${failedAddresses.length} stakers`, + ); + console.log(`Total records in CSV: ${finalResults.length}`); + } + + // If there are still failed addresses, save them and notify + if (stillFailedAddresses.length > 0) { + const stillFailedPath = path.join( + resultsDir, + `still_failed_addresses_${targetBlock}.json`, + ); + fs.writeFileSync( + stillFailedPath, + JSON.stringify(stillFailedAddresses, null, 2), + ); + console.log( + `\n⚠️ WARNING: ${stillFailedAddresses.length} addresses still failed processing`, + ); + console.log(`These addresses saved to: ${stillFailedPath}`); + console.log(`You can process them manually by running:`); + console.log( + `node scripts/ossov/snapshot-ossov.js --block ${targetBlock} --addresses ${stillFailedPath}`, + ); + + // Return the list of still failed addresses + return stillFailedAddresses; + } + } + + return []; +} + +let specificAddresses: string[] | undefined; +const configPath = path.resolve(__dirname, "./config.json"); +if (fs.existsSync(configPath)) { + try { + const config = JSON.parse(fs.readFileSync(configPath, "utf8")); + specificAddresses = config.addresses; + console.log( + `Loaded ${specificAddresses?.length} addresses from config.json`, + ); + } catch (error) { + console.error("Error reading config file:", error); + } +} + +getStakingData({ + startFromBlock: 900618, // block where the staking contract have stake txs + specificAddresses: + specificAddresses && specificAddresses.length > 0 + ? specificAddresses + : undefined, +}) + .then(() => process.exit(0)) + .catch((error) => { + console.error("Fatal error:", error); + process.exit(1); + }); + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));