From a932710c49284c6e6c18385800acdf9f387dc8f2 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Thu, 7 Mar 2024 21:07:08 +0800 Subject: [PATCH] feat: improve claiming rewards flow --- src/constants.ts | 2 +- .../staking/components/delegation-details.tsx | 12 +++++++----- src/features/staking/components/modals/rewards.tsx | 9 +++++++++ src/features/staking/components/staking-overview.tsx | 4 ++-- .../staking/components/validator-delegation.tsx | 8 +++----- src/features/staking/context/selectors.ts | 11 +++++++++++ src/features/staking/lib/core/tx.ts | 8 ++++++-- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 48895ae..4d02e27 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,4 +25,4 @@ export const defaultAvatar = `${basePath}/default-avatar.svg`; export const unbondingDays = isTestnet ? 3 : 21; // Arbitrary value to avoid using a bigger fee than the actual reward -export const minClaimableXion = 0.0001; +export const minClaimableXion = 0.00001; diff --git a/src/features/staking/components/delegation-details.tsx b/src/features/staking/components/delegation-details.tsx index d87c10f..0126446 100644 --- a/src/features/staking/components/delegation-details.tsx +++ b/src/features/staking/components/delegation-details.tsx @@ -1,3 +1,4 @@ +import BigNumber from "bignumber.js"; import type { Validator } from "cosmjs-types/cosmos/staking/v1beta1/staking"; import { memo, useEffect, useState } from "react"; @@ -21,6 +22,7 @@ import { import type { StakingContextType, StakingState } from "../context/state"; import { useValidatorLogo } from "../hooks"; import { coinIsPositive } from "../lib/core/coins"; +import { getCanClaimRewards } from "../lib/core/tx"; import { formatCoin, formatCommission, @@ -156,7 +158,7 @@ const DelegationRowBase = ({
{ if (!validator) return; @@ -319,16 +321,16 @@ const DelegationDetails = () => { case "staked-asc": case "staked-desc": return sortUtil( - a.balance.amount, - b.balance.amount, + new BigNumber(a.balance.amount), + new BigNumber(b.balance.amount), delegationsSortMethod === "staked-asc", ); case "rewards-asc": case "rewards-desc": return sortUtil( - a.rewards.amount, - b.rewards.amount, + new BigNumber(a.rewards.amount), + new BigNumber(b.rewards.amount), delegationsSortMethod === "rewards-asc", ); diff --git a/src/features/staking/components/modals/rewards.tsx b/src/features/staking/components/modals/rewards.tsx index 56b7bb3..7f7537f 100644 --- a/src/features/staking/components/modals/rewards.tsx +++ b/src/features/staking/components/modals/rewards.tsx @@ -1,6 +1,8 @@ +import BigNumber from "bignumber.js"; import { memo, useEffect, useRef, useState } from "react"; import { toast } from "react-toastify"; +import { minClaimableXion } from "@/constants"; import { Button, HeroText } from "@/features/core/components/base"; import CommonModal, { ModalDescription, @@ -9,6 +11,7 @@ import CommonModal, { import { fetchUserDataAction } from "../../context/actions"; import { useStaking } from "../../context/hooks"; import { setModalOpened } from "../../context/reducer"; +import { normaliseCoin } from "../../lib/core/coins"; import { claimRewards } from "../../lib/core/tx"; type Step = "completed" | "loading"; @@ -34,6 +37,12 @@ const claimRewardsLoop = async ( .reduce(async (promise, delegation) => { await promise; + const normalised = normaliseCoin(delegation.rewards); + + if (new BigNumber(normalised.amount).lt(minClaimableXion)) { + return; + } + const addresses = { delegator: delegatorAddress, validator: delegation.validatorAddress, diff --git a/src/features/staking/components/staking-overview.tsx b/src/features/staking/components/staking-overview.tsx index 1df6f58..4dd9b2d 100644 --- a/src/features/staking/components/staking-overview.tsx +++ b/src/features/staking/components/staking-overview.tsx @@ -15,11 +15,11 @@ import { useStaking } from "../context/hooks"; import { setModalOpened } from "../context/reducer"; import { getAPR, + getCanClaimAnyRewards, getTotalDelegation, getTotalRewards, } from "../context/selectors"; import { getEmptyXionCoin } from "../lib/core/coins"; -import { getIsMinimumClaimable } from "../lib/core/tx"; import { formatAPR, formatCoin, formatXionToUSD } from "../lib/formatters"; import { DivisorVertical } from "./divisor"; @@ -78,7 +78,7 @@ const StakingOverview = () => { Claimable Rewards
{formatXionToUSD(totalRewards)} - {getIsMinimumClaimable(totalRewards) && ( + {getCanClaimAnyRewards(staking.state) && ( { staking.dispatch( diff --git a/src/features/staking/components/validator-delegation.tsx b/src/features/staking/components/validator-delegation.tsx index 95b4208..ae5977f 100644 --- a/src/features/staking/components/validator-delegation.tsx +++ b/src/features/staking/components/validator-delegation.tsx @@ -18,6 +18,7 @@ import { getValidatorDetailsAction } from "../context/actions"; import { useStaking } from "../context/hooks"; import { setModalOpened } from "../context/reducer"; import { + getCanClaimAnyRewards, getTokensAvailableBG, getTotalDelegation, getTotalRewards, @@ -67,10 +68,7 @@ export default function ValidatorDelegation() { const userTotalUnbondings = getTotalUnbonding(staking.state, null); - const totalRewards = getTotalRewards( - validatorDetails.operatorAddress, - staking.state, - ); + const totalRewards = getTotalRewards(null, staking.state); const canShowDetail = getCanShowDetails(staking.state); @@ -94,7 +92,7 @@ export default function ValidatorDelegation() { Claimable Rewards
{formatXionToUSD(totalRewards)} - {totalRewards && totalRewards?.amount !== "0" && ( + {getCanClaimAnyRewards(staking.state) && ( { staking.dispatch( diff --git a/src/features/staking/context/selectors.ts b/src/features/staking/context/selectors.ts index 44199b7..6c6ed77 100644 --- a/src/features/staking/context/selectors.ts +++ b/src/features/staking/context/selectors.ts @@ -2,6 +2,7 @@ import BigNumber from "bignumber.js"; import type { Validator } from "cosmjs-types/cosmos/staking/v1beta1/staking"; import { normaliseCoin, sumAllCoins } from "../lib/core/coins"; +import { getCanClaimRewards } from "../lib/core/tx"; import type { StakingState } from "./state"; export const getTotalDelegation = ( @@ -88,6 +89,16 @@ export const getVotingPowerPerc = ( .toNumber(); }; +export const getCanClaimAnyRewards = (state: StakingState) => { + const { delegations } = state; + + if (!delegations?.items.length) { + return false; + } + + return delegations.items.some((d) => getCanClaimRewards(d?.rewards)); +}; + export const getAllValidators = ( state: StakingState, ): Record => diff --git a/src/features/staking/lib/core/tx.ts b/src/features/staking/lib/core/tx.ts index ea971ac..358fb5b 100644 --- a/src/features/staking/lib/core/tx.ts +++ b/src/features/staking/lib/core/tx.ts @@ -153,8 +153,12 @@ export const claimRewards = async ( .catch(handleTxError); }; -export const getIsMinimumClaimable = (amount: Coin) => { - const normalised = normaliseCoin(amount); +export const getCanClaimRewards = (rewards?: Coin) => { + if (!rewards) { + return false; + } + + const normalised = normaliseCoin(rewards); return new BigNumber(normalised.amount).gte(minClaimableXion); };