-
Notifications
You must be signed in to change notification settings - Fork 201
feat[Gas rebate V2]: cap refunded priority fees, add block lookback config, and document usage #4920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
md0x
merged 3 commits into
master
from
pablo/uma-2991-gas-rebates-refine-gas-rebate-script2
Jan 19, 2026
Merged
feat[Gas rebate V2]: cap refunded priority fees, add block lookback config, and document usage #4920
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # UMA Voter Gas Rebate Scripts | ||
|
|
||
| This directory contains scripts for calculating gas rebates for UMA protocol voters. | ||
|
|
||
| ## VoterGasRebateV2.ts | ||
|
|
||
| The main script for calculating gas rebates for UMA 2.0 voters. It finds all `VoteCommitted` and `VoteRevealed` events in a specified time range and calculates the gas used for each event. The script aggregates gas rebates by voter and saves the results to a JSON file. | ||
|
|
||
| ### How It Works | ||
|
|
||
| 1. Determines the previous month's date range (designed to run monthly) | ||
| 2. Fetches all `VoteCommitted` and `VoteRevealed` events from the VotingV2 contract | ||
| 3. Filters out voters with less than the minimum staked tokens | ||
| 4. Deduplicates commit events (only the first commit per voter per round is refunded) | ||
| 5. Matches commit events with corresponding reveal events | ||
| 6. Calculates gas rebates (with optional priority fee cap) | ||
| 7. Saves results to `rebates/Rebate_<N>.json` | ||
|
|
||
| ### Usage | ||
|
|
||
| ```bash | ||
| # Run from the affiliates package directory | ||
| cd packages/affiliates | ||
|
|
||
| # Basic usage (uses defaults, calculates for previous month) | ||
| yarn hardhat run gas-rebate/VoterGasRebateV2.ts --network mainnet | ||
|
|
||
| # With custom configuration | ||
| OVERRIDE_FROM_BLOCK=18000000 \ | ||
| OVERRIDE_TO_BLOCK=18500000 \ | ||
| MIN_STAKED_TOKENS=1000 \ | ||
| yarn hardhat run gas-rebate/VoterGasRebateV2.ts --network mainnet | ||
|
|
||
| # With priority fee cap (optional) | ||
| MAX_PRIORITY_FEE_GWEI=0.001 \ | ||
| yarn hardhat run gas-rebate/VoterGasRebateV2.ts --network mainnet | ||
| ``` | ||
|
|
||
| ### Environment Variables | ||
|
|
||
| | Variable | Description | Default | | ||
| | ------------------------- | ------------------------------------------------------------------------ | ----------------------------------------- | | ||
| | `OVERRIDE_FROM_BLOCK` | Start block number (overrides automatic date-based calculation) | Auto-calculated from previous month start | | ||
| | `OVERRIDE_TO_BLOCK` | End block number (overrides automatic date-based calculation) | Auto-calculated from previous month end | | ||
| | `MIN_STAKED_TOKENS` | Minimum UMA tokens staked to be eligible for rebate | `500` | | ||
| | `MAX_PRIORITY_FEE_GWEI` | Maximum priority fee to refund (in gwei). If not set, no cap is applied. | None (no cap) | | ||
| | `MAX_BLOCK_LOOK_BACK` | Maximum block range for paginated event queries | `20000` | | ||
| | `TRANSACTION_CONCURRENCY` | Number of concurrent RPC requests for fetching transactions/blocks | `50` | | ||
| | `MAX_RETRIES` | Maximum retry attempts for failed RPC calls | `10` | | ||
| | `RETRY_DELAY` | Delay between retries in milliseconds | `1000` | | ||
|
|
||
| ### Output Format | ||
|
|
||
| The script outputs a JSON file to `rebates/Rebate_<N>.json` with the following structure: | ||
|
|
||
| ```json | ||
| { | ||
| "votingContractAddress": "0x004395edb43EFca9885CEdad51EC9fAf93Bd34ac", | ||
| "rebate": 63, | ||
| "fromBlock": 23914921, | ||
| "toBlock": 24136052, | ||
| "countVoters": 813, | ||
| "totalRebateAmount": 3.733496328767278, | ||
| "shareholderPayout": { | ||
| "0x156527BC2e57610c23Ac795A1252cAc56453e320": 0.01473407276015984, | ||
| "0x226DAce98e689118D9199246f8DfBc9115d8B034": 0.003304298094274105 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| | Field | Description | | ||
| | ----------------------- | ------------------------------------------------------- | | ||
| | `votingContractAddress` | Address of the VotingV2 contract | | ||
| | `rebate` | Sequential rebate number | | ||
| | `fromBlock` | Starting block of the rebate period | | ||
| | `toBlock` | Ending block of the rebate period | | ||
| | `countVoters` | Number of voters receiving rebates | | ||
| | `totalRebateAmount` | Total ETH amount to be rebated | | ||
| | `shareholderPayout` | Map of voter addresses to their rebate amounts (in ETH) | | ||
|
|
||
| ### Priority Fee Capping (Optional) | ||
|
|
||
| By default, the script rebates the full gas cost including any priority fee. You can optionally cap the priority fee (tip) portion to prevent rebating excessive tips by setting `MAX_PRIORITY_FEE_GWEI`. | ||
|
|
||
| For example, with `MAX_PRIORITY_FEE_GWEI=0.001`: | ||
|
|
||
| - If a voter paid a 0.0005 gwei priority fee, they get rebated the full 0.0005 gwei | ||
| - If a voter paid a 0.002 gwei priority fee, they only get rebated 0.001 gwei | ||
|
|
||
| The base fee is always fully rebated. Enabling a cap can encourage voters to use reasonable gas settings while still covering network costs. | ||
|
|
||
| ## Legacy Scripts | ||
|
|
||
| - **VoterGasRebate.js** - Original gas rebate script for UMA 1.0 | ||
| - **FindBlockAtTimeStamp.js** - Utility to find block numbers at specific timestamps | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,8 @@ const { | |
| MAX_RETRIES, | ||
| RETRY_DELAY, | ||
| MIN_STAKED_TOKENS, | ||
| MAX_PRIORITY_FEE_GWEI, | ||
| MAX_BLOCK_LOOK_BACK, | ||
| } = process.env; | ||
|
|
||
| async function retryAsyncOperation<T>( | ||
|
|
@@ -86,10 +88,11 @@ export async function run(): Promise<void> { | |
| // Fetch all commit and reveal events. | ||
| const voting = await hre.ethers.getContractAt("VotingV2", await getAddress("VotingV2", 1)); | ||
|
|
||
| const maxBlockLookBack = MAX_BLOCK_LOOK_BACK ? Number(MAX_BLOCK_LOOK_BACK) : 20000; | ||
| const searchConfig = { | ||
| fromBlock, | ||
| toBlock, | ||
| maxBlockLookBack: 20000, | ||
| maxBlockLookBack, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional config so we can lower lookback when getting this errors: |
||
| }; | ||
|
|
||
| // Find resolved events | ||
|
|
@@ -169,11 +172,51 @@ export async function run(): Promise<void> { | |
| ` for a total of ${transactionsToRefund.length} transactions` | ||
| ); | ||
|
|
||
| // Max priority fee to refund (optional - if not set, no cap is applied) | ||
| const maxPriorityFee = MAX_PRIORITY_FEE_GWEI ? ethers.utils.parseUnits(MAX_PRIORITY_FEE_GWEI, "gwei") : null; | ||
| if (maxPriorityFee) { | ||
| console.log("Max priority fee to refund:", ethers.utils.formatUnits(maxPriorityFee, "gwei"), "gwei"); | ||
| } else { | ||
| console.log("No priority fee cap applied"); | ||
| } | ||
|
|
||
| // Get unique block numbers from transactions to fetch block data | ||
| const uniqueBlockNumbers = [...new Set(transactionsToRefund.map((tx) => tx.blockNumber))]; | ||
| console.log(`Fetching base fee data for ${uniqueBlockNumbers.length} unique blocks...`); | ||
|
|
||
| // Fetch block data in parallel to get base fees | ||
| const blockDataMap = new Map<number, BigNumber>(); | ||
| await Bluebird.map( | ||
| uniqueBlockNumbers, | ||
| async (blockNumber) => { | ||
| const block = await retryAsyncOperation(async () => await voting.provider.getBlock(blockNumber)); | ||
| if (block.baseFeePerGas) { | ||
| blockDataMap.set(blockNumber, block.baseFeePerGas); | ||
| } | ||
| }, | ||
| { concurrency: transactionConcurrency } | ||
| ); | ||
|
|
||
| const shareholderPayoutBN: { [address: string]: BigNumber } = {}; | ||
| // Now, traverse all transactions and calculate the rebate for each. | ||
| for (const transaction of transactionsToRefund) { | ||
| // Eth used is the gas used * the gas price. | ||
| const resultantRebate = transaction.gasUsed.mul(transaction.effectiveGasPrice); | ||
| const baseFee = blockDataMap.get(transaction.blockNumber); | ||
| let effectiveGasPriceForRebate: BigNumber; | ||
|
|
||
| if (baseFee) { | ||
| // Calculate the actual priority fee paid | ||
| const actualPriorityFee = transaction.effectiveGasPrice.sub(baseFee); | ||
| // Cap the priority fee at maxPriorityFee if set | ||
| const cappedPriorityFee = | ||
| maxPriorityFee && actualPriorityFee.gt(maxPriorityFee) ? maxPriorityFee : actualPriorityFee; | ||
| // Rebate = gasUsed * (baseFee + cappedPriorityFee) | ||
| effectiveGasPriceForRebate = baseFee.add(cappedPriorityFee); | ||
| } else { | ||
| // Fallback for pre-EIP-1559 transactions (shouldn't happen on mainnet post-London) | ||
| effectiveGasPriceForRebate = transaction.effectiveGasPrice; | ||
| } | ||
|
|
||
| const resultantRebate = transaction.gasUsed.mul(effectiveGasPriceForRebate); | ||
| // Save the output to the shareholderPayout object. Append to existing value if it exists. | ||
| if (!shareholderPayoutBN[transaction.from]) shareholderPayoutBN[transaction.from] = BigNumber.from(0); | ||
| shareholderPayoutBN[transaction.from] = shareholderPayoutBN[transaction.from].add(resultantRebate); | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we also update the fallback value from 500 to 1000 in the script?