Skip to content
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 packages/affiliates/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ artifacts
# as a local/regenerable artifact.
gas-rebate/rebates/*.audit.json
gas-rebate/corrections/*.audit.json

# Delegate rebate exclusion list is kept private. The monthly audit records only its SHA-256 and
# size, so payouts stay reproducible without committing the addresses.
gas-rebate/exclusions/
48 changes: 48 additions & 0 deletions packages/affiliates/gas-rebate/VoterGasRebateV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "@nomiclabs/hardhat-ethers";
import { findBlockNumberAtTimestamp, getWeb3 } from "@uma/common";
import { getAddress } from "@uma/contracts-node";
import type { VotingV2 } from "@uma/contracts-node/dist/packages/contracts-node/typechain/core/ethers/VotingV2";
import { createHash } from "crypto";
import fs from "fs";
import hre from "hardhat";
import moment from "moment";
Expand All @@ -24,10 +25,41 @@ const {
MIN_STAKED_TOKENS,
MAX_PRIORITY_FEE_GWEI,
MAX_BLOCK_LOOK_BACK,
COMMIT_LOOKBACK_BLOCKS,
EXCLUSION_LIST_PATH,
IGNORE_EXCLUSION_LIST,
} = process.env;

const DEFAULT_MIN_STAKED_TOKENS = "1000";
const DEFAULT_MAX_PRIORITY_FEE_GWEI = "0.001";
const DEFAULT_COMMIT_LOOKBACK_BLOCKS = 50000;

// Addresses in this list receive no rebate, e.g. delegate voters. The audit
// records only the list's SHA-256 and size so the payout stays reproducible without publishing it.
// Set IGNORE_EXCLUSION_LIST=true to skip it entirely (used for the pre-policy portion of a month).
function loadExclusionList(): { addresses: Set<string>; hash: string | null; size: number } {
if (IGNORE_EXCLUSION_LIST && /^(true|1|yes)$/i.test(IGNORE_EXCLUSION_LIST))
return { addresses: new Set(), hash: null, size: 0 };

const listPath = EXCLUSION_LIST_PATH || path.join(__dirname, "exclusions", "rebate-exclusions.json");
if (!fs.existsSync(listPath)) return { addresses: new Set(), hash: null, size: 0 };

const parsed = JSON.parse(fs.readFileSync(listPath, "utf8"));
const rawAddresses: unknown = Array.isArray(parsed) ? parsed : parsed?.addresses;
if (!Array.isArray(rawAddresses))
throw new Error(`Exclusion list ${listPath} must be a JSON array of addresses or { "addresses": [...] }`);

const addresses = new Set<string>();
for (const entry of rawAddresses) {
if (typeof entry !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(entry.trim()))
throw new Error(`Exclusion list ${listPath} contains an invalid address: ${String(entry)}`);
addresses.add(entry.trim().toLowerCase());
}
const hash = createHash("sha256")
.update(JSON.stringify([...addresses].sort()))
.digest("hex");
return { addresses, hash, size: addresses.size };
}

export async function run(): Promise<void> {
console.log("Running UMA2.0 Gas rebate script! This script assumes you are running it for the previous month🍌.");
Expand Down Expand Up @@ -61,6 +93,8 @@ export async function run(): Promise<void> {
"gwei"
);
const maxBlockLookBack = MAX_BLOCK_LOOK_BACK ? Number(MAX_BLOCK_LOOK_BACK) : 250;
const commitLookbackBlocks = COMMIT_LOOKBACK_BLOCKS ? Number(COMMIT_LOOKBACK_BLOCKS) : DEFAULT_COMMIT_LOOKBACK_BLOCKS;
const exclusionList = loadExclusionList();
const retryConfig = {
retries: MAX_RETRIES ? Number(MAX_RETRIES) : 10,
delay: RETRY_DELAY ? Number(RETRY_DELAY) : 1000,
Expand All @@ -76,7 +110,10 @@ export async function run(): Promise<void> {
minStakedTokens: ethers.utils.formatEther(minTokens),
maxPriorityFeeGwei: maxPriorityFee ? ethers.utils.formatUnits(maxPriorityFee, "gwei") : null,
maxBlockLookBack,
commitLookbackBlocks,
transactionConcurrency,
exclusionListSize: exclusionList.size,
exclusionListHash: exclusionList.hash,
});

// Fetch all commit and reveal events.
Expand All @@ -90,6 +127,8 @@ export async function run(): Promise<void> {
maxBlockLookBack,
transactionConcurrency,
maxPriorityFee,
commitLookbackBlocks,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record commit lookback in the audit config

When COMMIT_LOOKBACK_BLOCKS is overridden, the payout can change because this value is now passed into the calculator, but the monthly audit's effectiveConfig still omits it. That leaves committed rebate artifacts (and later correction/audit reruns if the default changes) without the policy parameter needed to reproduce why boundary commits were or were not recovered; include the resolved lookback value in MonthlyAuditReportConfig/Markdown alongside the other effective parameters.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 100fe2ccommitLookbackBlocks is now recorded in the monthly audit effectiveConfig and rendered in the Markdown (Commit lookback blocks: 50000), alongside the other effective parameters.

excludedAddresses: exclusionList.addresses,
retryConfig,
});

Expand All @@ -110,6 +149,12 @@ export async function run(): Promise<void> {
console.log("No priority fee cap applied");
}

console.log(
`Exclusion list: ${exclusionList.size} addresses (sha256 ${exclusionList.hash ?? "none"}); excluded ` +
`${rebateComputation.excludedVoterCount} voters removing ` +
`${ethers.utils.formatEther(rebateComputation.excludedRebateWei)} ETH`
);

// Create a formatted output that is not bignumbers.
const shareholderPayout: { [key: string]: number } = {};
for (const [key, value] of Object.entries(rebateComputation.shareholderPayoutWei))
Expand Down Expand Up @@ -148,12 +193,15 @@ export async function run(): Promise<void> {
maxPriorityFeeGwei: maxPriorityFee ? ethers.utils.formatUnits(maxPriorityFee, "gwei") : null,
maxPriorityFeeWei: maxPriorityFee ? maxPriorityFee.toString() : null,
maxBlockLookBack,
commitLookbackBlocks,
transactionConcurrency,
maxRetries: retryConfig.retries,
retryDelay: retryConfig.delay,
overrideFromBlockConfigured: Boolean(OVERRIDE_FROM_BLOCK),
overrideToBlockConfigured: Boolean(OVERRIDE_TO_BLOCK),
customNodeUrlConfigured: Boolean(process.env.CUSTOM_NODE_URL),
exclusionListHash: exclusionList.hash,
exclusionListSize: exclusionList.size,
},
});
console.log("Monthly audit JSON written to", auditReports.jsonPath);
Expand Down
50 changes: 50 additions & 0 deletions packages/affiliates/gas-rebate/rebates/Rebate_69.audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# VotingV2 Monthly Gas Rebate Audit - Rebate 69

Generated at: 2026-07-14T11:01:34.442Z
Output rebate file: `/home/ubuntu/ghq/github.com/UMAprotocol/protocol/packages/affiliates/gas-rebate/rebates/Rebate_69.json`
VotingV2 contract: `0x004395edb43EFca9885CEdad51EC9fAf93Bd34ac`
Block range: 25218798-25326350

## Effective Config

- Minimum staked tokens: 1000.0 UMA (1000000000000000000000 wei)
- Max priority fee: 0.001 gwei
- Max block lookback: 250
- Commit lookback blocks: 50000
- Transaction concurrency: 100
- Max retries: 10
- Retry delay: 1000 ms
- Override from block configured: true
- Override to block configured: true
- Custom node URL configured: true
- Exclusion list size: 0
- Exclusion list SHA-256: none

## Summary

- Commit events: 86070
- Reveal events: 104284
- Eligible reveal events: 94764
- Matched commit events: 76499
- Transactions: 8857
- Voters: 579
- Excluded voters: 0
- Total payout: 3664251413949599720 wei (3.66425141394959972 ETH)
- Excluded rebate: 0 wei (0.0 ETH)

## Event Collection

- Validation passed: true
- Chunk size: 250
- Ranges queried: 631
- Query attempts: 1262
- Retry count: 0
- Split count: 0
- Validation failures: 0
- Provider errors: 0
- Receipt validations: 14533
- Boundary commits recovered: 18265

## Anomalies

- None
Loading