|
| 1 | +// env NETWORK=flare yarn ts-node scripts/analytics/run/min-condition-check.ts 265 |
| 2 | +// env NETWORK=songbird yarn ts-node scripts/analytics/run/min-condition-check.ts 265 |
| 3 | + |
| 4 | +import { BURN_ADDRESS } from "../../../libs/fsp-rewards/src/constants"; |
| 5 | +import { buildRewardClaimMerkleTree } from "../../../libs/fsp-rewards/src/reward-calculation/reward-merkle-tree"; |
| 6 | +import { IRewardClaimWithProof } from "../../../libs/fsp-rewards/src/utils/RewardClaim"; |
| 7 | +import { deserializeRewardDistributionData } from "../../../libs/fsp-rewards/src/utils/stat-info/reward-distribution-data"; |
| 8 | + |
| 9 | +function key(claim: IRewardClaimWithProof) { |
| 10 | + return `${claim.body.beneficiary.toLowerCase()}-${claim.body.claimType}`; |
| 11 | +} |
| 12 | + |
| 13 | +function assembleMap(claims: IRewardClaimWithProof[]) { |
| 14 | + const map = new Map<string, IRewardClaimWithProof>(); |
| 15 | + for (const claim of claims) { |
| 16 | + const k = key(claim); |
| 17 | + if (map.has(k)) { |
| 18 | + throw new Error(`Duplicate key ${k}`); |
| 19 | + } |
| 20 | + map.set(k, claim); |
| 21 | + } |
| 22 | + return map; |
| 23 | +} |
| 24 | + |
| 25 | +function testSubsetKeys(map: Map<string, IRewardClaimWithProof>, subset: Map<string, IRewardClaimWithProof>) { |
| 26 | + let burnDifference = 0n; |
| 27 | + for (const [k, v] of subset.entries()) { |
| 28 | + if (!map.has(k)) { |
| 29 | + throw new Error(`Key ${k} not found`); |
| 30 | + } |
| 31 | + const subsetAmount = BigInt(v.body.amount); |
| 32 | + const mapAmount = BigInt(map.get(k)!.body.amount); |
| 33 | + if (subsetAmount !== mapAmount) { |
| 34 | + if (v.body.beneficiary.toLowerCase() === BURN_ADDRESS.toLowerCase()) { |
| 35 | + burnDifference = subsetAmount - mapAmount; |
| 36 | + console.log("Additional burn:", burnDifference, Number(burnDifference) / 1e18); |
| 37 | + } else { |
| 38 | + throw new Error(`Amount mismatch for ${k}: ${subsetAmount} !== ${mapAmount}`); |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + return burnDifference; |
| 43 | +} |
| 44 | + |
| 45 | +function missingClaims(map: Map<string, IRewardClaimWithProof>, subset: Map<string, IRewardClaimWithProof>) { |
| 46 | + let totalAmount = 0n; |
| 47 | + let missingClaimCount = 0; |
| 48 | + for (const [k, v] of map.entries()) { |
| 49 | + if (!subset.has(k)) { |
| 50 | + const amount = BigInt(v.body.amount); |
| 51 | + console.log(`${v.body.beneficiary} ${v.body.claimType} ${v.body.amount} ${Number(amount) / 1e18}`); |
| 52 | + totalAmount += amount; |
| 53 | + missingClaimCount++; |
| 54 | + } |
| 55 | + } |
| 56 | + console.log("-------------------"); |
| 57 | + console.log("Total missing claims:", missingClaimCount); |
| 58 | + console.log("Total missing amount:", totalAmount, Number(totalAmount) / 1e18); |
| 59 | + return totalAmount; |
| 60 | +} |
| 61 | + |
| 62 | +function numberOfWeightBasedClaims(claims: IRewardClaimWithProof[]) { |
| 63 | + return claims.filter(c => c.body.claimType >= 2).length; |
| 64 | +} |
| 65 | + |
| 66 | +async function main() { |
| 67 | + if (!process.argv[2]) { |
| 68 | + throw new Error("no rewardEpochId"); |
| 69 | + } |
| 70 | + |
| 71 | + const rewardEpochId = parseInt(process.argv[2]); |
| 72 | + const network = process.argv[3]; |
| 73 | + if (!process.env.NETWORK) { |
| 74 | + throw new Error("NETWORK not set"); |
| 75 | + } |
| 76 | + console.log("Network:", process.env.NETWORK); |
| 77 | + |
| 78 | + const distributionData = deserializeRewardDistributionData(rewardEpochId, false); |
| 79 | + const distributionDataMinConditions = deserializeRewardDistributionData(rewardEpochId, true); |
| 80 | + const merkleTree = buildRewardClaimMerkleTree(distributionData.rewardClaims.map(c => c.body)); |
| 81 | + const merkleTreeMinConditions = buildRewardClaimMerkleTree(distributionDataMinConditions.rewardClaims.map(c => c.body)); |
| 82 | + |
| 83 | + const rewardClaimsMap = assembleMap(distributionData.rewardClaims); |
| 84 | + const rewardClaimsMinConditionsMap = assembleMap(distributionDataMinConditions.rewardClaims); |
| 85 | + |
| 86 | + const burnDifference = testSubsetKeys(rewardClaimsMap, rewardClaimsMinConditionsMap); |
| 87 | + console.log("All claims from minimal conditions present"); |
| 88 | + |
| 89 | + console.log("---------------Original ---------------------"); |
| 90 | + console.log("Reward distribution data:"); |
| 91 | + console.log("Merkle root: ", distributionData.merkleRoot, distributionData.merkleRoot === merkleTree.root); |
| 92 | + console.log("No. of weight based claims: ", distributionData.noOfWeightBasedClaims, distributionData.noOfWeightBasedClaims === numberOfWeightBasedClaims(distributionData.rewardClaims)); |
| 93 | + console.log("Claims:", distributionData.rewardClaims.length); |
| 94 | + |
| 95 | + console.log("---------------Min Conditions ---------------------"); |
| 96 | + console.log("Reward distribution data:"); |
| 97 | + console.log("Merkle root: ", distributionDataMinConditions.merkleRoot, distributionDataMinConditions.merkleRoot === merkleTreeMinConditions.root); |
| 98 | + console.log("No. of weight based claims: ", distributionDataMinConditions.noOfWeightBasedClaims, distributionDataMinConditions.noOfWeightBasedClaims === numberOfWeightBasedClaims(distributionDataMinConditions.rewardClaims)); |
| 99 | + console.log("Claims:", distributionDataMinConditions.rewardClaims.length); |
| 100 | + console.log("Missing claims:"); |
| 101 | + const missingClaimsAmount = missingClaims(rewardClaimsMap, rewardClaimsMinConditionsMap); |
| 102 | + if (missingClaimsAmount !== burnDifference) { |
| 103 | + throw new Error("Burn difference does not match missing claims"); |
| 104 | + } |
| 105 | + console.log("OK"); |
| 106 | +} |
| 107 | + |
| 108 | +main() |
| 109 | + .then(() => { |
| 110 | + console.dir("Done"); |
| 111 | + process.exit(0); |
| 112 | + }) |
| 113 | + .catch(e => { |
| 114 | + console.error(e); |
| 115 | + process.exit(1); |
| 116 | + }); |
0 commit comments