From 18a1d28e22f18c3561dc000cae8dec13afb17249 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 30 Oct 2024 17:41:34 +0900 Subject: [PATCH 1/2] fix: self transfer edge case --- packages/sdk/src/rewards/rewards.ts | 297 +++++++++++++++------------- 1 file changed, 155 insertions(+), 142 deletions(-) diff --git a/packages/sdk/src/rewards/rewards.ts b/packages/sdk/src/rewards/rewards.ts index 83baeda..4650baa 100644 --- a/packages/sdk/src/rewards/rewards.ts +++ b/packages/sdk/src/rewards/rewards.ts @@ -192,58 +192,65 @@ export class LidoSDKRewards extends LidoSDKModule { let totalRewards = 0n; let shareRate = baseShareRate; let prevSharesBalance = baseBalanceShares; - let rewards: Reward[] = events.map((event) => { - if (event.eventName === 'TransferShares') { - const { from, to, sharesValue } = event.args; - let type: Reward['type'], - changeShares: Reward['changeShares'], - balanceShares: Reward['balanceShares']; - - if (isAddressEqual(to, address)) { - type = isAddressEqual(from, zeroAddress) ? 'submit' : 'transfer_in'; - balanceShares = prevSharesBalance + sharesValue; - changeShares = sharesValue; - } else { - type = isAddressEqual(to, withdrawalQueueAddress) - ? 'withdrawal' - : 'transfer_out'; - balanceShares = prevSharesBalance - sharesValue; - changeShares = -sharesValue; + let rewards = events + .map((event) => { + if (event.eventName === 'TransferShares') { + const { from, to, sharesValue } = event.args; + + if (isAddressEqual(to, from)) { + return null; + } + + let type: Reward['type'], + changeShares: Reward['changeShares'], + balanceShares: Reward['balanceShares']; + + if (isAddressEqual(to, address)) { + type = isAddressEqual(from, zeroAddress) ? 'submit' : 'transfer_in'; + balanceShares = prevSharesBalance + sharesValue; + changeShares = sharesValue; + } else { + type = isAddressEqual(to, withdrawalQueueAddress) + ? 'withdrawal' + : 'transfer_out'; + balanceShares = prevSharesBalance - sharesValue; + changeShares = -sharesValue; + } + + prevSharesBalance = balanceShares; + return { + type, + balanceShares, + changeShares, + change: getCurrentStethFromShares(changeShares), + balance: getCurrentStethFromShares(balanceShares), + shareRate, + originalEvent: event, + }; } - - prevSharesBalance = balanceShares; - return { - type, - balanceShares, - changeShares, - change: getCurrentStethFromShares(changeShares), - balance: getCurrentStethFromShares(balanceShares), - shareRate, - originalEvent: event, - }; - } - if (event.eventName === 'TokenRebased') { - const { postTotalEther, postTotalShares } = event.args; - const oldBalance = getCurrentStethFromShares(prevSharesBalance); - currentTotalEther = postTotalEther; - currentTotalShares = postTotalShares; - const newBalance = getCurrentStethFromShares(prevSharesBalance); - shareRate = getCurrentShareRate(); - const change = newBalance - oldBalance; - totalRewards += change; - return { - type: 'rebase', - change, - apr: LidoSDKApr.calculateAprFromRebaseEvent(event.args), - changeShares: 0n, - balance: newBalance, - balanceShares: prevSharesBalance, - shareRate, - originalEvent: event, - }; - } - invariant(false, 'Impossible event'); - }); + if (event.eventName === 'TokenRebased') { + const { postTotalEther, postTotalShares } = event.args; + const oldBalance = getCurrentStethFromShares(prevSharesBalance); + currentTotalEther = postTotalEther; + currentTotalShares = postTotalShares; + const newBalance = getCurrentStethFromShares(prevSharesBalance); + shareRate = getCurrentShareRate(); + const change = newBalance - oldBalance; + totalRewards += change; + return { + type: 'rebase', + change, + apr: LidoSDKApr.calculateAprFromRebaseEvent(event.args), + changeShares: 0n, + balance: newBalance, + balanceShares: prevSharesBalance, + shareRate, + originalEvent: event, + }; + } + invariant(false, 'Impossible event'); + }) + .filter((event) => !!event) as Reward[]; if (includeOnlyRebases) { rewards = rewards.filter((r) => r.type === 'rebase'); @@ -380,101 +387,107 @@ export class LidoSDKRewards extends LidoSDKModule { const baseBalanceShares = prevBalanceShares; let totalRewards = 0n; - let rewards: Reward[] = events.map((event) => { - // it's a transfer - if ('value' in event) { - const { - from, - to, - shares, - sharesAfterIncrease, - value, - balanceAfterDecrease, - balanceAfterIncrease, - sharesAfterDecrease, - totalPooledEther, - totalShares, - } = event; - let type: Reward['type'], - changeShares: Reward['changeShares'], - balanceShares: Reward['balanceShares'], - change: Reward['change'], - balance: Reward['balance']; - - if (isAddressEqual(to as Address, address)) { - type = isAddressEqual(from as Address, zeroAddress) - ? 'submit' - : 'transfer_in'; - changeShares = BigInt(shares); - balanceShares = BigInt(sharesAfterIncrease); - change = BigInt(value); - balance = BigInt(balanceAfterIncrease); - } else { - type = isAddressEqual(to as Address, withdrawalQueueAddress) - ? 'withdrawal' - : 'transfer_out'; - balance = BigInt(balanceAfterDecrease); - change = -BigInt(value); - changeShares = -BigInt(shares); - balanceShares = BigInt(sharesAfterDecrease); + let rewards = events + .map((event) => { + // it's a transfer + if ('value' in event) { + const { + from, + to, + shares, + sharesAfterIncrease, + value, + balanceAfterDecrease, + balanceAfterIncrease, + sharesAfterDecrease, + totalPooledEther, + totalShares, + } = event; + let type: Reward['type'], + changeShares: Reward['changeShares'], + balanceShares: Reward['balanceShares'], + change: Reward['change'], + balance: Reward['balance']; + + if (isAddressEqual(to as Address, from as Address)) { + return null; + } + + if (isAddressEqual(to as Address, address)) { + type = isAddressEqual(from as Address, zeroAddress) + ? 'submit' + : 'transfer_in'; + changeShares = BigInt(shares); + balanceShares = BigInt(sharesAfterIncrease); + change = BigInt(value); + balance = BigInt(balanceAfterIncrease); + } else { + type = isAddressEqual(to as Address, withdrawalQueueAddress) + ? 'withdrawal' + : 'transfer_out'; + balance = BigInt(balanceAfterDecrease); + change = -BigInt(value); + changeShares = -BigInt(shares); + balanceShares = BigInt(sharesAfterDecrease); + } + + const shareRate = calcShareRate( + BigInt(totalPooledEther), + BigInt(totalShares), + LidoSDKRewards.PRECISION, + ); + prevBalance = balance; + prevBalanceShares = balanceShares; + + return { + type, + balanceShares, + changeShares, + change, + balance, + shareRate, + originalEvent: event, + }; } - - const shareRate = calcShareRate( - BigInt(totalPooledEther), - BigInt(totalShares), - LidoSDKRewards.PRECISION, - ); - prevBalance = balance; - prevBalanceShares = balanceShares; - - return { - type, - balanceShares, - changeShares, - change, - balance, - shareRate, - originalEvent: event, - }; - } - // it's a rebase - if ('apr' in event) { - const { - totalPooledEtherAfter, - totalSharesAfter, - apr: eventApr, - } = event; - - const totalEther = BigInt(totalPooledEtherAfter); - const totalShares = BigInt(totalSharesAfter); - - const newBalance = sharesToSteth( - prevBalanceShares, - totalEther, - totalShares, - LidoSDKRewards.PRECISION, - ); - const change = newBalance - prevBalance; - totalRewards += change; - prevBalance = newBalance; - - return { - type: 'rebase', - change, - apr: Number(eventApr), - changeShares: 0n, - balance: newBalance, - balanceShares: prevBalanceShares, - shareRate: calcShareRate( + // it's a rebase + if ('apr' in event) { + const { + totalPooledEtherAfter, + totalSharesAfter, + apr: eventApr, + } = event; + + const totalEther = BigInt(totalPooledEtherAfter); + const totalShares = BigInt(totalSharesAfter); + + const newBalance = sharesToSteth( + prevBalanceShares, totalEther, totalShares, LidoSDKRewards.PRECISION, - ), - originalEvent: event, - }; - } - invariant(false, 'impossible event'); - }); + ); + const change = newBalance - prevBalance; + totalRewards += change; + prevBalance = newBalance; + + return { + type: 'rebase', + change, + apr: Number(eventApr), + changeShares: 0n, + balance: newBalance, + balanceShares: prevBalanceShares, + shareRate: calcShareRate( + totalEther, + totalShares, + LidoSDKRewards.PRECISION, + ), + originalEvent: event, + }; + } + invariant(false, 'impossible event'); + }) + .filter((events) => !!events) as Reward[]; if (includeOnlyRebases) { rewards = rewards.filter((r) => r.type === 'rebase'); From b3e311dfd3b416466c6863c88722d5b0853d7853 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 30 Oct 2024 17:43:09 +0900 Subject: [PATCH 2/2] chore: changelog --- packages/sdk/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 0762a2e..aab5bdf 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,3 +1,11 @@ +# 4.0.1 + +## SDK + +### Fixed + +- `LidoSDKRewards` now filter outs edgecases with self-transfers + # 4.0.0 ## Breaking change