Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
buildDelegationClaimEntries,
buildCoinbaseClaimEntry,
buildWarehouseWithdrawEntry,
getRecoveryDustThreshold,
type ClaimCartEntry,
} from "@/utils/claimCart"
import type { DelegationBreakdown } from "@/hooks/atp/useAggregatedStakingData"
Expand Down Expand Up @@ -117,7 +118,13 @@ export const ClaimAllRewardsModal = ({
// share in the warehouse. Route it through `replaceTransactionByTx` so
// the fresh entry (wired to the LATEST distribute group) supersedes any
// prior withdraw.
const needsWithdraw = lastDistributeGroup !== null || pendingWarehouseWithdrawal > 0n
// A distribute in this batch always needs a withdraw to follow it. A
// pre-existing warehouse balance only does when it is above the dust
// threshold: every PullSplit leaves 1 wei behind after a distribute, so a
// drained warehouse still reads non-zero and would queue a withdraw that
// burns gas and moves nothing.
const needsWithdraw =
lastDistributeGroup !== null || pendingWarehouseWithdrawal >= getRecoveryDustThreshold(decimals ?? 18)
if (needsWithdraw && warehouseAddress) {
const withdrawEntry = buildWarehouseWithdrawEntry({
warehouseAddress,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Icon } from "@/components/Icon"
import { formatTokenAmountFull } from "@/utils/atpFormatters"
import { getRecoveryDustThreshold } from "@/utils/claimCart"
import type { DelegationBreakdown } from "@/hooks/atp/useAggregatedStakingData"
import type { CoinbaseBreakdown } from "@/hooks/rewards/rewardsTypes"

Expand Down Expand Up @@ -40,11 +41,18 @@ export const ClaimAllRewardsSummary = ({
)
const coinbasesWithRewards = coinbases.filter(c => c.rewards > 0n)

// Every PullSplit keeps 1 wei after a distribute, so a drained warehouse
// still reports a non-zero balance. Treat anything under the shared dust
// threshold as nothing to withdraw, otherwise the summary advertises a
// "ready to withdraw" row that costs gas and moves no tokens.
const warehouseWithdrawal =
pendingWarehouseWithdrawal >= getRecoveryDustThreshold(decimals) ? pendingWarehouseWithdrawal : 0n

// Calculate totals
const totalDelegationRewards = delegationsWithRewards.reduce((sum, d) => sum + d.rewards, 0n)
const totalCoinbaseRewards = coinbasesWithRewards.reduce((sum, c) => sum + c.rewards, 0n)
// Include pending warehouse withdrawal in total
const totalRewards = totalDelegationRewards + totalCoinbaseRewards + pendingWarehouseWithdrawal
const totalRewards = totalDelegationRewards + totalCoinbaseRewards + warehouseWithdrawal

const hasRewards = totalRewards > 0n

Expand Down Expand Up @@ -242,7 +250,7 @@ export const ClaimAllRewardsSummary = ({
)}

{/* Pending Warehouse Withdrawal */}
{pendingWarehouseWithdrawal > 0n && (
{warehouseWithdrawal > 0n && (
<div>
<div className="text-xs text-parchment/40 uppercase tracking-wide mb-3">
Pending Withdrawal
Expand All @@ -256,7 +264,7 @@ export const ClaimAllRewardsSummary = ({
</span>
</div>
<div className="font-mono text-sm font-bold text-chartreuse">
{formatTokenAmountFull(pendingWarehouseWithdrawal, decimals, symbol)}
{formatTokenAmountFull(warehouseWithdrawal, decimals, symbol)}
</div>
</div>
<div className="mt-2 text-xs text-parchment/40">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Icon } from "@/components/Icon"
import {
buildDelegationClaimEntries,
buildWarehouseWithdrawEntry,
getRecoveryDustThreshold,
type ClaimCartEntry,
} from "@/utils/claimCart"
import type { Address } from "viem"
Expand Down Expand Up @@ -64,10 +65,16 @@ export const ClaimDelegationRewardsButton = ({
const currentWarehouseBalance = warehouseBalance ?? 0n
const totalRollupRewards = rollupRewardsByRollup.reduce((sum, r) => sum + r.rewards, 0n)

// A PullSplit permanently holds 1 wei once it has distributed, so a fully
// withdrawn warehouse still reports a balance. Only treat a balance above
// the shared dust threshold as something worth claiming, otherwise the
// button stays enabled forever and every click burns gas for no tokens.
const hasWarehouseBalance = currentWarehouseBalance >= getRecoveryDustThreshold(decimals ?? 18)

const hasRewards =
totalRollupRewards > 0n ||
currentSplitBalance > 0n ||
currentWarehouseBalance > 0n
hasWarehouseBalance

const isReady = !!warehouseAddress && !!tokenAddress && !!beneficiary
const isDisabled = !isReady || !hasRewards
Expand Down Expand Up @@ -99,7 +106,7 @@ export const ClaimDelegationRewardsButton = ({
symbol: symbol ?? "",
splitContractBalance: currentSplitBalance,
})
const withdraw = entries.length > 0 || currentWarehouseBalance > 0n
const withdraw = entries.length > 0 || hasWarehouseBalance
? buildWarehouseWithdrawEntry({
warehouseAddress,
beneficiary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useCoinbaseRewardsAcrossRollups } from "@/hooks/rewards/useCoinbaseRewa
import { useERC20Balance } from "@/hooks/erc20/useERC20Balance"
import { useSplitsWarehouse } from "@/hooks/splits/useSplitsWarehouse"
import { calculateTotalUserShareFromSplitRewards, calculateUserShareFromTakeRate } from "@/utils/rewardCalculations"
import { getRecoveryDustThreshold } from "@/utils/claimCart"
import type { Address } from "viem"

export interface DelegationModalData {
Expand Down Expand Up @@ -76,6 +77,13 @@ export const ClaimDelegationRewardsModal = ({

const isLoadingBalances = isLoadingRollup || isLoadingSplitContract || isLoadingWarehouse

// A PullSplit keeps 1 wei forever once it has distributed, so a fully
// withdrawn warehouse still reports a balance. The figure below stays
// truthful, but only highlight it when there is enough to actually
// withdraw — a highlighted 1 wei reads as "claim me" and cannot be.
const hasWithdrawableWarehouseBalance =
(warehouseBalance ?? 0n) >= getRecoveryDustThreshold(decimals ?? 18)

const handleSuccess = () => {
onSuccess?.()
onClose()
Expand Down Expand Up @@ -285,7 +293,7 @@ export const ClaimDelegationRewardsModal = ({
<span className="inline-flex items-center justify-center w-4 h-4 rounded-full bg-chartreuse/20 border border-chartreuse text-[10px] font-bold text-chartreuse mr-1.5">3</span>
Warehouse (Shared)
</div>
<div className={`font-mono font-bold ${warehouseBalance && warehouseBalance > 0n ? 'text-chartreuse' : 'text-parchment/40'}`}>
<div className={`font-mono font-bold ${hasWithdrawableWarehouseBalance ? 'text-chartreuse' : 'text-parchment/40'}`}>
{decimals && symbol ? formatTokenAmount(warehouseBalance || 0n, decimals, symbol, 2) : '-'}
</div>
</div>
Expand Down
Loading