Claim Voting Rewards Safe Batch Generator
Generate Safe Transaction Builder compatible JSON for claiming all voting rewards (internal LP fees and external bribes) on the Lithos protocol.
Prerequisites
- •The
.envfile must containRPC_URLfor Plasma mainnet - •The owner address must be a multisig that owns veNFTs or has claimable rewards
Contract Addresses (Plasma Mainnet)
- •Voter:
0x2AF460a511849A7aA37Ac964074475b0E6249c69 - •VotingEscrow:
0x2Eff716Caa7F9EB441861340998B0952AF056686 - •Chain ID:
9745
Key Concepts
Internal vs External Bribes
- •Internal bribes (LP fees): Trading fees from liquidity provision, claimed via
claimFees() - •External bribes: Third-party incentives for voting, claimed via
claimBribes()
Reward Ownership
Rewards accrue to the owner address, not individual veNFT tokenIds. The owner can claim all rewards in a single batch transaction.
Instructions
Step 1: Get all pools and gauges
# Get number of pools source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "length()(uint256)" --rpc-url $RPC_URL # Get pool at index source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "pools(uint256)(address)" <INDEX> --rpc-url $RPC_URL # Get gauge for pool source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "gauges(address)(address)" <POOL_ADDRESS> --rpc-url $RPC_URL
Step 2: Get bribe contracts for each gauge
# Get internal bribe (LP fees) source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "internal_bribes(address)(address)" <GAUGE_ADDRESS> --rpc-url $RPC_URL # Get external bribe source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "external_bribes(address)(address)" <GAUGE_ADDRESS> --rpc-url $RPC_URL
Step 3: Get reward tokens for each bribe
# Get number of reward tokens source .env && cast call <BRIBE_ADDRESS> "rewardsListLength()(uint256)" --rpc-url $RPC_URL # Get reward token at index source .env && cast call <BRIBE_ADDRESS> "rewardTokens(uint256)(address)" <INDEX> --rpc-url $RPC_URL
Step 4: Check claimable amounts
# Check earned amount for owner source .env && cast call <BRIBE_ADDRESS> "earned(address,address)(uint256)" <OWNER_ADDRESS> <TOKEN_ADDRESS> --rpc-url $RPC_URL # IMPORTANT: Check bribe contract has sufficient balance source .env && cast call <TOKEN_ADDRESS> "balanceOf(address)(uint256)" <BRIBE_ADDRESS> --rpc-url $RPC_URL
CRITICAL: Only include rewards where bribe_balance >= earned_amount. Underfunded bribes will cause the transaction to revert with "ERC20: transfer amount exceeds balance".
Step 5: Generate claim calldata
For internal bribes (LP fees):
cast calldata "claimFees(address[],address[][])" "[<BRIBE1>,<BRIBE2>,...]" "[[<TOKEN1A>,<TOKEN1B>],[<TOKEN2A>],...]"
For external bribes:
cast calldata "claimBribes(address[],address[][])" "[<BRIBE1>,<BRIBE2>,...]" "[[<TOKEN1A>,<TOKEN1B>],[<TOKEN2A>],...]"
Step 6: Create Safe batch JSON
Generate a JSON file with this structure:
{
"version": "1.0",
"chainId": "9745",
"createdAt": <UNIX_TIMESTAMP>,
"meta": {
"name": "Claim All Voting Rewards for <SHORT_ADDRESS>...",
"description": "Claim all internal (LP fees) and external bribes across <NUM_BRIBES> bribe contracts for address <FULL_ADDRESS>",
"txBuilderVersion": "1.16.5"
},
"transactions": [
{
"to": "0x2AF460a511849A7aA37Ac964074475b0E6249c69",
"value": "0",
"data": "<CLAIM_FEES_CALLDATA>",
"contractMethod": {
"inputs": [
{ "internalType": "address[]", "name": "_fees", "type": "address[]" },
{ "internalType": "address[][]", "name": "_tokens", "type": "address[][]" }
],
"name": "claimFees",
"payable": false
},
"contractInputsValues": {
"_fees": "[<BRIBE_ADDRESSES>]",
"_tokens": "[[<TOKEN_ARRAYS>]]"
}
},
{
"to": "0x2AF460a511849A7aA37Ac964074475b0E6249c69",
"value": "0",
"data": "<CLAIM_BRIBES_CALLDATA>",
"contractMethod": {
"inputs": [
{ "internalType": "address[]", "name": "_bribes", "type": "address[]" },
{ "internalType": "address[][]", "name": "_tokens", "type": "address[][]" }
],
"name": "claimBribes",
"payable": false
},
"contractInputsValues": {
"_bribes": "[<BRIBE_ADDRESSES>]",
"_tokens": "[[<TOKEN_ARRAYS>]]"
}
}
]
}
Step 7: Write to file
Save the JSON to safe-txs/safe.claim-bribes-<OWNER_SHORT>.json.
Using the Script
There is an existing script that automates this process:
npx tsx scripts/claimAllBribesForAddress.ts <OWNER_ADDRESS>
This script:
- •Enumerates all 25+ gauges
- •Gets internal and external bribes for each
- •Checks all reward tokens and claimable amounts
- •Verifies bribe contracts have sufficient balance (skips underfunded)
- •Generates Safe batch JSON with
claimFeesandclaimBribestransactions - •Outputs a summary of claimable and skipped rewards
Verification
Simulate the transactions before executing:
# Simulate claimFees source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "<CLAIM_FEES_CALLDATA>" --from <OWNER_ADDRESS> --rpc-url $RPC_URL # Simulate claimBribes source .env && cast call 0x2AF460a511849A7aA37Ac964074475b0E6249c69 "<CLAIM_BRIBES_CALLDATA>" --from <OWNER_ADDRESS> --rpc-url $RPC_URL
Example Usage
User: "Claim all voting rewards for 0x495a98fd059551385Fc9bAbBcFD88878Da3A1b78"
- •Run the script or manually enumerate all gauges
- •Find claimable rewards:
- •vAMM-WXPL/USDT0 (internal): 15,625 WXPL, 3,196 USDT0
- •sAMM-msUSD/USDT0 (external): 3,330 USDT0
- •vAMM-WXPL/LITH (internal): 91 WXPL, 3,719 LITH
- •etc.
- •Skip underfunded bribes (e.g., ELITE bribe with insufficient balance)
- •Generate
safe-txs/safe.claim-bribes-0x495a98.json - •Simulate both transactions
- •Report success with rewards breakdown
Common Token Addresses
| Token | Address | Decimals |
|---|---|---|
| WXPL | 0x6100E367285b01F48D07953803A2d8dCA5D19873 | 18 |
| USDT0 | 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb | 6 |
| LITH | 0xAbB48792A3161E81B47cA084c0b7A22a50324A44 | 18 |
| USDe | 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34 | 18 |
| msUSD | 0x29AD7fE4516909b9e498B5a65339e54791293234 | 18 |
| WETH | 0x9895D81bB462A195b4922ED7De0e3ACD007c32CB | 18 |
| ebUSD | 0xEf7B1A03E0897C33b63159e38d779e3970C0E2fc | 18 |
| ELITE | 0x30C8Cf6B46AA4DF3F9fbC2857aca92F10a6cAd7f | 18 |
Common Mistakes to Avoid
- •
Including underfunded bribes - Always check
balanceOf(bribe)>=earned(owner, token). Underfunded bribes will revert the entire batch. - •
Including tokens with 0 claimable - Only include tokens where
earned() > 0. - •
Using wrong address - Verify the owner address actually owns veNFTs or has earned rewards.
- •
Missing reward tokens - Check ALL reward tokens in each bribe contract, not just the first one.
Notes
- •Rewards accrue to the owner address, not individual tokenIds
- •Internal bribes are LP trading fees (claimFees)
- •External bribes are third-party incentives (claimBribes)
- •Both can be batched into a single Safe transaction
- •Always simulate before executing to catch revert conditions
- •Underfunded bribes are a known issue - skip them and report to protocol team