From 6d4872de7fdc4dd065f8f27f24edd105235b09b0 Mon Sep 17 00:00:00 2001 From: "kacper.golem" Date: Tue, 30 Jul 2024 12:06:21 +0200 Subject: [PATCH 1/2] OCT-1730: Improvements for the epoch verifier --- epoch-verifier/README.md | 8 ++- epoch-verifier/src/data/context.ts | 37 ++++++++++---- epoch-verifier/src/data/fetcher.ts | 15 ++++-- epoch-verifier/src/data/models.ts | 49 ++++++++++++++++++- epoch-verifier/src/verifications/donations.ts | 26 +++++++--- epoch-verifier/src/verifications/utils.ts | 2 +- epoch-verifier/src/verifier.ts | 37 +++++++++----- 7 files changed, 138 insertions(+), 36 deletions(-) diff --git a/epoch-verifier/README.md b/epoch-verifier/README.md index 93eed44a4..9f8e7d742 100644 --- a/epoch-verifier/README.md +++ b/epoch-verifier/README.md @@ -18,8 +18,14 @@ Check usage with: yarn verify:help ``` +If you want to use the script before the end of the AW, you can use the `--simulated` option. This will use the current state of the BE and simulate the end of the AW. -Script has already defined several, most important deployments (`--deployment` option), but one can connect to any other octant backend using `--url` option. +e.g +``` +yarn verify:mainnet --simulated +``` + +Script has already defined several, most important deployments (`--deployment` option), but one can connect to any other octant backend using `--url` option. diff --git a/epoch-verifier/src/data/context.ts b/epoch-verifier/src/data/context.ts index c585c9954..7cc11d9cc 100644 --- a/epoch-verifier/src/data/context.ts +++ b/epoch-verifier/src/data/context.ts @@ -6,15 +6,17 @@ import { Reward, UserBudget, UserDonation, - EpochUqs + EpochUqs, + SimulatedReward } from "./models" export interface Context { - allocations: Allocation[] - budgets: Map - epochInfo: EpochInfo - rewards: Reward[] - uqs: Map + allocations: Allocation[]; + budgets: Map; + epochInfo: EpochInfo; + rewards: Reward[]; + uqs: Map; + isSimulated: boolean; } function transformToAllocations(allocationRecords: AllocationRecord[]): Allocation[] { @@ -29,7 +31,7 @@ function transformToAllocations(allocationRecords: AllocationRecord[]): Allocati return Array.from(allocations.values()) } -export function buildContext(userBudgets: UserBudget[], allocations: AllocationRecord[], rewards: Reward[], epochInfo: EpochInfo, epochUqs: EpochUqs[]): Context { +export function buildContext(userBudgets: UserBudget[], allocations: AllocationRecord[], rewards: Reward[], epochInfo: EpochInfo, epochUqs: EpochUqs[], isSimulated: boolean): Context { const positiveUserBudgets = userBudgets.filter(positiveUserBudget => positiveUserBudget.amount !== BigInt(0)); @@ -38,7 +40,8 @@ export function buildContext(userBudgets: UserBudget[], allocations: AllocationR budgets: new Map(positiveUserBudgets.map(value => [value.user, value.amount] as const)), epochInfo, rewards, - epochUqs: new Map(epochUqs.map(value => [value.userAddress, value.uniquenessQuotient] as const)) + epochUqs: new Map(epochUqs.map(value => [value.userAddress, value.uniquenessQuotient] as const)), + isSimulated } } @@ -81,9 +84,23 @@ export function individualDonationsAggregatedByProjectsWithUQs(context: Context) return individualDonations; } -export function rewardsByProject(context: Context): Map { - return new Map(context.rewards +function _getSimulatedRewardsByProject(rewards: SimulatedReward[]): Map { + return new Map(rewards + .filter(r => r.matched !== BigInt(0)) + .map((r) => [r.address, r] as const) + ); +} + +function _getRewardsByProject(rewards: Reward[]): Map { + return new Map(rewards .filter(r => r.matched !== BigInt(0)) .map((r) => [r.project, r] as const) ); } + +export function rewardsByProject(context: Context): Map { + if (context.isSimulated) { + return _getSimulatedRewardsByProject(context.rewards.projectsRewards); + } + return _getRewardsByProject(context.rewards); +} diff --git a/epoch-verifier/src/data/fetcher.ts b/epoch-verifier/src/data/fetcher.ts index b637bde54..8273e5398 100644 --- a/epoch-verifier/src/data/fetcher.ts +++ b/epoch-verifier/src/data/fetcher.ts @@ -12,7 +12,8 @@ import { UserBudget, AllocationRecord, ApiRewardsBudgets, ApiAllocations, ApiRewards, - ApiEpochUqs, EpochUqsImpl + ApiEpochUqs, EpochUqsImpl, + FinalizedSimulationImpl } from "./models"; const REQUEST_TIMEOUT = 150_000; @@ -60,10 +61,6 @@ export class HttpFetcher { return this._get_array(`/allocations/epoch/${epoch}?includeZeroAllocations=true`, "users' allocations", AllocationImpl, (data: ApiAllocations) => data.allocations) } - async apiGetRewards(epoch: number): Promise { - return this._get_array(`/rewards/projects/epoch/${epoch}`, "projects rewards", RewardImpl, (data: ApiRewards) => data.rewards) - } - async apiGetEpochInfo(epoch: number): Promise { return this._get(`/epochs/info/${epoch}`, "epoch info", EpochInfoImpl) } @@ -71,4 +68,12 @@ export class HttpFetcher { async apiGetEpochUqs(epoch: number): Promise { return this._get_array(`/user/uq/${epoch}/all`, "epoch uqs", EpochUqsImpl, (data: ApiEpochUqs) => data.uqsInfo); } + + async apiGetRewards(epoch: number): Promise { + return this._get_array(`/rewards/projects/epoch/${epoch}`, "projects rewards", RewardImpl, (data: ApiRewards) => data.rewards) + } + + async apiGetFinalizedSimulated() { + return this._get('/snapshots/finalized/simulate', 'finalized simulated snapshot', FinalizedSimulationImpl); + } } diff --git a/epoch-verifier/src/data/models.ts b/epoch-verifier/src/data/models.ts index c0da734b3..1613501d7 100644 --- a/epoch-verifier/src/data/models.ts +++ b/epoch-verifier/src/data/models.ts @@ -58,12 +58,20 @@ export interface Allocation { user: Address; } -export interface Reward { - allocated: bigint; +interface IReward { matched: bigint; +} + +export interface Reward extends IReward { + allocated: bigint; project: Address; } +export interface SimulatedReward extends IReward { + amount: bigint; // allocated + matched + address: Address; +} + export interface EpochInfo { communityFund: bigint; individualRewards: bigint; @@ -83,6 +91,12 @@ export interface EpochUqs { uniquenessQuotient: number; } +export interface FinalizedSimulation { + patronsRewards: bigint; + matchedRewards: bigint; + projectRewards: SimulatedReward[]; +} + export class UserBudgetImpl implements Deserializable { user: Address; @@ -128,6 +142,20 @@ export class RewardImpl implements Deserializable { } } +export class SimulatedRewardImpl implements Deserializable { + amount: bigint; + matched: bigint; + address: Address; + + from(input: any) { + this.address = input.address; + this.amount = BigInt(input.amount); + this.matched = BigInt(input.matched); + + return this + } +} + export class EpochInfoImpl implements Deserializable { individualRewards: bigint; @@ -180,3 +208,20 @@ export class EpochUqsImpl implements Deserializable{ } } +export class FinalizedSimulationImpl implements Deserializable{ + patronsRewards: bigint; + matchedRewards: bigint; + projectsRewards: SimulatedReward[]; + + from(input: any) { + this.patronsRewards = BigInt(input.patronsRewards); + this.matchedRewards = BigInt(input.matchedRewards); + this.projectsRewards = input.projectsRewards.map((reward: any) => { + const simulatedReward = new SimulatedRewardImpl(); + return simulatedReward.from(reward); + }); + + return this; + } +} + diff --git a/epoch-verifier/src/verifications/donations.ts b/epoch-verifier/src/verifications/donations.ts index c1bafeb5c..6e6ce0e0f 100644 --- a/epoch-verifier/src/verifications/donations.ts +++ b/epoch-verifier/src/verifications/donations.ts @@ -13,8 +13,7 @@ function _groupAllocationsByProjects(aggregatedDonations: Map { const sumOfSqrt = values.reduce((sum, value) => sum + sqrt(multiplyFloatByBigInt(value.uq, value.amount)), BigInt(0)); - const resultValue = sumOfSqrt ** BigInt(2); - + let resultValue = sumOfSqrt ** BigInt(2); totalPlainQF += resultValue; result.set(project, resultValue); @@ -74,22 +73,29 @@ function _getUserAllocationsForProjects(context: Context): Map export function verifyUserDonationsVsRewards(context: Context): VerificationResult { const projectsAllocations = Array.from(_getUserAllocationsForProjects(context).entries()); const rewards = rewardsByProject(context); + + if (context.isSimulated) { + return assertAll(projectsAllocations, ([proposal, allocated]) => assertEq(allocated, rewards.get(proposal)!.amount - rewards.get(proposal)!.matched, BigInt(100), true)); + } + return assertAll(projectsAllocations, ([proposal, allocated]) => assertEq(allocated, rewards.get(proposal)!.allocated, BigInt(100), true)); - return assertEq(10, 10, BigInt(100), true); } export function verifyRewardsVsUserDonations(context: Context): VerificationResult { const projectsAllocations = _getUserAllocationsForProjects(context); const rewards = Array.from(rewardsByProject(context).entries()); - return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.allocated, projectsAllocations.get(project)!, BigInt(100), true)); - return assertEq(10, 10, BigInt(100), true); + if (context.isSimulated) { + return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.amount - reward.matched, projectsAllocations.get(project)!, BigInt(100), true)); + } + + return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.allocated, projectsAllocations.get(project)!, BigInt(100), true)); } export function verifyMatchedFunds(context: Context): VerificationResult { const rewards = rewardsByProject(context) const totalMatchedFunding = context.epochInfo.matchedRewards; - const aggregatedDonations = individualDonationsAggregatedByProjectsWithUQs(context) + const aggregatedDonations = individualDonationsAggregatedByProjectsWithUQs(context); const [aggregatedCappedMatchedFunding, leftover] = _computeMatchingFundQFAndCapAndUQ(aggregatedDonations, totalMatchedFunding); return assertAll(aggregatedCappedMatchedFunding, ([project, matched]) => { @@ -116,6 +122,14 @@ export function verifyTotalWithdrawals(context: Context): VerificationResult { .map(([user, donationsSum]) => context.budgets.get(user)! - donationsSum) .reduce((acc, user_claimed) => acc + user_claimed, BigInt(0)) + if (context.isSimulated) { + const rewards = context.rewards.projectsRewards + .filter((reward) => reward.matched !== BigInt(0)) + .reduce((acc, reward) => acc + reward.amount, BigInt(0)) + + return assertEq(claimed + rewards, context.epochInfo.totalWithdrawals) + } + const rewards = context.rewards .filter((reward) => reward.matched !== BigInt(0)) .reduce((acc, reward) => acc + reward.allocated + reward.matched, BigInt(0)) diff --git a/epoch-verifier/src/verifications/utils.ts b/epoch-verifier/src/verifications/utils.ts index 49d23eb54..d83cd9780 100644 --- a/epoch-verifier/src/verifications/utils.ts +++ b/epoch-verifier/src/verifications/utils.ts @@ -2,7 +2,7 @@ import { VerificationResult } from "../runner"; type Predicate = (elem: T) => boolean | VerificationResult -export const SCALE_FACTOR = 10n ** 18n; +export const SCALE_FACTOR = 10n ** 36n; function abs(v: bigint): bigint { if (v < BigInt(0)) { diff --git a/epoch-verifier/src/verifier.ts b/epoch-verifier/src/verifier.ts index f47f9fe3b..f79be5975 100644 --- a/epoch-verifier/src/verifier.ts +++ b/epoch-verifier/src/verifier.ts @@ -8,9 +8,10 @@ import { registerVerifications } from "./verifications"; interface Options { - deployment?: string - epoch: number - url?: string + deployment?: string; + epoch: number; + url?: string; + simulated?: boolean; } const DEPLOYMENTS: { [key: string]: string } = { @@ -39,20 +40,33 @@ function getDeploymentUrl(options: Options): string { } async function run(epoch: string, opts: any) { - const options: Options = { epoch: parseInt(epoch, 10), ...opts } + const options: Options = { epoch: parseInt(epoch, 10), simulated: opts.simulated, ...opts } const baseUrl = getDeploymentUrl(options) console.log(`Using url: ${baseUrl}`) - console.log(`Running verification scripts for epoch: ${options.epoch}`) + + if (options.simulated) { + console.log(`Running verification scripts for epoch: ${options.epoch} in simulated mode.`) + } + else { + console.log(`Running verification scripts for epoch: ${options.epoch}.`) + } const fetcher = new HttpFetcher(baseUrl) - const results = await Promise.all([ + const fetchPromises = [ fetcher.apiGetUserBudgets(options.epoch), fetcher.apiGetAllocations(options.epoch), - fetcher.apiGetRewards(options.epoch), fetcher.apiGetEpochInfo(options.epoch), fetcher.apiGetEpochUqs(options.epoch) - ]) + ]; + + if (options.simulated) { + fetchPromises.push(fetcher.apiGetFinalizedSimulated()); + } else { + fetchPromises.push(fetcher.apiGetRewards(options.epoch)); + } + + const results = await Promise.all(fetchPromises); if (results.some(isNull)) { process.exit(1) @@ -61,11 +75,11 @@ async function run(epoch: string, opts: any) { const [ userBudgets, allocations, - rewards, epochInfo, - epochUqs + epochUqs, + rewards ] = results; - const context = buildContext(userBudgets!, allocations!, rewards!, epochInfo!, epochUqs!) + const context = buildContext(userBudgets!, allocations!, rewards!, epochInfo!, epochUqs!, options.simulated); const runner = new Runner() registerVerifications(runner) @@ -81,6 +95,7 @@ program .description("Epoch verifier script.") .addOption(new Option("--deployment ", "specify deployment to connect to").choices(Object.keys(DEPLOYMENTS))) .option("--url ", "custom deployment url. Do not use with --deployment option") + .option("--simulated", "run the script in simulated mode") .argument("", "Epoch number for which the verification should be done.") .action(run) .parse(process.argv); From c987269315c2f04be44fc9be94bd4e953590838d Mon Sep 17 00:00:00 2001 From: "kacper.golem" Date: Thu, 1 Aug 2024 09:48:10 +0200 Subject: [PATCH 2/2] fix: after cr --- epoch-verifier/src/data/models.ts | 4 ++-- epoch-verifier/src/verifications/utils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/epoch-verifier/src/data/models.ts b/epoch-verifier/src/data/models.ts index 1613501d7..99abb3153 100644 --- a/epoch-verifier/src/data/models.ts +++ b/epoch-verifier/src/data/models.ts @@ -213,10 +213,10 @@ export class FinalizedSimulationImpl implements Deserializable { + this.projectsRewards = input.projectsRewards.map((reward: SimulatedReward) => { const simulatedReward = new SimulatedRewardImpl(); return simulatedReward.from(reward); }); diff --git a/epoch-verifier/src/verifications/utils.ts b/epoch-verifier/src/verifications/utils.ts index d83cd9780..49d23eb54 100644 --- a/epoch-verifier/src/verifications/utils.ts +++ b/epoch-verifier/src/verifications/utils.ts @@ -2,7 +2,7 @@ import { VerificationResult } from "../runner"; type Predicate = (elem: T) => boolean | VerificationResult -export const SCALE_FACTOR = 10n ** 36n; +export const SCALE_FACTOR = 10n ** 18n; function abs(v: bigint): bigint { if (v < BigInt(0)) {