From 761e90b59e8279bad03f2ad52acd55b819602e20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:28:13 -0300 Subject: [PATCH 01/12] chore(app): bump next-router-mock from 0.9.3 to 0.9.7 (#2521) Bumps [next-router-mock](https://github.com/scottrippey/next-router-mock) from 0.9.3 to 0.9.7. - [Commits](https://github.com/scottrippey/next-router-mock/compare/v0.9.3...v0.9.7) --- updated-dependencies: - dependency-name: next-router-mock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ralf --- packages/app/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 5f2b0eb931..f47c43e1d1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -106,7 +106,7 @@ "jest-environment-jsdom": "28.1.0", "jest-preview": "^0.3.1", "jest-transformer-svg": "^2.0.1", - "next-router-mock": "0.9.3", + "next-router-mock": "0.9.7", "pinst": "3.0.0", "postcss": "8.4.27", "prettier": "2.8.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 150f7e5ee3..e0b51d31ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -331,8 +331,8 @@ importers: specifier: ^2.0.1 version: 2.0.1(jest@28.1.0)(react@18.2.0) next-router-mock: - specifier: 0.9.3 - version: 0.9.3(next@13.4.12)(react@18.2.0) + specifier: 0.9.7 + version: 0.9.7(next@13.4.8)(react@18.2.0) pinst: specifier: 3.0.0 version: 3.0.0 @@ -15501,8 +15501,8 @@ packages: trouter: 3.2.1 dev: false - /next-router-mock@0.9.3(next@13.4.12)(react@18.2.0): - resolution: {integrity: sha512-jl8eFe71LpMVGeBMpoxILkGfEgGY7IfLy8XPyv05/o61p5oQRNpoMmk46VMxRIpt0fI8XcvznBZKpDK6vbYQcQ==} + /next-router-mock@0.9.7(next@13.4.8)(react@18.2.0): + resolution: {integrity: sha512-y5ioLCIsdkJKwcoPnrUyocNEJT22RK4wKSg6LO0Q2XkBBvkYprEWy5FiCZt+CA8+qpfxpBlNca76F+glEohbRA==} peerDependencies: next: '>=10.0.0' react: '>=17.0.0' From 39154b4187c7592212828060e338be59ddff76a9 Mon Sep 17 00:00:00 2001 From: Oluwakorede Fashokun Date: Fri, 28 Jul 2023 16:03:34 -0400 Subject: [PATCH 02/12] fix(sdk): add SDK versioning (#2708) --- .github/workflows/publish-sdk.yml | 3 +++ packages/sdk/package.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-sdk.yml b/.github/workflows/publish-sdk.yml index 3f269f75b4..259bbb8d8c 100644 --- a/.github/workflows/publish-sdk.yml +++ b/.github/workflows/publish-sdk.yml @@ -34,6 +34,9 @@ jobs: - name: Build code run: pnpm build + + - name: Bump version + run: pnpm bump-version - name: Publish run: | diff --git a/packages/sdk/package.json b/packages/sdk/package.json index a9114487ca..cdb370de0d 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -11,7 +11,8 @@ "dev": "tsc -b -w", "build": "tsc -b", "check-types": "tsc --noEmit", - "generate-contract-types": "typechain --target ethers-v5 --out-dir ./src/contracts/types './src/contracts/abis/*.json' --show-stack-traces" + "generate-contract-types": "typechain --target ethers-v5 --out-dir ./src/contracts/types './src/contracts/abis/*.json' --show-stack-traces", + "bump-version": "npm version '$(cat package.json | jq -r '.version')'" }, "keywords": [], "author": "", From 88af62903b51c7fec13ad2d282ab3e553bfcc03f Mon Sep 17 00:00:00 2001 From: leifu Date: Mon, 31 Jul 2023 16:11:08 +0300 Subject: [PATCH 03/12] fix(*): replace est. rewards file (#2685) * Removed the old distributor from sdk and fetched the estimated rewards from aws * Remove the old distributor in the kwenta claim * Clean the code * fix(*): aws bucket url --------- Co-authored-by: platschi --- .../dashboard/Stake/TradingRewardsTab.tsx | 2 +- packages/app/src/state/staking/actions.ts | 18 ++--- packages/app/src/state/staking/types.ts | 2 +- packages/sdk/src/services/kwentaToken.ts | 69 ++++++------------- 4 files changed, 31 insertions(+), 60 deletions(-) diff --git a/packages/app/src/sections/dashboard/Stake/TradingRewardsTab.tsx b/packages/app/src/sections/dashboard/Stake/TradingRewardsTab.tsx index fdc99bf586..2d8cfa7779 100644 --- a/packages/app/src/sections/dashboard/Stake/TradingRewardsTab.tsx +++ b/packages/app/src/sections/dashboard/Stake/TradingRewardsTab.tsx @@ -59,7 +59,7 @@ const TradingRewardsTab: FC = memo( }, [totalFuturesFeeQuery.data]) const estimatedRewardQuery = useGetFile( - `trading-rewards-snapshots/${network.id === 420 ? `goerli-` : ''}epoch-current.json` + `/${network.id === 420 ? `goerli-` : ''}epoch-current.json` ) const estimatedReward = useMemo( () => BigNumber.from(estimatedRewardQuery?.data?.claims[walletAddress!]?.amount ?? 0), diff --git a/packages/app/src/state/staking/actions.ts b/packages/app/src/state/staking/actions.ts index 43cca7aa0d..54b459555f 100644 --- a/packages/app/src/state/staking/actions.ts +++ b/packages/app/src/state/staking/actions.ts @@ -319,7 +319,7 @@ export const fetchClaimableRewards = createAsyncThunk< { claimableKwentaRewards: Awaited< ReturnType - >['claimableRewards'][] + >['claimableRewards'] claimableOpRewards: Awaited< ReturnType >['claimableRewards'] @@ -338,20 +338,20 @@ export const fetchClaimableRewards = createAsyncThunk< staking: { epochPeriod }, } = getState() - const { claimableRewards: claimableKwentaRewardsV2, totalRewards: kwentaRewardsV2 } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, false, false, 9) + const { claimableRewards: claimableKwentaRewards, totalRewards: kwentaRewards } = + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, false, 9) const { claimableRewards: claimableOpRewards, totalRewards: opRewards } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, true, false) + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, false) const { claimableRewards: claimableSnxOpRewards, totalRewards: snxOpRewards } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, true, true) + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, true) return { - claimableKwentaRewards: [claimableKwentaRewardsV2], + claimableKwentaRewards, claimableOpRewards, claimableSnxOpRewards, - kwentaRewards: kwentaRewardsV2.toString(), + kwentaRewards: kwentaRewards.toString(), opRewards: opRewards.toString(), snxOpRewards: snxOpRewards.toString(), } @@ -370,7 +370,7 @@ export const claimMultipleAllRewards = createAsyncThunk } = getState() const { hash } = await sdk.kwentaToken.claimMultipleAllRewards([ - ...claimableKwentaRewards, + claimableKwentaRewards, claimableOpRewards, claimableSnxOpRewards, ]) @@ -395,7 +395,7 @@ export const claimMultipleKwentaRewards = createAsyncThunk `trading-rewards-snapshots/${networkId === 420 ? 'goerli-' : ''}epoch-current${i}.json` + (i) => `/${networkId === 420 ? 'goerli-' : ''}epoch-current${i}.json` ) const responses: EpochData[] = await Promise.all( fileNames.map(async (fileName) => { - const response = await fleekClient.get(fileName) + const response = await awsClient.get(fileName) return { ...response.data } }) ) @@ -527,19 +527,16 @@ export default class KwentaTokenService { return { estimatedKwentaRewards, estimatedOpRewards } } - public async getClaimableRewards(epochPeriod: number, isOldDistributor: boolean = true) { - const { MultipleMerkleDistributor, MultipleMerkleDistributorPerpsV2 } = - this.sdk.context.multicallContracts + public async getClaimableRewards(epochPeriod: number) { + const { MultipleMerkleDistributorPerpsV2 } = this.sdk.context.multicallContracts const { walletAddress } = this.sdk.context - if (!MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2) { + if (!MultipleMerkleDistributorPerpsV2) { throw new Error(sdkErrors.UNSUPPORTED_NETWORK) } const periods = Array.from(new Array(Number(epochPeriod)), (_, i) => i) - const adjustedPeriods = isOldDistributor - ? periods.slice(0, TRADING_REWARDS_CUTOFF_EPOCH) - : periods.slice(TRADING_REWARDS_CUTOFF_EPOCH) + const adjustedPeriods = periods.slice(TRADING_REWARDS_CUTOFF_EPOCH) const fileNames = adjustedPeriods.map( (i) => `${this.sdk.context.networkId === 420 ? `goerli-` : ''}epoch-${i}.json` @@ -548,13 +545,7 @@ export default class KwentaTokenService { const responses: EpochData[] = await Promise.all( fileNames.map(async (fileName, index) => { const response = await awsClient.get(fileName) - const period = isOldDistributor - ? index >= 5 - ? index >= 10 - ? index + 2 - : index + 1 - : index - : index + TRADING_REWARDS_CUTOFF_EPOCH + const period = index + TRADING_REWARDS_CUTOFF_EPOCH return { ...response.data, period } }) ) @@ -572,11 +563,7 @@ export default class KwentaTokenService { .filter((x): x is ClaimParams => !!x) const claimed: boolean[] = await this.sdk.context.multicallProvider.all( - rewards.map((reward) => - isOldDistributor - ? MultipleMerkleDistributor.isClaimed(reward[0], reward[4]) - : MultipleMerkleDistributorPerpsV2.isClaimed(reward[0], reward[4]) - ) + rewards.map((reward) => MultipleMerkleDistributorPerpsV2.isClaimed(reward[0], reward[4])) ) const { totalRewards, claimableRewards } = rewards.reduce( @@ -596,13 +583,11 @@ export default class KwentaTokenService { public async getClaimableAllRewards( epochPeriod: number, - isOldDistributor: boolean = true, isOp: boolean = false, isSnx: boolean = false, cutoffPeriod: number = 0 ) { const { - MultipleMerkleDistributor, MultipleMerkleDistributorPerpsV2, MultipleMerkleDistributorOp, MultipleMerkleDistributorSnxOp, @@ -610,7 +595,6 @@ export default class KwentaTokenService { const { walletAddress } = this.sdk.context if ( - !MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2 || !MultipleMerkleDistributorOp || !MultipleMerkleDistributorSnxOp @@ -620,9 +604,7 @@ export default class KwentaTokenService { const periods = Array.from(new Array(Number(epochPeriod)), (_, i) => i) - const adjustedPeriods = isOldDistributor - ? periods.slice(0, TRADING_REWARDS_CUTOFF_EPOCH) - : isOp + const adjustedPeriods = isOp ? periods.slice(OP_REWARDS_CUTOFF_EPOCH) : periods.slice(TRADING_REWARDS_CUTOFF_EPOCH) @@ -638,13 +620,7 @@ export default class KwentaTokenService { try { if (index < cutoffPeriod) return null const response = await awsClient.get(fileName) - const period = isOldDistributor - ? index >= 5 - ? index >= 10 - ? index + 2 - : index + 1 - : index - : isOp + const period = isOp ? isSnx ? index : index + OP_REWARDS_CUTOFF_EPOCH @@ -672,9 +648,7 @@ export default class KwentaTokenService { const claimed: boolean[] = await this.sdk.context.multicallProvider.all( rewards.map((reward) => - isOldDistributor - ? MultipleMerkleDistributor.isClaimed(reward[0], reward[4]) - : isOp + isOp ? isSnx ? MultipleMerkleDistributorSnxOp.isClaimed(reward[0], reward[4]) : MultipleMerkleDistributorOp.isClaimed(reward[0], reward[4]) @@ -697,24 +671,23 @@ export default class KwentaTokenService { return { claimableRewards, totalRewards } } - public async claimMultipleKwentaRewards(claimableRewards: ClaimParams[][]) { - const { BatchClaimer, MultipleMerkleDistributor, MultipleMerkleDistributorPerpsV2 } = - this.sdk.context.contracts + public async claimKwentaRewards(claimableRewards: ClaimParams[]) { + const { MultipleMerkleDistributorPerpsV2 } = this.sdk.context.contracts - if (!BatchClaimer || !MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2) { + if (!MultipleMerkleDistributorPerpsV2) { throw new Error(sdkErrors.UNSUPPORTED_NETWORK) } - return this.sdk.transactions.createContractTxn(BatchClaimer, 'claimMultiple', [ - [MultipleMerkleDistributor.address, MultipleMerkleDistributorPerpsV2.address], - claimableRewards, - ]) + return this.sdk.transactions.createContractTxn( + MultipleMerkleDistributorPerpsV2, + 'claimMultiple', + [claimableRewards] + ) } public async claimMultipleAllRewards(claimableRewards: ClaimParams[][]) { const { BatchClaimer, - MultipleMerkleDistributor, MultipleMerkleDistributorPerpsV2, MultipleMerkleDistributorOp, MultipleMerkleDistributorSnxOp, @@ -722,7 +695,6 @@ export default class KwentaTokenService { if ( !BatchClaimer || - !MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2 || !MultipleMerkleDistributorOp || !MultipleMerkleDistributorSnxOp @@ -732,7 +704,6 @@ export default class KwentaTokenService { return this.sdk.transactions.createContractTxn(BatchClaimer, 'claimMultiple', [ [ - MultipleMerkleDistributor.address, MultipleMerkleDistributorPerpsV2.address, MultipleMerkleDistributorOp.address, MultipleMerkleDistributorSnxOp.address, From ced333c208174f1896a7f70e641573d7c4bd2120 Mon Sep 17 00:00:00 2001 From: leifu Date: Mon, 31 Jul 2023 16:23:10 +0300 Subject: [PATCH 04/12] fix(app): missing asset charts on landing page (#2714) Add missing eth/btc chart to landing page --- packages/app/src/sections/homepage/Assets.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/app/src/sections/homepage/Assets.tsx b/packages/app/src/sections/homepage/Assets.tsx index 57e9d4bdeb..43dd616f79 100644 --- a/packages/app/src/sections/homepage/Assets.tsx +++ b/packages/app/src/sections/homepage/Assets.tsx @@ -1,5 +1,5 @@ import { SECONDS_PER_DAY } from '@kwenta/sdk/constants' -import { MarketKeyByAsset } from '@kwenta/sdk/utils' +import { getDisplayAsset, MarketKeyByAsset } from '@kwenta/sdk/utils' import { wei } from '@synthetixio/wei' import { ColorType, createChart, UTCTimestamp } from 'lightweight-charts' import router from 'next/router' @@ -85,7 +85,12 @@ export const PriceChart = ({ asset }: PriceChartProps) => { }, }) - requestCandlesticks(asset, Math.floor(Date.now() / 1000) - SECONDS_PER_DAY, undefined, 3600) + requestCandlesticks( + getDisplayAsset(asset), + Math.floor(Date.now() / 1000) - SECONDS_PER_DAY, + undefined, + 3600 + ) .then((bars) => { let positive = false if (bars !== undefined) { From 724ac3812df523bae0af0d7f50daa44749307c9f Mon Sep 17 00:00:00 2001 From: leifu Date: Mon, 31 Jul 2023 16:58:07 +0300 Subject: [PATCH 05/12] fix(app): set max decimals in trade history (#2702) Support max decimals in format number --- .../futures/TradingHistory/TradesHistoryTable.tsx | 1 + packages/sdk/src/utils/number.ts | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx index ca9800bc0e..87154390a3 100644 --- a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx +++ b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx @@ -126,6 +126,7 @@ const TradesHistoryTable: FC = ({ mobile, display }) => {formatNumber(cellProps.getValue().abs(), { suggestDecimals: true, truncateOver: 1e6, + maxDecimals: 6, })}{' '} {normal ? '💀' : ''} diff --git a/packages/sdk/src/utils/number.ts b/packages/sdk/src/utils/number.ts index 7b77524ee6..6d1d9ea7dc 100644 --- a/packages/sdk/src/utils/number.ts +++ b/packages/sdk/src/utils/number.ts @@ -121,19 +121,17 @@ export const formatNumber = (value: WeiSource, options?: FormatNumberOptions) => const weiBeforeAsString = truncation ? weiValue.abs().div(truncation.divisor) : weiValue.abs() - const decimals = truncation + const defaultDecimals = truncation ? truncation.decimals : suggestDecimals ? suggestedDecimals(weiBeforeAsString) : options?.minDecimals ?? DEFAULT_NUMBER_DECIMALS - let weiAsStringWithDecimals = weiBeforeAsString.toString(decimals) + const decimals = options?.maxDecimals + ? Math.min(defaultDecimals, options.maxDecimals) + : defaultDecimals - if (options?.maxDecimals || options?.maxDecimals === 0) { - weiAsStringWithDecimals = wei(weiAsStringWithDecimals).toString(options.maxDecimals) - } - - const withCommas = commifyAndPadDecimals(weiAsStringWithDecimals, decimals) + const withCommas = commifyAndPadDecimals(weiBeforeAsString.toString(decimals), decimals) formattedValue.push(withCommas) From 6ace6861e81f1a8529f38c559a86543bb4d0dcf8 Mon Sep 17 00:00:00 2001 From: platschi Date: Mon, 31 Jul 2023 13:37:38 -0300 Subject: [PATCH 06/12] chore(*): dependabot PR limit to 3 --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f5d502908a..0825e3565b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,7 +2,7 @@ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" - open-pull-requests-limit: 8 + open-pull-requests-limit: 3 target-branch: "dev" schedule: interval: "daily" @@ -11,7 +11,7 @@ updates: directory: "/" schedule: interval: "daily" - open-pull-requests-limit: 8 + open-pull-requests-limit: 3 target-branch: "dev" labels: - "npm" From b48959fc4cd4f2b46f4722c11917d45fc512d778 Mon Sep 17 00:00:00 2001 From: leifu Date: Tue, 1 Aug 2023 00:38:23 +0300 Subject: [PATCH 07/12] fix(app): reset staking data at disconnect (#2701) * Reset the staking data when switching the wallet * Fixed the error when wallet disconnecting * Fix the escrow data reset * Cleaned the code and dropped the unnecessary file * Remove the staking v2 reverse migration banner * Reset the estimated rewards and claimable rewards when swtiching the wallet * Updated the claimable rewards after switching the wallet, no need to access to staking page * Fixed the naming --- packages/app/src/constants/announcement.ts | 2 +- .../Stake/InputCards/RedeemInputCard.tsx | 103 ----- .../dashboard/Stake/RedemptionTab.tsx | 21 - .../futures/FeeInfoBox/TradePanelFeeInfo.tsx | 8 +- .../shared/Layout/HomeLayout/Banner.tsx | 11 +- packages/app/src/state/futures/hooks.ts | 14 +- packages/app/src/state/staking/actions.ts | 379 +++++++++--------- packages/app/src/state/staking/reducer.ts | 100 +++-- packages/app/src/state/staking/selectors.ts | 22 - packages/app/src/state/staking/types.ts | 82 ++-- packages/app/src/state/wallet/actions.ts | 2 + 11 files changed, 305 insertions(+), 439 deletions(-) delete mode 100644 packages/app/src/sections/dashboard/Stake/InputCards/RedeemInputCard.tsx delete mode 100644 packages/app/src/sections/dashboard/Stake/RedemptionTab.tsx diff --git a/packages/app/src/constants/announcement.ts b/packages/app/src/constants/announcement.ts index d307f3cc42..6c4efec004 100644 --- a/packages/app/src/constants/announcement.ts +++ b/packages/app/src/constants/announcement.ts @@ -1,7 +1,7 @@ import { MILLISECONDS_PER_DAY } from '@kwenta/sdk/constants' // Shows or hides the home page banner entirely when set to true/false -export const BANNER_ENABLED = true +export const BANNER_ENABLED = false // Sets the link destination for the banner component, or renders the banner as // plain, un-clickable text if set to a falsey value (`false`, `null`, '', etc...) export const BANNER_LINK_URL = diff --git a/packages/app/src/sections/dashboard/Stake/InputCards/RedeemInputCard.tsx b/packages/app/src/sections/dashboard/Stake/InputCards/RedeemInputCard.tsx deleted file mode 100644 index 4461e6f8c2..0000000000 --- a/packages/app/src/sections/dashboard/Stake/InputCards/RedeemInputCard.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { truncateNumbers } from '@kwenta/sdk/utils' -import { FC, useCallback, useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import styled from 'styled-components' - -import Button from 'components/Button' -import { FlexDivRowCentered } from 'components/layout/flex' -import { StakingCard } from 'sections/dashboard/Stake/card' -import { useAppDispatch, useAppSelector } from 'state/hooks' -import { approveKwentaToken, redeemToken } from 'state/staking/actions' -import { - selectIsVeKwentaTokenApproved, - selectIsVKwentaTokenApproved, - selectVeKwentaBalance, - selectVKwentaBalance, -} from 'state/staking/selectors' -import { numericValueCSS } from 'styles/common' - -type RedeemInputCardProps = { - inputLabel: string - isVKwenta: boolean -} - -const RedeemInputCard: FC = ({ inputLabel, isVKwenta }) => { - const { t } = useTranslation() - const dispatch = useAppDispatch() - - const vKwentaBalance = useAppSelector(selectVKwentaBalance) - const veKwentaBalance = useAppSelector(selectVeKwentaBalance) - const isVKwentaApproved = useAppSelector(selectIsVKwentaTokenApproved) - const isVeKwentaApproved = useAppSelector(selectIsVeKwentaTokenApproved) - - const isApproved = useMemo( - () => (isVKwenta ? isVKwentaApproved : isVeKwentaApproved), - [isVKwenta, isVKwentaApproved, isVeKwentaApproved] - ) - - const balance = useMemo( - () => (isVKwenta ? vKwentaBalance : veKwentaBalance), - [isVKwenta, vKwentaBalance, veKwentaBalance] - ) - - const buttonTranslationKey = useMemo(() => { - return isApproved - ? 'dashboard.stake.tabs.stake-table.redeem' - : 'dashboard.stake.tabs.stake-table.approve' - }, [isApproved]) - - const submitRedeem = useCallback(() => { - const token = isVKwenta ? 'vKwenta' : 'veKwenta' - - if (!isApproved) { - dispatch(approveKwentaToken(token)) - } else { - dispatch(redeemToken(token)) - } - }, [dispatch, isApproved, isVKwenta]) - - return ( - -
- -
{inputLabel}
- -
{t('dashboard.stake.tabs.stake-table.balance')}
-
{truncateNumbers(balance, 4)}
-
-
-
- -
- ) -} - -const StyledFlexDivRowCentered = styled(FlexDivRowCentered)` - column-gap: 5px; -` - -const StakingInputCardContainer = styled(StakingCard)` - min-height: 125px; - max-height: 250px; - display: flex; - flex-direction: column; - justify-content: space-between; -` - -const StakeInputHeader = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; - color: ${(props) => props.theme.colors.selectedTheme.title}; - font-size: 14px; - - .max { - color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; - ${numericValueCSS}; - } -` - -export default RedeemInputCard diff --git a/packages/app/src/sections/dashboard/Stake/RedemptionTab.tsx b/packages/app/src/sections/dashboard/Stake/RedemptionTab.tsx deleted file mode 100644 index f6a6d01c61..0000000000 --- a/packages/app/src/sections/dashboard/Stake/RedemptionTab.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useTranslation } from 'react-i18next' - -import { SplitContainer } from 'components/layout/grid' - -import RedeemInputCard from './InputCards/RedeemInputCard' - -const RedemptionTab = () => { - const { t } = useTranslation() - - return ( - - - - - ) -} - -export default RedemptionTab diff --git a/packages/app/src/sections/futures/FeeInfoBox/TradePanelFeeInfo.tsx b/packages/app/src/sections/futures/FeeInfoBox/TradePanelFeeInfo.tsx index 21e9dcc337..fab2e798df 100644 --- a/packages/app/src/sections/futures/FeeInfoBox/TradePanelFeeInfo.tsx +++ b/packages/app/src/sections/futures/FeeInfoBox/TradePanelFeeInfo.tsx @@ -15,8 +15,8 @@ import ROUTES from 'constants/routes' import { selectTradePreview } from 'state/futures/selectors' import { useAppSelector } from 'state/hooks' import { - selectStakedEscrowedKwentaBalanceV2, - selectStakedKwentaBalanceV2, + selectStakedEscrowedKwentaBalance, + selectStakedKwentaBalance, } from 'state/staking/selectors' import { selectWallet } from 'state/wallet/selectors' @@ -36,8 +36,8 @@ export const TradePanelFeeInfo = memo(() => { const TradingRewardRow = memo(() => { const { t } = useTranslation() const walletAddress = useAppSelector(selectWallet) - const stakedEscrowedKwentaBalance = useAppSelector(selectStakedEscrowedKwentaBalanceV2) - const stakedKwentaBalance = useAppSelector(selectStakedKwentaBalanceV2) + const stakedEscrowedKwentaBalance = useAppSelector(selectStakedEscrowedKwentaBalance) + const stakedKwentaBalance = useAppSelector(selectStakedKwentaBalance) const isRewardEligible = useMemo( () => !!walletAddress && stakedKwentaBalance.add(stakedEscrowedKwentaBalance).gt(0), diff --git a/packages/app/src/sections/shared/Layout/HomeLayout/Banner.tsx b/packages/app/src/sections/shared/Layout/HomeLayout/Banner.tsx index 140df72d2c..1a7a07831b 100644 --- a/packages/app/src/sections/shared/Layout/HomeLayout/Banner.tsx +++ b/packages/app/src/sections/shared/Layout/HomeLayout/Banner.tsx @@ -54,21 +54,14 @@ const Banner = memo(() => { const dispatch = useAppDispatch() const showBanner = useAppSelector(selectShowBanner) const storedTime: number = localStore.get('bannerIsClicked') || 0 - const router = useRouter() useEffect( () => { const currentTime = new Date().getTime() - dispatch( - setShowBanner( - currentTime - storedTime >= BANNER_WAITING_TIME && - BANNER_ENABLED && - router.pathname.includes('staking') - ) - ) + dispatch(setShowBanner(currentTime - storedTime >= BANNER_WAITING_TIME && BANNER_ENABLED)) }, // eslint-disable-next-line react-hooks/exhaustive-deps - [storedTime, router.pathname] + [storedTime] ) const handleDismiss = useCallback( diff --git a/packages/app/src/state/futures/hooks.ts b/packages/app/src/state/futures/hooks.ts index 9ef1da6527..3a916127fb 100644 --- a/packages/app/src/state/futures/hooks.ts +++ b/packages/app/src/state/futures/hooks.ts @@ -1,10 +1,6 @@ import { useAppSelector, useFetchAction, usePollAction } from 'state/hooks' -import { fetchStakeMigrateData, fetchStakingV2Data } from 'state/staking/actions' -import { - selectSelectedEpoch, - selectStakingSupportedNetwork, - selectTradingRewardsSupportedNetwork, -} from 'state/staking/selectors' +import { fetchStakeMigrateData } from 'state/staking/actions' +import { selectSelectedEpoch, selectStakingSupportedNetwork } from 'state/staking/selectors' import { selectNetwork, selectWallet } from 'state/wallet/selectors' import { @@ -38,18 +34,12 @@ export const usePollMarketFuturesData = () => { const selectedAccountType = useAppSelector(selectFuturesType) const networkSupportsCrossMargin = useAppSelector(selectFuturesSupportedNetwork) const networkSupportsFutures = useAppSelector(selectFuturesSupportedNetwork) - const networkSupportsTradingRewards = useAppSelector(selectTradingRewardsSupportedNetwork) useFetchAction(fetchCrossMarginAccount, { dependencies: [networkId, wallet], disabled: !wallet || !networkSupportsCrossMargin || selectedAccountType === 'isolated_margin', }) - useFetchAction(fetchStakingV2Data, { - dependencies: [networkId, wallet], - disabled: !wallet || !networkSupportsTradingRewards, - }) - useFetchAction(fetchMarginTransfers, { dependencies: [networkId, wallet, selectedAccountType] }) usePollAction('fetchSharedFuturesData', fetchSharedFuturesData, { dependencies: [networkId], diff --git a/packages/app/src/state/staking/actions.ts b/packages/app/src/state/staking/actions.ts index 54b459555f..cdacfba65e 100644 --- a/packages/app/src/state/staking/actions.ts +++ b/packages/app/src/state/staking/actions.ts @@ -1,110 +1,98 @@ -import KwentaSDK from '@kwenta/sdk' -import { EscrowData } from '@kwenta/sdk/types' import { createAsyncThunk } from '@reduxjs/toolkit' import { BigNumber } from 'ethers' import { notifyError } from 'components/ErrorNotifier' import { monitorTransaction } from 'contexts/RelayerContext' import { FetchStatus, ThunkConfig } from 'state/types' +import { selectWallet } from 'state/wallet/selectors' import logError from 'utils/logError' -export const fetchStakingData = createAsyncThunk< - { - rewardEscrowBalance: string - stakedNonEscrowedBalance: string - stakedEscrowedBalance: string - claimableBalance: string - kwentaBalance: string - weekCounter: number - totalStakedBalance: string - vKwentaBalance: string - vKwentaAllowance: string - kwentaAllowance: string - epochPeriod: number - veKwentaBalance: string - veKwentaAllowance: string - }, - void, - ThunkConfig ->('staking/fetchStakingData', async (_, { extra: { sdk } }) => { - try { - const { - rewardEscrowBalance, - stakedNonEscrowedBalance, - stakedEscrowedBalance, - claimableBalance, - kwentaBalance, - weekCounter, - totalStakedBalance, - vKwentaBalance, - vKwentaAllowance, - kwentaAllowance, - epochPeriod, - veKwentaBalance, - veKwentaAllowance, - } = await sdk.kwentaToken.getStakingData() - - return { - rewardEscrowBalance: rewardEscrowBalance.toString(), - stakedNonEscrowedBalance: stakedNonEscrowedBalance.toString(), - stakedEscrowedBalance: stakedEscrowedBalance.toString(), - claimableBalance: claimableBalance.toString(), - kwentaBalance: kwentaBalance.toString(), - weekCounter, - totalStakedBalance: totalStakedBalance.toString(), - vKwentaBalance: vKwentaBalance.toString(), - vKwentaAllowance: vKwentaAllowance.toString(), - kwentaAllowance: kwentaAllowance.toString(), - epochPeriod, - veKwentaBalance: veKwentaBalance.toString(), - veKwentaAllowance: veKwentaAllowance.toString(), +import { + ZERO_CLAIMABLE_REWARDS, + ZERO_ESCROW_BALANCE, + ZERO_ESTIMATED_REWARDS, + ZERO_STAKING_DATA, + ZERO_STAKING_V2_DATA, +} from './reducer' +import { + ClaimableRewards, + EscrowBalance, + EstimatedRewards, + StakingAction, + StakingActionV2, +} from './types' + +export const fetchStakingData = createAsyncThunk( + 'staking/fetchStakingData', + async (_, { getState, extra: { sdk } }) => { + try { + const wallet = selectWallet(getState()) + if (!wallet) return ZERO_STAKING_DATA + + const { + rewardEscrowBalance, + stakedNonEscrowedBalance, + stakedEscrowedBalance, + claimableBalance, + kwentaBalance, + weekCounter, + totalStakedBalance, + kwentaAllowance, + epochPeriod, + } = await sdk.kwentaToken.getStakingData() + + return { + escrowedKwentaBalance: rewardEscrowBalance.toString(), + stakedKwentaBalance: stakedNonEscrowedBalance.toString(), + stakedEscrowedKwentaBalance: stakedEscrowedBalance.toString(), + claimableBalance: claimableBalance.toString(), + kwentaBalance: kwentaBalance.toString(), + weekCounter, + totalStakedBalance: totalStakedBalance.toString(), + kwentaAllowance: kwentaAllowance.toString(), + epochPeriod, + } + } catch (err) { + logError(err) + notifyError('Failed to fetch staking data', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch staking data', err) - throw err } -}) +) -export const fetchStakingV2Data = createAsyncThunk< - { - rewardEscrowBalance: string - stakedNonEscrowedBalance: string - stakedEscrowedBalance: string - claimableBalance: string - totalStakedBalance: string - stakedResetTime: number - kwentaStakingV2Allowance: string - }, - void, - ThunkConfig ->('staking/fetchStakingDataV2', async (_, { extra: { sdk } }) => { - try { - const { - rewardEscrowBalance, - stakedNonEscrowedBalance, - stakedEscrowedBalance, - claimableBalance, - totalStakedBalance, - stakedResetTime, - kwentaStakingV2Allowance, - } = await sdk.kwentaToken.getStakingV2Data() - - return { - rewardEscrowBalance: rewardEscrowBalance.toString(), - stakedNonEscrowedBalance: stakedNonEscrowedBalance.toString(), - stakedEscrowedBalance: stakedEscrowedBalance.toString(), - claimableBalance: claimableBalance.toString(), - totalStakedBalance: totalStakedBalance.toString(), - stakedResetTime, - kwentaStakingV2Allowance: kwentaStakingV2Allowance.toString(), +export const fetchStakingV2Data = createAsyncThunk( + 'staking/fetchStakingDataV2', + async (_, { getState, extra: { sdk } }) => { + try { + const wallet = selectWallet(getState()) + if (!wallet) return ZERO_STAKING_V2_DATA + + const { + rewardEscrowBalance, + stakedNonEscrowedBalance, + stakedEscrowedBalance, + claimableBalance, + totalStakedBalance, + stakedResetTime, + kwentaStakingV2Allowance, + } = await sdk.kwentaToken.getStakingV2Data() + + return { + escrowedKwentaBalance: rewardEscrowBalance.toString(), + stakedKwentaBalance: stakedNonEscrowedBalance.toString(), + stakedEscrowedKwentaBalance: stakedEscrowedBalance.toString(), + claimableBalance: claimableBalance.toString(), + totalStakedBalance: totalStakedBalance.toString(), + stakedResetTime, + kwentaStakingV2Allowance: kwentaStakingV2Allowance.toString(), + } + } catch (err) { + logError(err) + notifyError('Failed to fetch staking V2 data', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch staking V2 data', err) - throw err } -}) +) export const approveKwentaToken = createAsyncThunk< void, @@ -142,82 +130,88 @@ export const redeemToken = createAsyncThunk[]; totalVestable: string }, - void, - ThunkConfig ->('staking/fetchEscrowData', async (_, { extra: { sdk } }) => { - try { - const { escrowData, totalVestable } = await sdk.kwentaToken.getEscrowData() - - return { - escrowData: escrowData.map((e) => ({ - ...e, - vestable: e.vestable.toString(), - amount: e.amount.toString(), - fee: e.fee.toString(), - })), - totalVestable: totalVestable.toString(), +export const fetchEscrowData = createAsyncThunk( + 'staking/fetchEscrowData', + async (_, { getState, extra: { sdk } }) => { + try { + const wallet = selectWallet(getState()) + if (!wallet) return ZERO_ESCROW_BALANCE + + const { escrowData, totalVestable } = await sdk.kwentaToken.getEscrowData() + + return { + escrowData: escrowData.map((e) => ({ + ...e, + vestable: e.vestable.toString(), + amount: e.amount.toString(), + fee: e.fee.toString(), + })), + totalVestable: totalVestable.toString(), + } + } catch (err) { + logError(err) + notifyError('Failed to fetch escrow data', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch escrow data', err) - throw err } -}) +) -export const fetchEscrowV2Data = createAsyncThunk< - { escrowData: EscrowData[]; totalVestable: string }, - void, - ThunkConfig ->('staking/fetchEscrowV2Data', async (_, { extra: { sdk } }) => { - try { - const { escrowData, totalVestable } = await sdk.kwentaToken.getEscrowV2Data() - - return { - escrowData: escrowData.map((e) => ({ - ...e, - vestable: e.vestable.toString(), - amount: e.amount.toString(), - fee: e.fee.toString(), - })), - totalVestable: totalVestable.toString(), +export const fetchEscrowV2Data = createAsyncThunk( + 'staking/fetchEscrowV2Data', + async (_, { getState, extra: { sdk } }) => { + try { + const wallet = selectWallet(getState()) + if (!wallet) return ZERO_ESCROW_BALANCE + + const { escrowData, totalVestable } = await sdk.kwentaToken.getEscrowV2Data() + + return { + escrowData: escrowData.map((e) => ({ + ...e, + vestable: e.vestable.toString(), + amount: e.amount.toString(), + fee: e.fee.toString(), + })), + totalVestable: totalVestable.toString(), + } + } catch (err) { + logError(err) + notifyError('Failed to fetch escrow V2 data', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch escrow V2 data', err) - throw err } -}) +) -export const fetchEstimatedRewards = createAsyncThunk< - { estimatedKwentaRewards: string; estimatedOpRewards: string }, - void, - ThunkConfig ->('staking/fetchEstimatedRewards', async (_, { extra: { sdk } }) => { - try { - const { estimatedKwentaRewards, estimatedOpRewards } = - await sdk.kwentaToken.getEstimatedRewards() - return { - estimatedKwentaRewards: estimatedKwentaRewards.toString(), - estimatedOpRewards: estimatedOpRewards.toString(), +export const fetchEstimatedRewards = createAsyncThunk( + 'staking/fetchEstimatedRewards', + async (_, { getState, extra: { sdk } }) => { + try { + const wallet = selectWallet(getState()) + if (!wallet) return ZERO_ESTIMATED_REWARDS + + const { estimatedKwentaRewards, estimatedOpRewards } = + await sdk.kwentaToken.getEstimatedRewards() + return { + estimatedKwentaRewards: estimatedKwentaRewards.toString(), + estimatedOpRewards: estimatedOpRewards.toString(), + } + } catch (err) { + logError(err) + notifyError('Failed to fetch estimated rewards', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch estimated rewards', err) - throw err } -}) +) export const fetchStakeMigrateData = createAsyncThunk( 'staking/fetchMigrateData', async (_, { dispatch }) => { - dispatch(fetchStakingData()) - dispatch(fetchClaimableRewards()) + await dispatch(fetchStakingData()) dispatch(fetchStakingV2Data()) dispatch(fetchEscrowData()) dispatch(fetchEscrowV2Data()) dispatch(fetchEstimatedRewards()) + dispatch(fetchClaimableRewards()) } ) @@ -315,52 +309,41 @@ export const compoundRewards = createAsyncThunk( } ) -export const fetchClaimableRewards = createAsyncThunk< - { - claimableKwentaRewards: Awaited< - ReturnType - >['claimableRewards'] - claimableOpRewards: Awaited< - ReturnType - >['claimableRewards'] - claimableSnxOpRewards: Awaited< - ReturnType - >['claimableRewards'] - kwentaRewards: string - opRewards: string - snxOpRewards: string - }, - void, - ThunkConfig ->('staking/fetchClaimableRewards', async (_, { getState, extra: { sdk } }) => { - try { - const { - staking: { epochPeriod }, - } = getState() - - const { claimableRewards: claimableKwentaRewards, totalRewards: kwentaRewards } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, false, 9) - - const { claimableRewards: claimableOpRewards, totalRewards: opRewards } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, false) - - const { claimableRewards: claimableSnxOpRewards, totalRewards: snxOpRewards } = - await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, true) - - return { - claimableKwentaRewards, - claimableOpRewards, - claimableSnxOpRewards, - kwentaRewards: kwentaRewards.toString(), - opRewards: opRewards.toString(), - snxOpRewards: snxOpRewards.toString(), +export const fetchClaimableRewards = createAsyncThunk( + 'staking/fetchClaimableRewards', + async (_, { getState, extra: { sdk } }) => { + try { + const { + staking: { epochPeriod }, + } = getState() + const wallet = selectWallet(getState()) + + if (!wallet) return ZERO_CLAIMABLE_REWARDS + + const { claimableRewards: claimableKwentaRewards, totalRewards: kwentaRewards } = + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, false, false, 9) + + const { claimableRewards: claimableOpRewards, totalRewards: opRewards } = + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, false) + + const { claimableRewards: claimableSnxOpRewards, totalRewards: snxOpRewards } = + await sdk.kwentaToken.getClaimableAllRewards(epochPeriod, true, true) + + return { + claimableKwentaRewards, + claimableOpRewards, + claimableSnxOpRewards, + kwentaRewards: kwentaRewards.toString(), + opRewards: opRewards.toString(), + snxOpRewards: snxOpRewards.toString(), + } + } catch (err) { + logError(err) + notifyError('Failed to fetch claimable rewards', err) + throw err } - } catch (err) { - logError(err) - notifyError('Failed to fetch claimable rewards', err) - throw err } -}) +) export const claimMultipleAllRewards = createAsyncThunk( 'staking/claimMultipleAllRewards', diff --git a/packages/app/src/state/staking/reducer.ts b/packages/app/src/state/staking/reducer.ts index 9d82989d90..ec79ba7c7e 100644 --- a/packages/app/src/state/staking/reducer.ts +++ b/packages/app/src/state/staking/reducer.ts @@ -30,44 +30,72 @@ import { } from './actions' import { StakingState } from './types' -export const STAKING_INITIAL_STATE: StakingState = { +const ZERO_STAKE_BALANCE = { + escrowedKwentaBalance: '0', + stakedKwentaBalance: '0', + totalStakedBalance: '0', + claimableBalance: '0', + stakedEscrowedKwentaBalance: '0', +} + +export const ZERO_ESCROW_BALANCE = { + totalVestable: '0', + escrowData: [], +} + +const ZERO_VERSIONED_STAKE_DATA = { + ...ZERO_STAKE_BALANCE, + ...ZERO_ESCROW_BALANCE, +} + +const INITIAL_STAKE_INFO = { kwentaBalance: '0', - vKwentaBalance: '0', - veKwentaBalance: '0', - v1: { - escrowedKwentaBalance: '0', - claimableBalance: '0', - totalStakedBalance: '0', - stakedEscrowedKwentaBalance: '0', - stakedKwentaBalance: '0', - totalVestable: '0', - escrowData: [], - }, - v2: { - escrowedKwentaBalance: '0', - claimableBalance: '0', - totalStakedBalance: '0', - stakedEscrowedKwentaBalance: '0', - stakedKwentaBalance: '0', - totalVestable: '0', - escrowData: [], - }, - stakedResetTime: 0, + kwentaAllowance: '0', epochPeriod: 0, weekCounter: 1, - selectedEscrowVersion: 1, - kwentaAllowance: '0', +} + +const INITIAL_STAKE_V2_INFO = { + stakedResetTime: 0, kwentaStakingV2Allowance: '0', - vKwentaAllowance: '0', - veKwentaAllowance: '0', +} + +export const ZERO_ESTIMATED_REWARDS = { + estimatedKwentaRewards: '0', + estimatedOpRewards: '0', +} + +export const ZERO_STAKING_DATA = { + ...ZERO_STAKE_BALANCE, + ...INITIAL_STAKE_INFO, +} + +export const ZERO_STAKING_V2_DATA = { + ...ZERO_STAKE_BALANCE, + ...INITIAL_STAKE_V2_INFO, +} + +export const ZERO_CLAIMABLE_REWARDS = { kwentaRewards: '0', opRewards: '0', snxOpRewards: '0', - estimatedKwentaRewards: '0', - estimatedOpRewards: '0', claimableKwentaRewards: [], claimableOpRewards: [], claimableSnxOpRewards: [], +} + +export const STAKING_INITIAL_STATE: StakingState = { + v1: { + ...ZERO_VERSIONED_STAKE_DATA, + }, + v2: { + ...ZERO_VERSIONED_STAKE_DATA, + }, + ...INITIAL_STAKE_INFO, + ...INITIAL_STAKE_V2_INFO, + ...ZERO_ESTIMATED_REWARDS, + ...ZERO_CLAIMABLE_REWARDS, + selectedEscrowVersion: 1, stakingMigrationCompleted: true, stakeStatus: FetchStatus.Idle, unstakeStatus: FetchStatus.Idle, @@ -135,19 +163,15 @@ const stakingSlice = createSlice({ }, extraReducers: (builder) => { builder.addCase(fetchStakingData.fulfilled, (state, action) => { - state.v1.escrowedKwentaBalance = action.payload.rewardEscrowBalance - state.v1.stakedKwentaBalance = action.payload.stakedNonEscrowedBalance - state.v1.stakedEscrowedKwentaBalance = action.payload.stakedEscrowedBalance + state.v1.escrowedKwentaBalance = action.payload.escrowedKwentaBalance + state.v1.stakedKwentaBalance = action.payload.stakedKwentaBalance + state.v1.stakedEscrowedKwentaBalance = action.payload.stakedEscrowedKwentaBalance state.v1.claimableBalance = action.payload.claimableBalance state.v1.totalStakedBalance = action.payload.totalStakedBalance state.kwentaBalance = action.payload.kwentaBalance state.weekCounter = action.payload.weekCounter - state.vKwentaBalance = action.payload.vKwentaBalance - state.vKwentaAllowance = action.payload.vKwentaAllowance state.kwentaAllowance = action.payload.kwentaAllowance state.epochPeriod = action.payload.epochPeriod - state.veKwentaBalance = action.payload.veKwentaBalance - state.veKwentaAllowance = action.payload.veKwentaAllowance state.stakeStatus = FetchStatus.Idle state.unstakeStatus = FetchStatus.Idle state.stakeEscrowedStatus = FetchStatus.Idle @@ -158,9 +182,9 @@ const stakingSlice = createSlice({ state.approveKwentaStatus = FetchStatus.Idle }) builder.addCase(fetchStakingV2Data.fulfilled, (state, action) => { - state.v2.escrowedKwentaBalance = action.payload.rewardEscrowBalance - state.v2.stakedKwentaBalance = action.payload.stakedNonEscrowedBalance - state.v2.stakedEscrowedKwentaBalance = action.payload.stakedEscrowedBalance + state.v2.escrowedKwentaBalance = action.payload.escrowedKwentaBalance + state.v2.stakedKwentaBalance = action.payload.stakedKwentaBalance + state.v2.stakedEscrowedKwentaBalance = action.payload.stakedEscrowedKwentaBalance state.v2.claimableBalance = action.payload.claimableBalance state.v2.totalStakedBalance = action.payload.totalStakedBalance state.kwentaStakingV2Allowance = action.payload.kwentaStakingV2Allowance diff --git a/packages/app/src/state/staking/selectors.ts b/packages/app/src/state/staking/selectors.ts index a303e3e742..3a526bb12c 100644 --- a/packages/app/src/state/staking/selectors.ts +++ b/packages/app/src/state/staking/selectors.ts @@ -14,16 +14,6 @@ export const selectKwentaBalance = createSelector( toWei ) -export const selectVKwentaBalance = createSelector( - (state: RootState) => state.staking.vKwentaBalance, - toWei -) - -export const selectVeKwentaBalance = createSelector( - (state: RootState) => state.staking.veKwentaBalance, - toWei -) - export const selectEscrowedKwentaBalance = createSelector( (state: RootState) => state.staking.v1.escrowedKwentaBalance, toWei @@ -86,18 +76,6 @@ export const selectIsKwentaTokenApproved = createSelector( (kwentaBalance, kwentaAllowance) => kwentaBalance.lte(kwentaAllowance) ) -export const selectIsVKwentaTokenApproved = createSelector( - selectVKwentaBalance, - (state: RootState) => state.staking.vKwentaAllowance, - (vKwentaBalance, vKwentaAllowance) => vKwentaBalance.lte(vKwentaAllowance) -) - -export const selectIsVeKwentaTokenApproved = createSelector( - selectVeKwentaBalance, - (state: RootState) => state.staking.veKwentaAllowance, - (veKwentaBalance, veKwentaAllowance) => veKwentaBalance.lte(veKwentaAllowance) -) - export const selectIsKwentaTokenApprovedV2 = createSelector( selectKwentaBalance, (state: RootState) => state.staking.kwentaStakingV2Allowance, diff --git a/packages/app/src/state/staking/types.ts b/packages/app/src/state/staking/types.ts index ecf92f3d77..5f6602d316 100644 --- a/packages/app/src/state/staking/types.ts +++ b/packages/app/src/state/staking/types.ts @@ -2,50 +2,70 @@ import { EscrowData, ClaimParams } from '@kwenta/sdk/types' import { FetchStatus } from 'state/types' -export type VersionedStakeData = { +type StakeBalance = { escrowedKwentaBalance: string - claimableBalance: string + stakedKwentaBalance: string totalStakedBalance: string + claimableBalance: string stakedEscrowedKwentaBalance: string - stakedKwentaBalance: string - totalVestable: string - escrowData: EscrowData[] } -export type StakingState = { +type StakingMiscInfo = { kwentaBalance: string - vKwentaBalance: string - veKwentaBalance: string - v1: VersionedStakeData - v2: VersionedStakeData - stakedResetTime: number + kwentaAllowance: string epochPeriod: number weekCounter: number - selectedEscrowVersion: 1 | 2 - kwentaAllowance: string - vKwentaAllowance: string - veKwentaAllowance: string +} + +type StakingMiscInfoV2 = { + stakedResetTime: number kwentaStakingV2Allowance: string +} + +export type EstimatedRewards = { + estimatedKwentaRewards: string + estimatedOpRewards: string +} + +export type ClaimableRewards = { kwentaRewards: string opRewards: string snxOpRewards: string - estimatedKwentaRewards: string - estimatedOpRewards: string claimableKwentaRewards: ClaimParams[] claimableOpRewards: ClaimParams[] claimableSnxOpRewards: ClaimParams[] - selectedEpoch?: number - stakingMigrationCompleted: boolean - stakeStatus: FetchStatus - unstakeStatus: FetchStatus - stakeEscrowedStatus: FetchStatus - unstakeEscrowedStatus: FetchStatus - getRewardStatus: FetchStatus - claimKwentaRewardsStatus: FetchStatus - claimOpRewardsStatus: FetchStatus - claimSnxOpRewardsStatus: FetchStatus - claimAllRewardsStatus: FetchStatus - vestEscrowedRewardsStatus: FetchStatus - approveKwentaStatus: FetchStatus - compoundRewardsStatus: FetchStatus } + +export type EscrowBalance = { + totalVestable: string + escrowData: EscrowData[] +} + +export type VersionedStakeData = StakeBalance & EscrowBalance + +export type StakingState = StakingMiscInfo & + StakingMiscInfoV2 & + EstimatedRewards & + ClaimableRewards & { + v1: VersionedStakeData + v2: VersionedStakeData + selectedEscrowVersion: 1 | 2 + selectedEpoch?: number + stakingMigrationCompleted: boolean + stakeStatus: FetchStatus + unstakeStatus: FetchStatus + stakeEscrowedStatus: FetchStatus + unstakeEscrowedStatus: FetchStatus + getRewardStatus: FetchStatus + claimKwentaRewardsStatus: FetchStatus + claimOpRewardsStatus: FetchStatus + claimSnxOpRewardsStatus: FetchStatus + claimAllRewardsStatus: FetchStatus + vestEscrowedRewardsStatus: FetchStatus + approveKwentaStatus: FetchStatus + compoundRewardsStatus: FetchStatus + } + +export type StakingAction = StakeBalance & StakingMiscInfo + +export type StakingActionV2 = StakeBalance & StakingMiscInfoV2 diff --git a/packages/app/src/state/wallet/actions.ts b/packages/app/src/state/wallet/actions.ts index c901c0296e..8d6d5dd6d9 100644 --- a/packages/app/src/state/wallet/actions.ts +++ b/packages/app/src/state/wallet/actions.ts @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/browser' import { ethers } from 'ethers' import { fetchBalances } from 'state/balances/actions' +import { fetchStakeMigrateData } from 'state/staking/actions' import type { ThunkConfig } from 'state/types' import { setWalletAddress } from './reducer' @@ -12,6 +13,7 @@ export const resetWalletAddress = createAsyncThunk { dispatch(setWalletAddress(walletAddress)) dispatch(fetchBalances()) + dispatch(fetchStakeMigrateData()) } ) From 5503157db2ef19e7c496319a59790bc25cc9326d Mon Sep 17 00:00:00 2001 From: leifu Date: Tue, 1 Aug 2023 15:56:07 +0300 Subject: [PATCH 08/12] fix(*): right-aligned badge (#2710) * Make badge right-aligned * Right-aligned the badges on futures market table * Fixed the lint warning * Add a new flag to handle the approximate value * Revert the change of the formatNumber and set the trade size filter * Remove the unnecessary options --------- Co-authored-by: Ralf --- .../src/sections/dashboard/FuturesMarketsTable.tsx | 12 +++++------- .../src/sections/futures/Trade/MarketsDropdown.tsx | 11 ++++++++--- .../futures/TradingHistory/TradesHistoryTable.tsx | 1 + packages/sdk/src/utils/number.ts | 1 - 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx b/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx index cff632f755..00d13b64ad 100644 --- a/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx +++ b/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx @@ -14,6 +14,7 @@ import styled from 'styled-components' import ChangePercent from 'components/ChangePercent' import ColoredPrice from 'components/ColoredPrice' import Currency from 'components/Currency' +import { FlexDivRowCentered } from 'components/layout/flex' import MarketBadge from 'components/MarketBadge' import { DesktopOnlyView, MobileOrTabletView } from 'components/Media' import Spacer from 'components/Spacer' @@ -392,12 +393,8 @@ const StyledTable = styled(Table)` margin-bottom: 20px; ` as typeof Table -const StyledText = styled.div` - display: flex; - align-items: center; - margin-bottom: -4px; - grid-column: 2; - grid-row: 1; +const StyledText = styled(FlexDivRowCentered)` + justify-content: space-between; color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; font-family: ${(props) => props.theme.fonts.bold}; ` @@ -405,8 +402,9 @@ const StyledText = styled.div` const MarketContainer = styled.div` display: grid; grid-template-rows: auto auto; - grid-template-columns: auto auto; + grid-template-columns: 40px auto; align-items: center; + width: 200px; ` const StyledMobileTable = styled(StyledTable)` diff --git a/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx b/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx index 39b70391a9..84689432ef 100644 --- a/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx +++ b/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx @@ -276,7 +276,7 @@ const MarketsDropdown: React.FC = ({ mobile }) => { ), cell: ({ row }) => { return ( -
+ = ({ mobile }) => { row.original.change ? row.original.change * 100 : '0', 2 )} - style={{ textAlign: 'right', width: '60px' }} /> } /> -
+ ) }, accessorKey: 'change', @@ -322,6 +321,12 @@ const MarketsDropdown: React.FC = ({ mobile }) => { ) } +const BadgeContainer = styled(FlexDivRowCentered)` + text-align: right; + width: 60px; + justify-content: flex-end; +` + const MarketsList = styled.div<{ mobile?: boolean; height: number }>` top: 66px; z-index: 1000; diff --git a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx index 87154390a3..fbc321478b 100644 --- a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx +++ b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx @@ -50,6 +50,7 @@ const TradesHistoryTable: FC = ({ mobile, display }) => fundingAccrued: trade?.fundingAccrued, } }) + .filter((trade) => trade.amount.abs().gt(0.000001)) : [] return [...new Set(futuresTrades)] }, [futuresTradesQuery.data]) diff --git a/packages/sdk/src/utils/number.ts b/packages/sdk/src/utils/number.ts index 6d1d9ea7dc..e542c740c2 100644 --- a/packages/sdk/src/utils/number.ts +++ b/packages/sdk/src/utils/number.ts @@ -86,7 +86,6 @@ export const commifyAndPadDecimals = (value: string, decimals: number) => { return formatted } -// TODO: implement max decimals export const formatNumber = (value: WeiSource, options?: FormatNumberOptions) => { const prefix = options?.prefix const suffix = options?.suffix From 574461fbcd7798ee0acc37c2f2ab2a097e4f4861 Mon Sep 17 00:00:00 2001 From: leifu Date: Tue, 1 Aug 2023 17:07:45 +0300 Subject: [PATCH 09/12] fix(app): suggestedDecimals for mobile market dropdown (#2719) --- .../app/src/sections/futures/Trade/MarketsDropdownSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/sections/futures/Trade/MarketsDropdownSelector.tsx b/packages/app/src/sections/futures/Trade/MarketsDropdownSelector.tsx index 2dc512a1c0..a0f10d34c5 100644 --- a/packages/app/src/sections/futures/Trade/MarketsDropdownSelector.tsx +++ b/packages/app/src/sections/futures/Trade/MarketsDropdownSelector.tsx @@ -54,7 +54,7 @@ const MarketsDropdownSelector: FC = (props) => (
- {formatDollars(props.priceDetails.priceInfo?.price ?? '0')} + {formatDollars(props.priceDetails.priceInfo?.price ?? '0', { suggestDecimals: true })} {formatPercent(props.priceDetails.oneDayChange)} From ff510826761e80a3e5ca7f9e6b2ef3fc9a991e37 Mon Sep 17 00:00:00 2001 From: leifu Date: Tue, 1 Aug 2023 19:05:26 +0300 Subject: [PATCH 10/12] fix(app): infinite trade history loading (#2712) * Fixed the infinite loading of the live trade history * Added the funciton check and fixed the lint warning * Removed the unnecessary deps --- packages/app/src/components/Table/Table.tsx | 8 +++++++- packages/app/src/sections/futures/Trade/TradeBalance.tsx | 4 +--- .../futures/TradingHistory/TradesHistoryTable.tsx | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/app/src/components/Table/Table.tsx b/packages/app/src/components/Table/Table.tsx index 2eed7e5bc9..a4ebdf34f7 100644 --- a/packages/app/src/components/Table/Table.tsx +++ b/packages/app/src/components/Table/Table.tsx @@ -7,7 +7,7 @@ import { getSortedRowModel, } from '@tanstack/react-table' import type { ColumnDef, Row, SortingState, VisibilityState } from '@tanstack/react-table' -import React, { DependencyList, FC, useCallback, useMemo, useRef, useState } from 'react' +import React, { DependencyList, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' import styled, { css } from 'styled-components' import { genericMemo } from 'types/helpers' @@ -141,6 +141,12 @@ const Table = ({ [onTableRowClick] ) + useEffect(() => { + if (typeof lastRef === 'function' && data.length > 0) { + lastRef(defaultRef.current) + } + }, [lastRef, data.length]) + return ( <> diff --git a/packages/app/src/sections/futures/Trade/TradeBalance.tsx b/packages/app/src/sections/futures/Trade/TradeBalance.tsx index 6ee1b87f54..f73fab4b6e 100644 --- a/packages/app/src/sections/futures/Trade/TradeBalance.tsx +++ b/packages/app/src/sections/futures/Trade/TradeBalance.tsx @@ -20,7 +20,6 @@ import { selectFuturesType, selectIdleMargin, selectLockedMarginInMarkets, - selectWithdrawableMargin, } from 'state/futures/selectors' import { useAppDispatch, useAppSelector } from 'state/hooks' @@ -68,7 +67,6 @@ const TradeBalance = memo(() => { const availableCrossMargin = useAppSelector(selectIdleMargin) const lockedMargin = useAppSelector(selectLockedMarginInMarkets) const availableIsolatedMargin = useAppSelector(selectAvailableMargin) - const withdrawable = useAppSelector(selectWithdrawableMargin) const openModal = useAppSelector(selectShowModal) const [expanded, setExpanded] = useState(false) @@ -83,7 +81,7 @@ const TradeBalance = memo(() => { const isDepositRequired = useMemo(() => { return isCrossMarginAccount && availableCrossMargin.lt(MIN_MARGIN_AMOUNT) && lockedMargin.eq(0) - }, [availableCrossMargin, lockedMargin]) + }, [availableCrossMargin, isCrossMarginAccount, lockedMargin]) const onClickContainer = useCallback(() => { if (!isCrossMarginAccount) return diff --git a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx index fbc321478b..862d62ede7 100644 --- a/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx +++ b/packages/app/src/sections/futures/TradingHistory/TradesHistoryTable.tsx @@ -203,7 +203,7 @@ const TableAlignment = css` const StyledTable = styled(Table)` border: none; - height: 100%; + overflow-y: auto; .table-row, .table-body-row { From 226f17f8cce3de2f950c36a01e27d547ef18a8f0 Mon Sep 17 00:00:00 2001 From: leifu Date: Wed, 2 Aug 2023 00:19:43 +0300 Subject: [PATCH 11/12] feat(app): add position history to dashboard (#2709) * Add position history table * Fixed the pagination and data fetching issue for traders * Update the tab button order * Added funding to the table * Added tab button order, right-aligned elements * Renamed the element * Fixed the layout issue of the tab button * Adjusted the column width * Passed the history as a prop to position history table * Fixed the leaderboard loading * Fixed the slowness issue of the leaderboard page * keep the position history on dashboard separated from the leaderboard selected trader * Updated the button style * Updated the inactive button text color and text font, added the top border of pagination * Removed the left and right border of the pagination * Show the pagination border if there is an extra component * Revert the butotn style; Move the trader history to futures; Rename the pagination style --- .../app/src/components/Button/TabButton.tsx | 15 +- .../Currency/CurrencyPrice/CurrencyPrice.tsx | 1 + .../app/src/components/Table/Pagination.tsx | 13 +- packages/app/src/pages/dashboard/history.tsx | 19 ++- .../src/sections/dashboard/HistoryTabs.tsx | 101 ++++++++++++++ .../TraderHistory.tsx | 131 +++++++++--------- .../src/sections/futures/Trades/Trades.tsx | 12 +- .../src/sections/leaderboard/Leaderboard.tsx | 23 ++- packages/app/src/state/futures/selectors.ts | 11 ++ packages/app/src/translations/en.json | 20 +-- 10 files changed, 247 insertions(+), 99 deletions(-) create mode 100644 packages/app/src/sections/dashboard/HistoryTabs.tsx rename packages/app/src/sections/{leaderboard => futures}/TraderHistory.tsx (77%) diff --git a/packages/app/src/components/Button/TabButton.tsx b/packages/app/src/components/Button/TabButton.tsx index ae8d1d81ab..5079617c86 100644 --- a/packages/app/src/components/Button/TabButton.tsx +++ b/packages/app/src/components/Button/TabButton.tsx @@ -63,6 +63,7 @@ const TabButton: React.FC = React.memo( $vertical={props.vertical} $nofill={props.nofill} $flat={flat} + $isRounded={props.isRounded} onClick={onClick} $variant={props.variant} > @@ -113,11 +114,11 @@ const sharedStyle = css<{ &:disabled { background-color: transparent; p { - color: ${(props) => props.theme.colors.selectedTheme.button.tab.disabled.text}; + color: ${(props) => props.theme.colors.selectedTheme.button.disabled.text}; } svg { path { - fill: ${(props) => props.theme.colors.selectedTheme.button.tab.disabled.text}; + fill: ${(props) => props.theme.colors.selectedTheme.button.disabled.text}; } } @@ -137,7 +138,7 @@ const sharedStyle = css<{ color: ${(props) => props.active ? props.theme.colors.selectedTheme.button.text.primary - : props.theme.colors.selectedTheme.gray}; + : props.theme.colors.selectedTheme.newTheme.text.secondary}; } .detail { @@ -184,9 +185,9 @@ const InlineTab = styled.div<{ const StyledButton = styled(Button).attrs({ size: 'small' })<{ $vertical?: boolean + $isRounded?: boolean $nofill?: boolean $flat?: boolean - active?: boolean $variant?: 'noOutline' | undefined }>` p { @@ -195,17 +196,15 @@ const StyledButton = styled(Button).attrs({ size: 'small' })<{ ${(props) => css` flex-direction: ${props.$vertical ? 'column' : 'row'}; - border-radius: ${props.isRounded ? '100px' : '8px'}; + border-radius: ${props.$isRounded ? '100px' : '8px'}; `} ${sharedStyle} - + ${(props) => props.$variant === 'noOutline' && css` border: none; border-radius: 100px; - padding: 10px 15px; - width: 75px; `} ` diff --git a/packages/app/src/components/Currency/CurrencyPrice/CurrencyPrice.tsx b/packages/app/src/components/Currency/CurrencyPrice/CurrencyPrice.tsx index c946a5b6fc..0834a1bdd6 100644 --- a/packages/app/src/components/Currency/CurrencyPrice/CurrencyPrice.tsx +++ b/packages/app/src/components/Currency/CurrencyPrice/CurrencyPrice.tsx @@ -47,6 +47,7 @@ export const CurrencyPrice: FC = memo( suggestDecimals: true, sign: currencyKey === 'sUSD' ? '$' : sign, currencyKey: showCurrencyKey ? currencyKey : undefined, + maxDecimals: 4, ...formatOptions, })} diff --git a/packages/app/src/components/Table/Pagination.tsx b/packages/app/src/components/Table/Pagination.tsx index c7f073eee9..5bbf692d20 100644 --- a/packages/app/src/components/Table/Pagination.tsx +++ b/packages/app/src/components/Table/Pagination.tsx @@ -41,7 +41,7 @@ const Pagination: FC = React.memo( return ( <> - + @@ -84,15 +84,20 @@ const PageInfo = styled.span` color: ${(props) => props.theme.colors.selectedTheme.gray}; ` -const PaginationContainer = styled(GridDivCenteredCol)<{ compact: boolean }>` +const PaginationContainer = styled(GridDivCenteredCol)<{ + $compact: boolean + $bottomBorder: boolean +}>` grid-template-columns: auto 1fr auto; - padding: ${(props) => (props.compact ? '10px' : '15px')} 12px; + padding: ${(props) => (props.$compact ? '10px' : '15px')} 12px; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; justify-items: center; + border-top: ${(props) => props.theme.colors.selectedTheme.border}; + border-bottom: ${(props) => + props.$bottomBorder ? props.theme.colors.selectedTheme.border : 'none'}; ${media.lessThan('lg')` - border: ${(props) => props.theme.colors.selectedTheme.border}; border-radius: 0px; `} ` diff --git a/packages/app/src/pages/dashboard/history.tsx b/packages/app/src/pages/dashboard/history.tsx index a779816783..4f2da7bd09 100644 --- a/packages/app/src/pages/dashboard/history.tsx +++ b/packages/app/src/pages/dashboard/history.tsx @@ -1,27 +1,38 @@ import Head from 'next/head' -import { ReactNode } from 'react' +import { ReactNode, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { MobileHiddenView, MobileOnlyView } from 'components/Media' import Spacer from 'components/Spacer' import DashboardLayout from 'sections/dashboard/DashboardLayout' +import HistoryTabs, { HistoryTab } from 'sections/dashboard/HistoryTabs' import TradesTab from 'sections/futures/MobileTrade/UserTabs/TradesTab' -import Trades from 'sections/futures/Trades' import { usePollDashboardFuturesData } from 'state/futures/hooks' type HistoryPageProps = React.FC & { getLayout: (page: ReactNode) => JSX.Element } const HistoryPage: HistoryPageProps = () => { const { t } = useTranslation() + usePollDashboardFuturesData() + + const [currentTab, setCurrentTab] = useState(HistoryTab.Positions) + + const handleChangeTab = useCallback( + (tab: HistoryTab) => () => { + setCurrentTab(tab) + }, + [] + ) + return ( <> {t('dashboard-history.page-title')} - - + + diff --git a/packages/app/src/sections/dashboard/HistoryTabs.tsx b/packages/app/src/sections/dashboard/HistoryTabs.tsx new file mode 100644 index 0000000000..5ef9a651a5 --- /dev/null +++ b/packages/app/src/sections/dashboard/HistoryTabs.tsx @@ -0,0 +1,101 @@ +import { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import TabButton from 'components/Button/TabButton' +import { TabPanel } from 'components/Tab' +import TraderHistory from 'sections/futures/TraderHistory' +import Trades from 'sections/futures/Trades' +import { fetchPositionHistoryForTrader } from 'state/futures/actions' +import { selectUsersPositionHistory } from 'state/futures/selectors' +import { useAppDispatch, useAppSelector } from 'state/hooks' +import { selectWallet } from 'state/wallet/selectors' +import media from 'styles/media' + +export enum HistoryTab { + Positions = 'positions', + Trades = 'trades', +} + +type HistoryTabsProp = { + currentTab: HistoryTab + onChangeTab(tab: HistoryTab): () => void +} + +const HistoryTabs: React.FC = ({ currentTab, onChangeTab }) => { + const { t } = useTranslation() + const dispatch = useAppDispatch() + const walletAddress = useAppSelector(selectWallet) + const positionHistory = useAppSelector(selectUsersPositionHistory) + + useEffect(() => { + dispatch(fetchPositionHistoryForTrader(walletAddress ?? '')) + }, [dispatch, walletAddress]) + + return ( + + + + + + + +
+ + {}} + compact={true} + /> + + + + +
+
+ ) +} + +const HistoryTabsHeader = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: 15px; + + ${media.lessThan('md')` + flex-direction: column; + row-gap: 10px; + margin-bottom: 25px; + margin-top: 0px; + `} +` + +const HistoryTabsContainer = styled.div` + ${media.lessThan('md')` + padding: 15px; + `} +` + +const TabButtons = styled.div` + display: flex; + + & > button:not(:last-of-type) { + margin-right: 25px; + } + + ${media.lessThan('md')` + justify-content: flex-start; + `} +` + +export default HistoryTabs diff --git a/packages/app/src/sections/leaderboard/TraderHistory.tsx b/packages/app/src/sections/futures/TraderHistory.tsx similarity index 77% rename from packages/app/src/sections/leaderboard/TraderHistory.tsx rename to packages/app/src/sections/futures/TraderHistory.tsx index 4c63e9a1a7..58e9eaf228 100644 --- a/packages/app/src/sections/leaderboard/TraderHistory.tsx +++ b/packages/app/src/sections/futures/TraderHistory.tsx @@ -1,8 +1,9 @@ import { ZERO_WEI } from '@kwenta/sdk/constants' +import { FuturesPositionHistory } from '@kwenta/sdk/dist/types' import { getMarketName } from '@kwenta/sdk/utils' import { wei, WeiSource } from '@synthetixio/wei' import router from 'next/router' -import { FC, memo, useEffect, useMemo } from 'react' +import { FC, memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' @@ -10,45 +11,31 @@ import Currency from 'components/Currency' import CurrencyIcon from 'components/Currency/CurrencyIcon' import { FlexDiv } from 'components/layout/flex' import { DesktopOnlyView, MobileOrTabletView } from 'components/Media' -import FuturesIcon from 'components/Nav/FuturesIcon' -import Table, { TableHeader } from 'components/Table' +import Table, { TableHeader, TableNoResults } from 'components/Table' import { Body } from 'components/Text' -import { BANNER_HEIGHT_DESKTOP } from 'constants/announcement' import ROUTES from 'constants/routes' import TimeDisplay from 'sections/futures/Trades/TimeDisplay' -import { selectShowBanner } from 'state/app/selectors' -import { fetchPositionHistoryForTrader } from 'state/futures/actions' -import { - selectFuturesPositions, - selectPositionHistoryForSelectedTrader, - selectQueryStatuses, -} from 'state/futures/selectors' -import { useAppDispatch, useAppSelector } from 'state/hooks' +import { selectFuturesPositions, selectQueryStatuses } from 'state/futures/selectors' +import { useAppSelector } from 'state/hooks' import { FetchStatus } from 'state/types' -import { ExternalLink, FOOTER_HEIGHT } from 'styles/common' +import { ExternalLink } from 'styles/common' import media from 'styles/media' type TraderHistoryProps = { trader: string traderEns?: string | null + positionHistory: FuturesPositionHistory[] resetSelection: () => void compact?: boolean searchTerm?: string | undefined } const TraderHistory: FC = memo( - ({ trader, traderEns, resetSelection, compact, searchTerm }) => { + ({ trader, traderEns, positionHistory, resetSelection, compact, searchTerm }) => { const { t } = useTranslation() - const dispatch = useAppDispatch() - const positionHistory = useAppSelector(selectPositionHistoryForSelectedTrader) const positions = useAppSelector(selectFuturesPositions) - const showBanner = useAppSelector(selectShowBanner) const { selectedTraderPositionHistory: queryStatus } = useAppSelector(selectQueryStatuses) - useEffect(() => { - dispatch(fetchPositionHistoryForTrader(trader)) - }, [trader, dispatch]) - let data = useMemo(() => { return positionHistory .sort((a, b) => b.timestamp - a.timestamp) @@ -58,10 +45,8 @@ const TraderHistory: FC = memo( ? positions.find((p) => p.marketKey === stat.marketKey) : null - const pnlWithFeesPaid = stat.pnl - .sub(stat.feesPaid) - .add(stat.netFunding) - .add(thisPosition?.position?.accruedFunding ?? ZERO_WEI) + const funding = stat.netFunding.add(thisPosition?.position?.accruedFunding ?? ZERO_WEI) + const pnlWithFeesPaid = stat.pnl.sub(stat.feesPaid).add(funding) return { ...stat, @@ -69,6 +54,7 @@ const TraderHistory: FC = memo( currencyIconKey: stat.asset ? (stat.asset[0] !== 's' ? 's' : '') + stat.asset : '', marketShortName: getMarketName(stat.asset), status: stat.isOpen ? 'Open' : stat.isLiquidated ? 'Liquidated' : 'Closed', + funding, pnl: pnlWithFeesPaid, pnlPct: totalDeposit.gt(0) ? `(${pnlWithFeesPaid @@ -87,17 +73,11 @@ const TraderHistory: FC = memo( ) }, [positionHistory, positions, searchTerm]) - const tableHeight = useMemo( - () => window.innerHeight - FOOTER_HEIGHT - 161 - Number(showBanner) * BANNER_HEIGHT_DESKTOP, - [showBanner] - ) - return ( <> = memo( data={data} hideHeaders={compact} autoResetPageIndex={false} + compactPagination={true} columns={[ { header: () => ( @@ -136,7 +117,7 @@ const TraderHistory: FC = memo( ) }, - size: compact ? 40 : 100, + size: 100, }, { header: () => ( @@ -147,10 +128,9 @@ const TraderHistory: FC = memo( {cellProps.row.original.marketShortName} - ), - size: compact ? 40 : 100, + size: 150, }, { header: () => ( @@ -158,49 +138,78 @@ const TraderHistory: FC = memo( ), accessorKey: 'status', cell: (cellProps) => { - return {cellProps.row.original.status} + return {cellProps.row.original.status} }, - size: compact ? 40 : 100, + size: 30, }, { header: () => ( - + {t('leaderboard.trader-history.table.total-trades')} - + ), accessorKey: 'trades', - size: compact ? 40 : 100, + cell: (cellProps) => ( + {cellProps.getValue()} + ), + size: 130, }, { header: () => ( - + {t('leaderboard.trader-history.table.total-volume')} - + ), accessorKey: 'totalVolume', cell: (cellProps) => ( - + + + ), - size: compact ? 40 : 100, + size: 130, }, { header: () => ( - {t('leaderboard.trader-history.table.total-pnl')} + + {t('leaderboard.trader-history.table.total-pnl')} + ), accessorKey: 'pnl', cell: (cellProps) => ( - + {cellProps.row.original.pnlPct} - + ), - size: compact ? 40 : 100, + size: 130, + }, + { + header: () => ( + + {t('leaderboard.trader-history.table.funding')} + + ), + accessorKey: 'funding', + cell: (cellProps) => ( + + + + ), + size: 130, }, ], }, ]} + noResultsMessage={ + queryStatus.status !== FetchStatus.Loading && + data?.length === 0 && ( + + {t('dashboard.history.positions-history-table.no-result')} + + ) + } /> @@ -246,7 +255,6 @@ const TraderHistory: FC = memo( {cellProps.row.original.marketShortName} - ), size: 50, @@ -257,7 +265,7 @@ const TraderHistory: FC = memo( ), accessorKey: 'status', cell: (cellProps) => { - return {cellProps.row.original.status} + return {cellProps.row.original.status} }, size: 30, }, @@ -267,12 +275,12 @@ const TraderHistory: FC = memo( ), accessorKey: 'pnl', cell: (cellProps) => ( - + {cellProps.row.original.pnlPct} - + ), size: 40, }, @@ -286,6 +294,16 @@ const TraderHistory: FC = memo( } ) +const RightAlignedTableHeader = styled(TableHeader)` + width: 90%; + text-align: right; +` + +const RightAlignedContainer = styled.div` + width: 90%; + text-align: right; +` + const StyledTable = styled(Table)<{ compact?: boolean; height?: number }>` margin-top: ${({ compact }) => (compact ? '0' : '15px')}; height: ${({ height }) => (height ? height + 'px' : 'auto')}; @@ -343,12 +361,6 @@ const StyledSubtitle = styled(Body)` text-transform: capitalize; ` -const PnlContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; -` - const valueColor = css<{ $value: WeiSource }>` color: ${(props) => wei(props.$value).gt(0) @@ -362,12 +374,7 @@ const StyledValue = styled.div<{ $value: WeiSource }>` font-family: ${(props) => props.theme.fonts.mono}; font-size: 13px; margin: 0; - text-align: end; ${valueColor} ` -const StyledFuturesIcon = styled(FuturesIcon)` - margin-left: 5px; -` - export default TraderHistory diff --git a/packages/app/src/sections/futures/Trades/Trades.tsx b/packages/app/src/sections/futures/Trades/Trades.tsx index 8f97f09552..480df99ab8 100644 --- a/packages/app/src/sections/futures/Trades/Trades.tsx +++ b/packages/app/src/sections/futures/Trades/Trades.tsx @@ -244,12 +244,14 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { ) }, + size: 90, }, { header: () => {t('futures.market.user.trades.table.date')}, accessorKey: 'time', cell: (cellProps) => , enableSorting: true, + size: 90, }, { header: () => ( @@ -262,11 +264,14 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { cell: (cellProps) => { return (
- + + {formatDollars(cellProps.getValue(), { suggestDecimals: true })} +
) }, enableSorting: true, + size: 125, }, { header: () => ( @@ -289,6 +294,7 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { ) }, enableSorting: true, + size: 100, }, { header: () => ( @@ -311,6 +317,7 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { ) }, enableSorting: true, + size: 100, }, { header: () => ( @@ -330,6 +337,7 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { ) }, enableSorting: true, + size: 100, }, { header: () => ( @@ -338,7 +346,7 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { accessorKey: 'type', sortingFn: 'basic', cell: (cellProps) => <>{cellProps.getValue()}, - size: 90, + size: 60, }, ]} columnsDeps={columnsDeps} diff --git a/packages/app/src/sections/leaderboard/Leaderboard.tsx b/packages/app/src/sections/leaderboard/Leaderboard.tsx index 752bb8ce2f..e879742dde 100644 --- a/packages/app/src/sections/leaderboard/Leaderboard.tsx +++ b/packages/app/src/sections/leaderboard/Leaderboard.tsx @@ -11,8 +11,12 @@ import { CompetitionRound, COMPETITION_TIERS, PIN, Tier } from 'constants/compet import ROUTES from 'constants/routes' import useENS from 'hooks/useENS' import { CompetitionBanner } from 'sections/shared/components/CompetitionBanner' +import { fetchPositionHistoryForTrader } from 'state/futures/actions' import { setSelectedTrader } from 'state/futures/reducer' -import { selectSelectedTrader } from 'state/futures/selectors' +import { + selectPositionHistoryForSelectedTrader, + selectSelectedTrader, +} from 'state/futures/selectors' import { useAppDispatch, useAppSelector, useFetchAction } from 'state/hooks' import { fetchLeaderboard } from 'state/stats/actions' import { setLeaderboardSearchTerm } from 'state/stats/reducer' @@ -26,7 +30,7 @@ import media from 'styles/media' import AllTime from './AllTime' import Competition from './Competition' -import TraderHistory from './TraderHistory' +import TraderHistory from '../futures/TraderHistory' type LeaderboardProps = { compact?: boolean @@ -50,14 +54,18 @@ const Leaderboard: FC = ({ compact, mobile }) => { const [searchInput, setSearchInput] = useState('') const searchTerm = useAppSelector(selectLeaderboardSearchTerm) const [searchAddress, setSearchAddress] = useState('') + const wallet = useAppSelector(selectWallet) const selectedTrader = useAppSelector(selectSelectedTrader) + const positionHistory = useAppSelector(selectPositionHistoryForSelectedTrader) const searchEns = useENS(searchTerm) const leaderboardLoading = useAppSelector(selectLeaderboardLoading) const leaderboardData = useAppSelector(selectLeaderboard) - const walletAddress = useAppSelector(selectWallet) - useFetchAction(fetchLeaderboard, { dependencies: [searchTerm], disabled: !walletAddress }) + useFetchAction(fetchLeaderboard, { + dependencies: [searchTerm, wallet], + disabled: !wallet, + }) const pinRow = useMemo(() => { return leaderboardData.wallet.map((trader) => ({ ...trader, rank: 0, rankText: PIN })) @@ -146,7 +154,11 @@ const Leaderboard: FC = ({ compact, mobile }) => { useEffect(() => { setSearchAddress(searchEns.ensAddress ?? (isAddress(searchTerm) ? searchTerm : '')) - }, [searchTerm, searchEns]) + }, [searchTerm, searchEns, dispatch, trader]) + + useEffect(() => { + dispatch(fetchPositionHistoryForTrader(trader)) + }, [dispatch, trader]) return ( <> @@ -181,6 +193,7 @@ const Leaderboard: FC = ({ compact, mobile }) => { state.futures, + (networkId, wallet, futures) => { + if (!wallet) return [] + const history = futures.leaderboard.selectedTraderPositionHistory[networkId]?.[wallet] ?? [] + return unserializePositionHistory(history) + } +) + export const selectCrossMarginPositions = createSelector( selectCrossMarginAccountData, selectAllConditionalOrders, diff --git a/packages/app/src/translations/en.json b/packages/app/src/translations/en.json index 596a13d512..b7b0b33af9 100644 --- a/packages/app/src/translations/en.json +++ b/packages/app/src/translations/en.json @@ -704,8 +704,8 @@ }, "history": { "tabs": { - "futures": "Futures History", - "spot": "Spot History" + "positions": "Positions", + "trades": "Trades" }, "spot-history-table": { "date-time": "Date/Time", @@ -715,17 +715,8 @@ "no-trade-history": "You have no synth trading history", "no-trade-history-link": "Visit the Kwenta exchange to swap synths" }, - "futures-history-table": { - "asset": "Asset", - "size": "Size", - "pnl": "P&L", - "date-time": "Date/Time", - "fees": "Fees", - "price": "Price", - "side": "Side", - "market": "Market", - "type": "Type", - "no-result": "You have no futures trading history" + "positions-history-table": { + "no-result": "You have no futures positions history" } }, "deprecated": { @@ -1238,7 +1229,8 @@ "liquidated": "Liquidated", "total-trades": "Trades", "total-volume": "Total Volume", - "total-pnl": "Realized P&L" + "total-pnl": "Realized P&L", + "funding": "Funding" } }, "competition": { From 2af2213cf797bb5a8e52cd37bb2b454413a059d8 Mon Sep 17 00:00:00 2001 From: platschi Date: Tue, 1 Aug 2023 18:21:05 -0300 Subject: [PATCH 12/12] chore(*): bump version --- package.json | 2 +- packages/app/package.json | 2 +- packages/sdk/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 28807300cb..1872dfacd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kwenta", - "version": "7.4.13", + "version": "7.4.14", "description": "Kwenta", "main": "index.js", "scripts": { diff --git a/packages/app/package.json b/packages/app/package.json index f47c43e1d1..57fcadb9e5 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kwenta/app", - "version": "7.4.13", + "version": "7.4.14", "scripts": { "dev": "next", "build": "next build", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index cdb370de0d..288d52fa25 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@kwenta/sdk", - "version": "1.0.3", + "version": "1.0.4", "description": "SDK for headless interaction with Kwenta", "main": "dist/index.js", "directories": {