Last active
August 29, 2025 11:08
-
-
Save tsudmi/a1e28b3a542c16c8a71859cbde8d049e to your computer and use it in GitHub Desktop.
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
| import {ethers} from "ethers"; | |
| const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"); | |
| const userAddress = "0x2365887bBdb7fF611F54b380573a5055170fAE7D"; | |
| const osTokenEscrow = "0x09e84205DF7c68907e619D07aFD90143c5763605"; | |
| const contractAddresses = { | |
| leverageStratV1: "0x48cD14FDB8e72A03C8D952af081DBB127D6281fc", | |
| leverageStratV2: "0x7575BC9E5168f27B97F9028905A2Adf91d2fF53d", | |
| keeper: "0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5", | |
| boostHelpers: "0xdD3Cee7ECa7308f1CECf86A46A00698A5C81FD93", | |
| }; | |
| const contractAbis = { | |
| leverageStrat: [ | |
| { | |
| anonymous: false, | |
| inputs: [ | |
| {indexed: true, internalType: "address", name: "vault", type: "address"}, | |
| {indexed: true, internalType: "address", name: "user", type: "address"}, | |
| {indexed: false, internalType: "uint256", name: "osTokenShares", type: "uint256"}, | |
| {indexed: false, internalType: "uint256", name: "leverageOsTokenShares", type: "uint256"}, | |
| {indexed: false, internalType: "address", name: "referrer", type: "address"} | |
| ], | |
| name: "Deposited", | |
| type: "event" | |
| }, | |
| { | |
| inputs: [ | |
| {internalType: "address", name: "vault", type: "address"}, | |
| {internalType: "address", name: "user", type: "address"} | |
| ], | |
| name: "getStrategyProxy", | |
| outputs: [{internalType: "address", name: "proxy", type: "address"}], | |
| stateMutability: "view", | |
| type: "function" | |
| }, | |
| { | |
| inputs: [{internalType: "address", name: "proxy", type: "address"}], | |
| name: "isStrategyProxyExiting", | |
| outputs: [{internalType: "bool", name: "isExiting", type: "bool"}], | |
| stateMutability: "view", | |
| type: "function" | |
| } | |
| ], | |
| keeper: [ | |
| { | |
| anonymous: false, | |
| inputs: [ | |
| {indexed: true, internalType: "address", name: "caller", type: "address"}, | |
| {indexed: true, internalType: "bytes32", name: "rewardsRoot", type: "bytes32"}, | |
| {indexed: false, internalType: "uint256", name: "avgRewardPerSecond", type: "uint256"}, | |
| {indexed: false, internalType: "uint64", name: "updateTimestamp", type: "uint64"}, | |
| {indexed: false, internalType: "uint64", name: "nonce", type: "uint64"}, | |
| {indexed: false, internalType: "string", name: "rewardsIpfsHash", type: "string"} | |
| ], | |
| name: "RewardsUpdated", | |
| type: "event" | |
| } | |
| ], | |
| vault: [ | |
| { | |
| anonymous: false, | |
| inputs: [ | |
| {indexed: true, internalType: "address", name: "owner", type: "address"}, | |
| {indexed: true, internalType: "address", name: "receiver", type: "address"}, | |
| {indexed: false, internalType: "uint256", name: "positionTicket", type: "uint256"}, | |
| {indexed: false, internalType: "uint256", name: "shares", type: "uint256"} | |
| ], | |
| name: "ExitQueueEntered", | |
| type: "event" | |
| } | |
| ], | |
| boostHelpers: [ | |
| { | |
| inputs: [ | |
| {internalType: "address", name: "user", type: "address"}, | |
| {internalType: "address", name: "vault", type: "address"}, | |
| { | |
| components: [ | |
| {internalType: "bytes32", name: "rewardsRoot", type: "bytes32"}, | |
| {internalType: "int160", name: "reward", type: "int160"}, | |
| {internalType: "uint160", name: "unlockedMevReward", type: "uint160"}, | |
| {internalType: "bytes32[]", name: "proof", type: "bytes32[]"} | |
| ], | |
| internalType: "struct IKeeperRewards.HarvestParams", | |
| name: "harvestParams", | |
| type: "tuple" | |
| }, | |
| { | |
| components: [ | |
| {internalType: "uint256", name: "positionTicket", type: "uint256"}, | |
| {internalType: "uint256", name: "timestamp", type: "uint256"} | |
| ], | |
| internalType: "struct IBoostHelpers.ExitRequest", | |
| name: "exitRequest", | |
| type: "tuple" | |
| } | |
| ], | |
| name: "getBoostOsTokenShares", | |
| outputs: [{internalType: "uint256", name: "", type: "uint256"}], | |
| stateMutability: "nonpayable", | |
| type: "function" | |
| }, | |
| { | |
| inputs: [{internalType: "address", name: "proxy", type: "address"}], | |
| name: "getProxyLeverageStrategy", | |
| outputs: [{internalType: "contract ILeverageStrategy", name: "", type: "address"}], | |
| stateMutability: "view", | |
| type: "function" | |
| }, | |
| { | |
| inputs: [ | |
| {internalType: "address", name: "vault", type: "address"}, | |
| {internalType: "address", name: "user", type: "address"} | |
| ], | |
| name: "getStrategyProxy", | |
| outputs: [{internalType: "address", name: "proxy", type: "address"}], | |
| stateMutability: "view", | |
| type: "function" | |
| } | |
| ] | |
| }; | |
| const contracts = { | |
| leverageStratV1: new ethers.Contract(contractAddresses.leverageStratV1, contractAbis.leverageStrat, provider), | |
| leverageStratV2: new ethers.Contract(contractAddresses.leverageStratV2, contractAbis.leverageStrat, provider), | |
| keeper: new ethers.Contract(contractAddresses.keeper, contractAbis.keeper, provider), | |
| boostHelpers: new ethers.Contract(contractAddresses.boostHelpers, contractAbis.boostHelpers, provider) | |
| }; | |
| async function getVaultHarvestParams(vaultAddress, allRewards, rewardsRoot) { | |
| const vault = allRewards.vaults.find(v => v.vault === vaultAddress); | |
| return vault ? { | |
| rewardsRoot, | |
| reward: BigInt(vault.consensus_reward) + BigInt(vault.unlocked_mev_reward) + BigInt(vault.locked_mev_reward), | |
| unlockedMevReward: vault.unlocked_mev_reward, | |
| proof: vault.proof | |
| } : {rewardsRoot: "0x", reward: "0", unlockedMevReward: "0", proof: []}; | |
| } | |
| async function fetchDepositedVaults() { | |
| const eventsV1 = await contracts.leverageStratV1.queryFilter( | |
| contracts.leverageStratV1.filters.Deposited(null, userAddress), 21288813, "latest" | |
| ); | |
| const eventsV2 = await contracts.leverageStratV2.queryFilter( | |
| contracts.leverageStratV2.filters.Deposited(null, userAddress), 23242393, "latest" | |
| ); | |
| return [...new Set(eventsV1.concat(eventsV2).map(event => event.args.vault))]; | |
| } | |
| async function getLastRewards() { | |
| const events = await contracts.keeper.queryFilter(contracts.keeper.filters.RewardsUpdated(), 0, "latest"); | |
| if (!events.length) return [null, null]; | |
| const lastEvent = events.pop(); | |
| const jsonText = await fetch("https://ipfs.io/ipfs/" + lastEvent.args.rewardsIpfsHash).then(res => res.text()); | |
| return [lastEvent.args.rewardsRoot, JSON.parse(jsonText.replace(/(\d{16,})/g, '"$1"'))]; | |
| } | |
| async function getLastVaultExitRequest(vaultAddress, proxy, leverageStrategyAddress) { | |
| const leverageStrat = new ethers.Contract(leverageStrategyAddress, contractAbis.leverageStrat, provider); | |
| if (!(await leverageStrat.isStrategyProxyExiting(proxy))) { | |
| return {positionTicket: 0, timestamp: 0}; | |
| } | |
| const vault = new ethers.Contract(vaultAddress, contractAbis.vault, provider); | |
| const events = await vault.queryFilter(vault.filters.ExitQueueEntered(proxy, osTokenEscrow)); | |
| if (!events.length) throw new Error("No exit request found"); | |
| const lastEvent = events.pop(); | |
| return { | |
| positionTicket: lastEvent.args.positionTicket, | |
| timestamp: (await provider.getBlock(lastEvent.blockNumber)).timestamp | |
| }; | |
| } | |
| async function main() { | |
| const vaults = await fetchDepositedVaults(); | |
| if (!vaults.length) return; | |
| const [rewardsRoot, allRewards] = await getLastRewards(); | |
| for (const vaultAddress of vaults) { | |
| const proxy = await contracts.boostHelpers.getStrategyProxy(vaultAddress, userAddress); | |
| const leverageStrategyAddress = await contracts.boostHelpers.getProxyLeverageStrategy(proxy); | |
| const vaultHarvestParams = await getVaultHarvestParams(vaultAddress, allRewards, rewardsRoot); | |
| const exitRequest = await getLastVaultExitRequest(vaultAddress, proxy, leverageStrategyAddress); | |
| const boostOsTokenShares = await contracts.boostHelpers.getBoostOsTokenShares.staticCall(userAddress, vaultAddress, vaultHarvestParams, exitRequest); | |
| console.log(`Vault: ${vaultAddress}, User: ${userAddress} Boost Shares: ${ethers.formatEther(boostOsTokenShares)}`); | |
| } | |
| } | |
| main().catch(console.error); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment