From 6ea3611246f1734cf4190aae6bae05bd5aa35b36 Mon Sep 17 00:00:00 2001 From: therealemjy Date: Tue, 11 Nov 2025 18:00:52 +0100 Subject: [PATCH] feat: streamline how balance updates are shown on forms --- .changeset/clear-mails-shop.md | 5 + .changeset/quiet-sites-end.md | 5 + .../api/queries/getSimulatedPool/index.ts | 9 +- .../__snapshots__/index.eMode.spec.ts.snap | 52 ++-- .../__snapshots__/index.spec.ts.snap | 104 ++++---- .../getPools/formatOutput/index.ts | 19 +- .../api/queries/useGetPools/getPools/index.ts | 1 + .../src/components/BalanceUpdates/index.tsx | 127 +++++---- .../SelectTokenTextField/TokenList/index.tsx | 65 ++--- .../components/SelectTokenTextField/index.tsx | 17 +- apps/evm/src/components/Slider/index.tsx | 8 +- .../__snapshots__/index.prime.spec.tsx.snap | 25 -- .../__snapshots__/index.spec.tsx.snap | 32 +-- .../__tests__/index.prime.spec.tsx | 101 ------- .../AccountData/__tests__/index.spec.tsx | 74 +---- apps/evm/src/containers/AccountData/index.tsx | 106 ++------ .../containers/AccountData/useGetValues.ts | 240 ----------------- .../__snapshots__/index.spec.tsx.snap | 5 - .../AccountData2/__tests__/index.spec.tsx | 19 -- .../evm/src/containers/AccountData2/index.tsx | 81 ------ apps/evm/src/containers/SwapDetails/index.tsx | 28 +- .../src/hooks/useDelegateApproval/index.ts | 14 +- .../hooks/useFormatTokensToReadableValue.ts | 3 + .../__snapshots__/index.spec.tsx.snap | 252 ------------------ .../__tests__/index.spec.tsx | 115 -------- .../useGetHypotheticalUserPrimeApys/index.ts | 125 --------- .../libs/translations/translations/en.json | 48 ++-- .../__snapshots__/index.spec.tsx.snap | 13 + .../ApyBreakdown/__tests__/index.spec.tsx | 105 ++++++++ .../ApyBreakdown/formatRows/index.tsx | 110 ++++++++ .../OperationForm/ApyBreakdown/index.tsx | 125 +++++++++ .../__snapshots__/index.prime.spec.tsx.snap | 17 -- .../__snapshots__/index.spec.tsx.snap | 9 - .../AssetInfo/__tests__/index.prime.spec.tsx | 66 ----- .../AssetInfo/__tests__/index.spec.tsx | 25 -- .../Market/OperationForm/AssetInfo/index.tsx | 223 ---------------- .../Market/OperationForm/BorrowForm/index.tsx | 45 +++- .../OperationForm/BorrowForm/useForm/types.ts | 1 - .../BorrowForm/useForm/useFormValidation.ts | 2 +- .../OperationDetails/SwapDetails/index.tsx | 2 - .../OperationForm/OperationDetails/index.tsx | 57 ++-- .../Market/OperationForm/RepayForm/index.tsx | 42 ++- .../RepayForm/useForm/useFormValidation.ts | 2 +- .../SupplyForm/__tests__/index.spec.tsx | 36 ++- .../__tests__/indexIntegratedSwap.spec.tsx | 30 ++- .../Market/OperationForm/SupplyForm/index.tsx | 41 ++- .../SupplyForm/useForm/useFormValidation.ts | 2 +- .../OperationForm/WithdrawForm/index.tsx | 43 ++- .../__snapshots__/index.spec.tsx.snap | 2 +- apps/evm/src/pages/Swap/index.tsx | 2 + .../pages/Vai/Borrow/__tests__/index.spec.tsx | 5 +- apps/evm/src/pages/Vai/Borrow/index.tsx | 22 +- apps/evm/src/pages/Vai/Repay/index.tsx | 33 ++- .../src/utilities/convertMantissaToTokens.ts | 2 +- .../src/utilities/convertTokensToMantissa.ts | 2 +- .../getSwapToTokenAmountReceived/index.ts | 7 +- 56 files changed, 930 insertions(+), 1821 deletions(-) create mode 100644 .changeset/clear-mails-shop.md create mode 100644 .changeset/quiet-sites-end.md delete mode 100644 apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.prime.spec.tsx.snap delete mode 100644 apps/evm/src/containers/AccountData/__tests__/index.prime.spec.tsx delete mode 100644 apps/evm/src/containers/AccountData/useGetValues.ts delete mode 100644 apps/evm/src/containers/AccountData2/__tests__/__snapshots__/index.spec.tsx.snap delete mode 100644 apps/evm/src/containers/AccountData2/__tests__/index.spec.tsx delete mode 100644 apps/evm/src/containers/AccountData2/index.tsx delete mode 100644 apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/__snapshots__/index.spec.tsx.snap delete mode 100644 apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/index.spec.tsx delete mode 100644 apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/index.ts create mode 100644 apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/__snapshots__/index.spec.tsx.snap create mode 100644 apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/index.spec.tsx create mode 100644 apps/evm/src/pages/Market/OperationForm/ApyBreakdown/formatRows/index.tsx create mode 100644 apps/evm/src/pages/Market/OperationForm/ApyBreakdown/index.tsx delete mode 100644 apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.prime.spec.tsx.snap delete mode 100644 apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.spec.tsx.snap delete mode 100644 apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.prime.spec.tsx delete mode 100644 apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.spec.tsx delete mode 100644 apps/evm/src/pages/Market/OperationForm/AssetInfo/index.tsx diff --git a/.changeset/clear-mails-shop.md b/.changeset/clear-mails-shop.md new file mode 100644 index 0000000000..535d8a36e1 --- /dev/null +++ b/.changeset/clear-mails-shop.md @@ -0,0 +1,5 @@ +--- +"@venusprotocol/evm": minor +--- + +refactor logic to simulate user operations diff --git a/.changeset/quiet-sites-end.md b/.changeset/quiet-sites-end.md new file mode 100644 index 0000000000..dd1fd5f2ce --- /dev/null +++ b/.changeset/quiet-sites-end.md @@ -0,0 +1,5 @@ +--- +"@venusprotocol/evm": minor +--- + +streamline how balance updates are shown on forms diff --git a/apps/evm/src/clients/api/queries/getSimulatedPool/index.ts b/apps/evm/src/clients/api/queries/getSimulatedPool/index.ts index c570b01122..cf401459d5 100644 --- a/apps/evm/src/clients/api/queries/getSimulatedPool/index.ts +++ b/apps/evm/src/clients/api/queries/getSimulatedPool/index.ts @@ -35,13 +35,16 @@ export const getSimulatedPool = async ({ isUserPrime = false, userXvsStakedMantissa, }: GetSimulatedPoolInput): Promise => { - if (!pool || balanceMutations.length === 0) { + // Filter out 0 balance mutations + const filteredBalanceMutations = balanceMutations.filter(b => !b.amountTokens.isEqualTo(0)); + + if (!pool || filteredBalanceMutations.length === 0) { return { pool: undefined, }; } - const { vaiMutations, assetMutations } = balanceMutations.reduce<{ + const { vaiMutations, assetMutations } = filteredBalanceMutations.reduce<{ vaiMutations: VaiBalanceMutation[]; assetMutations: AssetBalanceMutation[]; }>( @@ -93,7 +96,7 @@ export const getSimulatedPool = async ({ let userBorrowBalanceTokens = asset.userBorrowBalanceTokens; let userBorrowBalanceCents = asset.userBorrowBalanceCents; - balanceMutations.forEach(({ action, amountTokens }) => { + filteredBalanceMutations.forEach(({ action, amountTokens }) => { const amountCents = amountTokens.multipliedBy(asset.tokenPriceCents); switch (action) { diff --git a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.eMode.spec.ts.snap b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.eMode.spec.ts.snap index 3c68db7a34..30b3c76950 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.eMode.spec.ts.snap +++ b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.eMode.spec.ts.snap @@ -135,8 +135,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -222,8 +222,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -281,8 +281,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.6, - "userLiquidationThresholdPercentage": 66, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -367,8 +367,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -453,8 +453,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.81, - "userLiquidationThresholdPercentage": 89.10000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -672,8 +672,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -738,8 +738,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -819,8 +819,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -869,8 +869,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.45, - "userLiquidationThresholdPercentage": 49.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -935,8 +935,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.75, - "userLiquidationThresholdPercentage": 82.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1029,8 +1029,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.65, - "userLiquidationThresholdPercentage": 71.5, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1094,8 +1094,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1160,8 +1160,8 @@ exports[`useGetPools > fetches and formats E-mode groups associated with each po "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", diff --git a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap index 137e9bc9cc..feba2c7742 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap +++ b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap @@ -135,8 +135,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -222,8 +222,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -281,8 +281,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.6, - "userLiquidationThresholdPercentage": 66, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -367,8 +367,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -453,8 +453,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.81, - "userLiquidationThresholdPercentage": 89.10000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -672,8 +672,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -738,8 +738,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -819,8 +819,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -869,8 +869,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.45, - "userLiquidationThresholdPercentage": 49.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -935,8 +935,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.75, - "userLiquidationThresholdPercentage": 82.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1029,8 +1029,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.65, - "userLiquidationThresholdPercentage": 71.5, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1094,8 +1094,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1160,8 +1160,8 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1331,8 +1331,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1418,8 +1418,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1477,8 +1477,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.6, - "userLiquidationThresholdPercentage": 66, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1563,8 +1563,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1649,8 +1649,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.81, - "userLiquidationThresholdPercentage": 89.10000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1859,8 +1859,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -1925,8 +1925,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2006,8 +2006,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2056,8 +2056,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.45, - "userLiquidationThresholdPercentage": 49.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2122,8 +2122,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.75, - "userLiquidationThresholdPercentage": 82.50000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2216,8 +2216,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.65, - "userLiquidationThresholdPercentage": 71.5, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2281,8 +2281,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", @@ -2347,8 +2347,8 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userBorrowBalanceCents": "0", "userBorrowBalanceTokens": "0", "userBorrowLimitSharePercentage": 0, - "userCollateralFactor": 0.8, - "userLiquidationThresholdPercentage": 88.00000000000001, + "userCollateralFactor": 0, + "userLiquidationThresholdPercentage": 0, "userSupplyBalanceCents": "0", "userSupplyBalanceTokens": "0", "userWalletBalanceCents": "0", diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts index 2ac4a49f8f..0e1855e1c0 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts @@ -29,6 +29,7 @@ export const formatOutput = ({ chainId, tokens, currentBlockNumber, + isUserConnected, userPrimeApyMap, userVTokenBalances = [], userTokenBalances = [], @@ -44,6 +45,7 @@ export const formatOutput = ({ currentBlockNumber: bigint; apiPools: ApiPool[]; userPoolEModeGroupIdMapping: Record; + isUserConnected: boolean; userPrimeApyMap?: Map; userCollateralVTokenAddresses?: string[]; userVTokenBalances?: VTokenBalance[]; @@ -147,17 +149,18 @@ export const formatOutput = ({ isBorrowableByUser = userEModeAssetSettings?.isBorrowable ?? false; } + let userFallbackLiquidationThresholdPercentage = 0; + let userFallbackCollateralFactor = 0; + // If the user has enabled a non-isolated E-mode group and that asset is not in it, then it // contributes towards that user's borrow limit using the pool settings. If the E-mode group // enabled by the user is inactive, we also fallback to using the pool settings - let userFallbackLiquidationThresholdPercentage = liquidationThresholdPercentage; - let userFallbackCollateralFactor = collateralFactor; - - // If the user has enabled an isolated E-mode group and that asset is not in it, then it does - // not contribute towards that user's borrow limit - if (userEModeGroup?.isActive && userEModeGroup.isIsolated) { - userFallbackLiquidationThresholdPercentage = 0; - userFallbackCollateralFactor = 0; + if ( + isUserConnected && + (!userEModeGroup || !userEModeGroup.isActive || !userEModeGroup.isIsolated) + ) { + userFallbackLiquidationThresholdPercentage = liquidationThresholdPercentage; + userFallbackCollateralFactor = collateralFactor; } const userCollateralFactor = diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts index cd06d67eaf..ebf9d1914b 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts @@ -164,6 +164,7 @@ export const getPools = async ({ const pools = formatOutput({ chainId, + isUserConnected: !!accountAddress, tokens, currentBlockNumber, apiPools, diff --git a/apps/evm/src/components/BalanceUpdates/index.tsx b/apps/evm/src/components/BalanceUpdates/index.tsx index 26cec7c71b..f6fdeb10d1 100644 --- a/apps/evm/src/components/BalanceUpdates/index.tsx +++ b/apps/evm/src/components/BalanceUpdates/index.tsx @@ -1,7 +1,7 @@ import { LabeledInlineContent, type LabeledInlineContentProps, ValueUpdate } from 'components'; import { useTranslation } from 'libs/translations'; import type { BalanceMutation, Pool } from 'types'; -import { areAddressesEqual, convertMantissaToTokens } from 'utilities'; +import { areAddressesEqual, formatTokensToReadableValue } from 'utilities'; export interface BalanceUpdatesProps { pool: Pool; @@ -16,80 +16,73 @@ export const BalanceUpdates: React.FC = ({ }) => { const { t } = useTranslation(); - const balanceUpdateRows: LabeledInlineContentProps[] = []; + const balanceUpdateRows: LabeledInlineContentProps[] = balanceMutations.reduce< + LabeledInlineContentProps[] + >((acc, balanceMutation) => { + // Skip VAI updates + if (balanceMutation.type === 'vai') { + return acc; + } - if (balanceMutations.length > 0) { - balanceUpdateRows.push( - ...balanceMutations.reduce((acc, balanceMutation) => { - // Skip VAI updates - if (balanceMutation.type === 'vai') { - return acc; - } - - const asset = pool.assets.find(asset => - areAddressesEqual(asset.vToken.address, balanceMutation.vTokenAddress), - ); + const asset = pool.assets.find(asset => + areAddressesEqual(asset.vToken.address, balanceMutation.vTokenAddress), + ); - const simulatedAsset = simulatedPool?.assets.find(asset => - areAddressesEqual(asset.vToken.address, balanceMutation.vTokenAddress), - ); + const simulatedAsset = simulatedPool?.assets.find(asset => + areAddressesEqual(asset.vToken.address, balanceMutation.vTokenAddress), + ); - if (!asset || !simulatedAsset) { - // This case should never happen - return acc; - } + if (!asset) { + // This case should never happen + return acc; + } - let label: undefined | string; - let balanceTokens: undefined | BigNumber; - let simulatedBalanceTokens: undefined | BigNumber; + let label: undefined | string; + let balanceTokens: undefined | BigNumber; + let simulatedBalanceTokens: undefined | BigNumber; - switch (balanceMutation.action) { - case 'supply': - case 'withdraw': - label = t('accountData.balanceUpdate.supplyBalance', { - tokenSymbol: asset.vToken.underlyingToken.symbol, - }); + if (balanceMutation.action === 'borrow' || balanceMutation.action === 'repay') { + label = t('accountData.balanceUpdate.borrowBalance'); - balanceTokens = asset.userSupplyBalanceTokens; - simulatedBalanceTokens = simulatedAsset.userSupplyBalanceTokens; - break; - case 'borrow': - case 'repay': - label = t('accountData.balanceUpdate.borrowBalance', { - tokenSymbol: asset.vToken.underlyingToken.symbol, - }); + balanceTokens = asset.userBorrowBalanceTokens; + simulatedBalanceTokens = simulatedAsset?.userBorrowBalanceTokens; + } else { + label = t('accountData.balanceUpdate.supplyBalance'); - balanceTokens = asset.userBorrowBalanceTokens; - simulatedBalanceTokens = simulatedAsset.userBorrowBalanceCents; - break; - } + balanceTokens = asset.userSupplyBalanceTokens; + simulatedBalanceTokens = simulatedAsset?.userSupplyBalanceTokens; + } - const row: LabeledInlineContentProps = { - iconSrc: asset.vToken.underlyingToken, - label, - children: ( - - ), - }; + const row: LabeledInlineContentProps = { + iconSrc: asset.vToken.underlyingToken, + label, + children: ( + + ), + }; - return [...acc, row]; - }, []), - ); - } + return [...acc, row]; + }, []); - return balanceUpdateRows.map(row => ); + return ( +
+ {balanceUpdateRows.map(row => ( + + ))} +
+ ); }; diff --git a/apps/evm/src/components/SelectTokenTextField/TokenList/index.tsx b/apps/evm/src/components/SelectTokenTextField/TokenList/index.tsx index 2020d811a8..cd520757d5 100644 --- a/apps/evm/src/components/SelectTokenTextField/TokenList/index.tsx +++ b/apps/evm/src/components/SelectTokenTextField/TokenList/index.tsx @@ -2,13 +2,13 @@ import { Typography } from '@mui/material'; import { type InputHTMLAttributes, useMemo, useState } from 'react'; -import { cn } from '@venusprotocol/ui'; import { TokenIconWithSymbol } from 'components/TokenIconWithSymbol'; import { useTranslation } from 'libs/translations'; import type { Token, TokenBalance } from 'types'; -import { convertMantissaToTokens } from 'utilities'; +import { areTokensEqual, convertMantissaToTokens } from 'utilities'; import { SenaryButton } from '@venusprotocol/ui'; +import { Icon } from 'components'; import { TextField } from '../../TextField'; import { useStyles as useParentStyles } from '../styles'; import { getTokenListItemTestId } from '../testIdGetters'; @@ -18,6 +18,8 @@ import { useStyles } from './styles'; export interface TokenListProps { tokenBalances: OptionalTokenBalance[]; onTokenClick: (token: Token) => void; + displayCommonTokenButtons: boolean; + selectedToken: Token; 'data-testid'?: string; } @@ -26,17 +28,17 @@ const commonTokenSymbols = ['XVS', 'BNB', 'USDT', 'BTCB']; export const TokenList: React.FC = ({ tokenBalances, onTokenClick, + displayCommonTokenButtons, + selectedToken, 'data-testid': testId, }) => { const { t } = useTranslation(); const parentStyles = useParentStyles(); const styles = useStyles(); - const commonTokenBalances = useMemo( - () => - tokenBalances.filter(tokenBalance => commonTokenSymbols.includes(tokenBalance.token.symbol)), - [tokenBalances], - ); + const commonTokenBalances = displayCommonTokenButtons + ? tokenBalances.filter(tokenBalance => commonTokenSymbols.includes(tokenBalance.token.symbol)) + : []; const [searchValue, setSearchValue] = useState(''); @@ -85,30 +87,29 @@ export const TokenList: React.FC = ({ return (
-
2 && 'mb-5 pl-3 pr-3 pt-3')}> +
+ + {commonTokenBalances.length > 2 && ( - <> - -
- {commonTokenBalances.map(commonTokenBalance => ( - onTokenClick(commonTokenBalance.token)} - css={styles.commonTokenButton} - key={`select-token-text-field-common-token-${commonTokenBalance.token.symbol}`} - > - - - ))} -
- +
+ {commonTokenBalances.map(commonTokenBalance => ( + onTokenClick(commonTokenBalance.token)} + css={styles.commonTokenButton} + key={`select-token-text-field-common-token-${commonTokenBalance.token.symbol}`} + > + + + ))} +
)}
@@ -139,6 +140,10 @@ export const TokenList: React.FC = ({ })} )} + + {!tokenBalance.balanceMantissa && areTokensEqual(tokenBalance.token, selectedToken) && ( + + )}
))}
diff --git a/apps/evm/src/components/SelectTokenTextField/index.tsx b/apps/evm/src/components/SelectTokenTextField/index.tsx index 694c279179..cf06f64366 100644 --- a/apps/evm/src/components/SelectTokenTextField/index.tsx +++ b/apps/evm/src/components/SelectTokenTextField/index.tsx @@ -2,11 +2,11 @@ import { Typography } from '@mui/material'; import { useState } from 'react'; -import { TokenIconWithSymbol } from 'components/TokenIconWithSymbol'; import type { Token } from 'types'; import { TertiaryButton } from '@venusprotocol/ui'; import { Icon } from '../Icon'; +import { TokenIcon } from '../TokenIcon'; import { TokenTextField, type TokenTextFieldProps } from '../TokenTextField'; import TokenList from './TokenList'; import { useStyles } from './styles'; @@ -17,10 +17,14 @@ import { } from './testIdGetters'; import type { OptionalTokenBalance } from './types'; +export * from './types'; + export interface SelectTokenTextFieldProps extends Omit { - selectedToken: Token; tokenBalances: OptionalTokenBalance[]; + selectedToken: Token; onChangeSelectedToken: (token: Token) => void; + token?: Token; + displayCommonTokenButtons?: boolean; 'data-testid'?: string; } @@ -35,6 +39,7 @@ export const SelectTokenTextField: React.FC = ({ rightMaxButton, 'data-testid': testId, description, + displayCommonTokenButtons = false, ...otherTokenTextFieldProps }) => { const styles = useStyles(); @@ -64,7 +69,11 @@ export const SelectTokenTextField: React.FC = ({ disabled={disabled} data-testid={!!testId && getTokenSelectButtonTestId({ parentTestId: testId })} > - +
+ + +
{selectedToken.symbol}
+
= ({ tokenBalances={tokenBalances} data-testid={testId} onTokenClick={handleChangeSelectedToken} + selectedToken={selectedToken} + displayCommonTokenButtons={displayCommonTokenButtons} /> )} diff --git a/apps/evm/src/components/Slider/index.tsx b/apps/evm/src/components/Slider/index.tsx index 5a7a61b29d..fc222c99d8 100644 --- a/apps/evm/src/components/Slider/index.tsx +++ b/apps/evm/src/components/Slider/index.tsx @@ -7,6 +7,7 @@ export interface SliderProps { step: number; max: number; min?: number; + disabled?: boolean; className?: string; } @@ -16,6 +17,7 @@ export const Slider: React.FC = ({ min = 0, max, step, + disabled = false, className, }) => ( = ({ step={step} onValueChange={([newValue]) => onChange(newValue)} className={cn('relative flex w-full touch-none items-center select-none', className)} + disabled={disabled} > = ({ ); diff --git a/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.prime.spec.tsx.snap b/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.prime.spec.tsx.snap deleted file mode 100644 index 0d67b40387..0000000000 --- a/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.prime.spec.tsx.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`AccountData - Feature flag enabled: Prime > renders correct values when using swap: { action: 'borrow' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.61HealthyDaily earnings$0.04$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values when using swap: { action: 'repay' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values when using swap: { action: 'supply' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.07"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values when using swap: { action: 'withdraw' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.07Supply balance$99.99$99.96"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'borrow', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'borrow', amountToken: 50 } 1`] = `"Borrow limit used:9.00%Limit:$1.92K.Health factor15.6211.11HealthyDaily earnings$0.04$0.06"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'repay', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'repay', amountToken: 50 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'supply', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'supply', amountToken: 50 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.1"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'withdraw', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04Supply balance$99.99"`; - -exports[`AccountData - Feature flag enabled: Prime > renders correct values: { action: 'withdraw', amountToken: 50 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.05Supply balance$99.99$49.99"`; diff --git a/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.spec.tsx.snap index 63340a6727..c93957e59f 100644 --- a/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/containers/AccountData/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,33 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AccountData > renders correct values when using swap: { action: 'borrow' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.61HealthyDaily earnings$0.04$0.04"`; +exports[`AccountData > renders correctly 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$1"`; -exports[`AccountData > renders correct values when using swap: { action: 'repay' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values when using swap: { action: 'supply' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.04"`; - -exports[`AccountData > renders correct values when using swap: { action: 'withdraw' } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$0.04$0.04Supply balance$115.08$115.04"`; - -exports[`AccountData > renders correct values: { action: 'borrow', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values: { action: 'borrow', amountToken: 10 } 1`] = `"Borrow limit used:7.06%Limit:$1.92K.Health factor15.6214.15HealthyDaily earnings$0.04$0.04"`; - -exports[`AccountData > renders correct values: { action: 'borrow', amountToken: 100 } 1`] = `"Borrow limit used:13.04%Limit:$1.92K.Health factor15.627.67HealthyDaily earnings$0.04$0.06"`; - -exports[`AccountData > renders correct values: { action: 'repay', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values: { action: 'repay', amountToken: 10 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values: { action: 'repay', amountToken: 100 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values: { action: 'supply', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04"`; - -exports[`AccountData > renders correct values: { action: 'supply', amountToken: 10 } 1`] = `"Borrow limit used:6.38%Limit:$1.93K.Health factor15.6215.67HealthyDaily earnings$0.04$0.04"`; - -exports[`AccountData > renders correct values: { action: 'supply', amountToken: 100 } 1`] = `"Borrow limit used:6.20%Limit:$1.99K.Health factor15.6216.14HealthyDaily earnings$0.04$0.04"`; - -exports[`AccountData > renders correct values: { action: 'withdraw', amountToken: +0 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04Supply balance$115.08"`; - -exports[`AccountData > renders correct values: { action: 'withdraw', amountToken: 10 } 1`] = `"Borrow limit used:6.42%Limit:$1.92K.Health factor15.6215.57HealthyDaily earnings$0.04$0.04Supply balance$115.08$102.29"`; - -exports[`AccountData > renders correct values: { action: 'withdraw', amountToken: 100 } 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$0.04Supply balance$115.08"`; +exports[`AccountData > renders correctly when passing a simulated pool 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$1$1"`; diff --git a/apps/evm/src/containers/AccountData/__tests__/index.prime.spec.tsx b/apps/evm/src/containers/AccountData/__tests__/index.prime.spec.tsx deleted file mode 100644 index bad0a2a83b..0000000000 --- a/apps/evm/src/containers/AccountData/__tests__/index.prime.spec.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import BigNumber from 'bignumber.js'; -import type { Mock } from 'vitest'; - -import { poolData } from '__mocks__/models/pools'; -import { renderComponent } from 'testUtils/render'; - -import { - useGetHypotheticalPrimeApys, - useGetPrimeStatus, - useGetXvsVaultUserInfo, -} from 'clients/api'; - -import { exactAmountInSwap } from '__mocks__/models/swaps'; -import { type UseIsFeatureEnabledInput, useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; -import { AccountData, type AccountDataProps } from '..'; - -describe('AccountData - Feature flag enabled: Prime', () => { - beforeEach(() => { - (useIsFeatureEnabled as Mock).mockImplementation( - ({ name }: UseIsFeatureEnabledInput) => name === 'prime', - ); - - (useGetPrimeStatus as Mock).mockImplementation(() => ({ - data: { - xvsVaultPoolId: 1, - }, - })); - - (useGetXvsVaultUserInfo as Mock).mockImplementation(() => ({ - data: { - stakedAmountMantissa: new BigNumber('1000000000000000'), - }, - })); - - (useGetHypotheticalPrimeApys as Mock).mockImplementation(() => ({ - data: { - supplyApyPercentage: new BigNumber(13.4), - borrowApyPercentage: new BigNumber(10.4), - }, - })); - }); - - it('renders without crashing', async () => { - renderComponent( - , - ); - }); - - it.each([ - { action: 'supply', amountToken: 0 }, - { action: 'supply', amountToken: 50 }, - { action: 'withdraw', amountToken: 0 }, - { action: 'withdraw', amountToken: 50 }, - { action: 'borrow', amountToken: 0 }, - { action: 'borrow', amountToken: 50 }, - { action: 'repay', amountToken: 0 }, - { action: 'repay', amountToken: 50 }, - ] as { action: AccountDataProps['action']; amountToken: number }[])( - 'renders correct values: %s', - async ({ action, amountToken }) => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }, - ); - - it.each([ - { action: 'supply' }, - { action: 'withdraw' }, - { action: 'borrow' }, - { action: 'repay' }, - ] as { action: AccountDataProps['action'] }[])( - 'renders correct values when using swap: %s', - async ({ action }) => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }, - ); -}); diff --git a/apps/evm/src/containers/AccountData/__tests__/index.spec.tsx b/apps/evm/src/containers/AccountData/__tests__/index.spec.tsx index 0fd747867f..674ef999ce 100644 --- a/apps/evm/src/containers/AccountData/__tests__/index.spec.tsx +++ b/apps/evm/src/containers/AccountData/__tests__/index.spec.tsx @@ -1,71 +1,19 @@ -import BigNumber from 'bignumber.js'; - import { poolData } from '__mocks__/models/pools'; -import { exactAmountInSwap } from '__mocks__/models/swaps'; import { renderComponent } from 'testUtils/render'; -import { AccountData, type AccountDataProps } from '..'; +import { AccountData } from '..'; describe('AccountData', () => { - it('renders without crashing', async () => { - renderComponent( - , - ); - }); - - it.each([ - { action: 'supply', amountToken: 0 }, - { action: 'supply', amountToken: 10 }, - { action: 'supply', amountToken: 100 }, - { action: 'withdraw', amountToken: 0 }, - { action: 'withdraw', amountToken: 10 }, - { action: 'withdraw', amountToken: 100 }, - { action: 'borrow', amountToken: 0 }, - { action: 'borrow', amountToken: 10 }, - { action: 'borrow', amountToken: 100 }, - { action: 'repay', amountToken: 100 }, - { action: 'repay', amountToken: 10 }, - { action: 'repay', amountToken: 0 }, - ] as { action: AccountDataProps['action']; amountToken: number }[])( - 'renders correct values: %s', - async ({ action, amountToken }) => { - const { container } = renderComponent( - , - ); + it('renders correctly', async () => { + const { container } = renderComponent(); - expect(container.textContent).toMatchSnapshot(); - }, - ); + expect(container.textContent).toMatchSnapshot(); + }); - it.each([ - { action: 'supply' }, - { action: 'withdraw' }, - { action: 'borrow' }, - { action: 'repay' }, - ] as { action: AccountDataProps['action'] }[])( - 'renders correct values when using swap: %s', - async ({ action }) => { - const { container } = renderComponent( - , - ); + it('renders correctly when passing a simulated pool', async () => { + const { container } = renderComponent( + , + ); - expect(container.textContent).toMatchSnapshot(); - }, - ); + expect(container.textContent).toMatchSnapshot(); + }); }); diff --git a/apps/evm/src/containers/AccountData/index.tsx b/apps/evm/src/containers/AccountData/index.tsx index 854ed450c4..0fdcd6bd55 100644 --- a/apps/evm/src/containers/AccountData/index.tsx +++ b/apps/evm/src/containers/AccountData/index.tsx @@ -1,74 +1,44 @@ -import type BigNumber from 'bignumber.js'; - import { cn } from '@venusprotocol/ui'; import { AccountHealthBar, HealthFactorPill, LabeledInlineContent, ValueUpdate } from 'components'; import PLACEHOLDER_KEY from 'constants/placeholderKey'; import { useTranslation } from 'libs/translations'; import { memo } from 'react'; -import type { Asset, Pool, Swap, TokenAction } from 'types'; -import { formatCentsToReadableValue } from 'utilities'; -import useGetValues from './useGetValues'; +import type { Pool } from 'types'; +import { calculateDailyEarningsCents, formatCentsToReadableValue } from 'utilities'; + +const formatToReadableDailyEarnings = ({ + yearlyEarningsCents, +}: { yearlyEarningsCents: BigNumber | undefined }) => { + const dailyEarningsCents = + yearlyEarningsCents && calculateDailyEarningsCents(yearlyEarningsCents); + + return formatCentsToReadableValue({ value: dailyEarningsCents }); +}; const MemoizedAccountHealthBar = memo(AccountHealthBar); export interface AccountDataProps { - asset: Asset; pool: Pool; - action: TokenAction; - amountTokens: BigNumber; - isUsingSwap?: boolean; - swap?: Swap; + simulatedPool?: Pool; className?: string; } -export const AccountData: React.FC = ({ - asset, - pool, - action, - amountTokens, - isUsingSwap = false, - swap, - className, -}) => { +export const AccountData: React.FC = ({ pool, simulatedPool, className }) => { const { t } = useTranslation(); - const { - poolUserDailyEarningsCents, - hypotheticalPoolUserHealthFactor, - hypotheticalPoolUserDailyEarningsCents, - hypotheticalPoolUserBorrowBalanceCents, - hypotheticalAssetUserSupplyBalanceCents, - hypotheticalAssetUserBorrowBalanceCents, - hypotheticalPoolUserBorrowLimitCents, - } = useGetValues({ asset, pool, swap, amountTokens, action, isUsingSwap }); + const refPool = simulatedPool ?? pool; const shouldShowHealth = - pool.userBorrowBalanceCents?.isGreaterThan(0) || - hypotheticalPoolUserBorrowBalanceCents?.isGreaterThan(0); - - const hypotheticalUserBorrowBalanceCents = - hypotheticalPoolUserBorrowBalanceCents || pool.userBorrowBalanceCents; - - const hypotheticalUserBorrowLimitCents = - hypotheticalPoolUserBorrowLimitCents || pool.userBorrowLimitCents; - - const shouldShowSupplyBalance = - action === 'withdraw' && - (asset.userSupplyBalanceCents.isGreaterThan(0) || - !!hypotheticalAssetUserSupplyBalanceCents?.isGreaterThan(0)); - - const shouldShowRepayBalance = - action === 'repay' && - (asset.userBorrowBalanceCents.isGreaterThan(0) || - !!hypotheticalAssetUserBorrowBalanceCents?.isGreaterThan(0)); + !!pool.userBorrowBalanceCents?.isGreaterThan(0) || + !!simulatedPool?.userBorrowLimitCents?.isGreaterThan(0); return (
{shouldShowHealth && (
= ({ pool.userHealthFactor !== undefined ? ( ) : ( PLACEHOLDER_KEY ) } update={ - hypotheticalPoolUserHealthFactor !== undefined ? ( - + simulatedPool?.userHealthFactor !== undefined ? ( + ) : undefined } /> @@ -98,37 +68,17 @@ export const AccountData: React.FC = ({ - - {shouldShowSupplyBalance && ( - - - - )} - - {shouldShowRepayBalance && ( - - - - )}
); }; diff --git a/apps/evm/src/containers/AccountData/useGetValues.ts b/apps/evm/src/containers/AccountData/useGetValues.ts deleted file mode 100644 index 3d36490836..0000000000 --- a/apps/evm/src/containers/AccountData/useGetValues.ts +++ /dev/null @@ -1,240 +0,0 @@ -import BigNumber from 'bignumber.js'; -import { useMemo } from 'react'; - -import type { Asset, Pool, Swap, TokenAction } from 'types'; -import { - areTokensEqual, - calculateDailyEarningsCents, - calculateHealthFactor, - calculateYearlyEarningsForAssets, - convertMantissaToTokens, - convertTokensToMantissa, - getSwapToTokenAmountReceivedTokens, -} from 'utilities'; - -import { useGetHypotheticalUserPrimeApys } from 'hooks/useGetHypotheticalUserPrimeApys'; - -export interface UseGetValuesInput { - asset: Asset; - pool: Pool; - action: TokenAction; - amountTokens: BigNumber; - isUsingSwap: boolean; - swap?: Swap; -} - -export interface UseGetValuesOutput { - poolUserDailyEarningsCents: BigNumber | undefined; - hypotheticalPoolUserHealthFactor: number | undefined; - hypotheticalPoolUserDailyEarningsCents: BigNumber | undefined; - hypotheticalPoolUserBorrowBalanceCents: BigNumber | undefined; - hypotheticalPoolUserBorrowLimitCents: BigNumber | undefined; - hypotheticalAssetUserSupplyBalanceCents: BigNumber | undefined; - hypotheticalAssetUserBorrowBalanceCents: BigNumber | undefined; -} - -const useGetValues = ({ - asset, - pool, - swap, - action, - isUsingSwap, - amountTokens, -}: UseGetValuesInput): UseGetValuesOutput => { - const toTokenAmountTokens = useMemo(() => { - if (isUsingSwap) { - return ( - getSwapToTokenAmountReceivedTokens(swap)?.swapToTokenAmountReceivedTokens ?? - new BigNumber(0) - ); - } - - return amountTokens; - }, [swap, amountTokens, isUsingSwap]); - - const hypotheticalUserPrimeApys = useGetHypotheticalUserPrimeApys({ - asset, - action, - toTokenAmountTokens, - }); - - return useMemo(() => { - const poolUserYearlyEarningsCents = calculateYearlyEarningsForAssets({ - assets: pool.assets, - }); - - const poolUserDailyEarningsCents = - poolUserYearlyEarningsCents && calculateDailyEarningsCents(poolUserYearlyEarningsCents); - - const returnValues: UseGetValuesOutput = { - poolUserDailyEarningsCents, - hypotheticalPoolUserHealthFactor: undefined, - hypotheticalPoolUserDailyEarningsCents: undefined, - hypotheticalPoolUserBorrowBalanceCents: undefined, - hypotheticalPoolUserBorrowLimitCents: undefined, - hypotheticalAssetUserSupplyBalanceCents: undefined, - hypotheticalAssetUserBorrowBalanceCents: undefined, - }; - - const isImpossibleWithdrawAction = - action === 'withdraw' && asset.userSupplyBalanceTokens.isLessThan(toTokenAmountTokens); - const isImpossibleRepayAction = - action === 'repay' && asset.userBorrowBalanceTokens.isLessThan(toTokenAmountTokens); - - if ( - toTokenAmountTokens.isEqualTo(0) || - isImpossibleWithdrawAction || - isImpossibleRepayAction || - // Check we have sufficient data - pool.userBorrowBalanceCents === undefined || - pool.userLiquidationThresholdCents === undefined || - pool.userSupplyBalanceCents === undefined || - pool.userBorrowLimitCents === undefined - ) { - return returnValues; - } - - const toTokenAmountCents = convertMantissaToTokens({ - value: convertTokensToMantissa({ - value: toTokenAmountTokens, - token: asset.vToken.underlyingToken, - }), - token: asset.vToken.underlyingToken, - }).times(asset.tokenPriceCents); - - const amountLiquidationThresholdValueCents = asset.isCollateralOfUser - ? toTokenAmountCents.times(asset.userLiquidationThresholdPercentage / 100) - : new BigNumber(0); - - const amountCollateralValueCents = asset.isCollateralOfUser - ? toTokenAmountCents.times(asset.userCollateralFactor) - : new BigNumber(0); - - let hypotheticalUserSupplyBalanceTokens: BigNumber | undefined; - let hypotheticalUserBorrowBalanceTokens: BigNumber | undefined; - let hypotheticalPoolUserLiquidationThresholdCents: BigNumber | undefined; - - if (action === 'supply') { - hypotheticalUserSupplyBalanceTokens = asset.userSupplyBalanceTokens.plus(toTokenAmountTokens); - - returnValues.hypotheticalPoolUserBorrowLimitCents = amountCollateralValueCents.plus( - pool.userBorrowLimitCents, - ); - - hypotheticalPoolUserLiquidationThresholdCents = pool.userLiquidationThresholdCents.plus( - amountLiquidationThresholdValueCents, - ); - } else if (action === 'withdraw') { - hypotheticalUserSupplyBalanceTokens = - asset.userSupplyBalanceTokens.minus(toTokenAmountTokens); - - returnValues.hypotheticalPoolUserBorrowLimitCents = pool.userBorrowLimitCents.minus( - amountCollateralValueCents, - ); - - returnValues.hypotheticalAssetUserSupplyBalanceCents = - hypotheticalUserSupplyBalanceTokens.multipliedBy(asset.tokenPriceCents); - - hypotheticalPoolUserLiquidationThresholdCents = pool.userLiquidationThresholdCents.minus( - amountLiquidationThresholdValueCents, - ); - } else if (action === 'borrow') { - hypotheticalUserBorrowBalanceTokens = asset.userBorrowBalanceTokens.plus(toTokenAmountTokens); - - returnValues.hypotheticalPoolUserBorrowBalanceCents = toTokenAmountTokens - .multipliedBy(asset.tokenPriceCents) - .plus(pool.userBorrowBalanceCents); - } else if (action === 'repay') { - hypotheticalUserBorrowBalanceTokens = - asset.userBorrowBalanceTokens.minus(toTokenAmountTokens); - - returnValues.hypotheticalAssetUserBorrowBalanceCents = - hypotheticalUserBorrowBalanceTokens.multipliedBy(asset.tokenPriceCents); - - returnValues.hypotheticalPoolUserBorrowBalanceCents = pool.userBorrowBalanceCents.minus( - toTokenAmountTokens.multipliedBy(asset.tokenPriceCents), - ); - } - - const hypotheticalAssets = pool.assets.map(a => { - if (!areTokensEqual(a.vToken, asset.vToken)) { - return a; - } - - const userSupplyBalanceTokens = - hypotheticalUserSupplyBalanceTokens || asset.userSupplyBalanceTokens; - const userSupplyBalanceCents = userSupplyBalanceTokens.multipliedBy(asset.tokenPriceCents); - - const userBorrowBalanceTokens = - hypotheticalUserBorrowBalanceTokens || asset.userBorrowBalanceTokens; - const userBorrowBalanceCents = userBorrowBalanceTokens.multipliedBy(asset.tokenPriceCents); - - // Include hypothetical Prime distributions - const borrowTokenDistributions = a.borrowTokenDistributions.map(borrowDistribution => { - if ( - borrowDistribution.type !== 'prime' || - !hypotheticalUserPrimeApys.borrowApy || - (action !== 'borrow' && action !== 'repay') - ) { - return borrowDistribution; - } - - return { - ...borrowDistribution, - apyPercentage: hypotheticalUserPrimeApys.borrowApy, - }; - }); - - const supplyTokenDistributions = a.supplyTokenDistributions.map(supplyDistribution => { - if ( - supplyDistribution.type !== 'prime' || - !hypotheticalUserPrimeApys.supplyApy || - (action !== 'supply' && action !== 'withdraw') - ) { - return supplyDistribution; - } - - return { - ...supplyDistribution, - apyPercentage: hypotheticalUserPrimeApys.supplyApy, - }; - }); - - return { - ...a, - borrowTokenDistributions, - supplyTokenDistributions, - userSupplyBalanceTokens, - userSupplyBalanceCents, - userBorrowBalanceTokens, - userBorrowBalanceCents, - }; - }); - - // Calculate hypothetical earnings - const borrowBalanceCents = returnValues.hypotheticalPoolUserBorrowBalanceCents - ? returnValues.hypotheticalPoolUserBorrowBalanceCents.toNumber() - : pool.userBorrowBalanceCents.toNumber(); - - const liquidationThresholdCents = hypotheticalPoolUserLiquidationThresholdCents - ? hypotheticalPoolUserLiquidationThresholdCents.toNumber() - : pool.userLiquidationThresholdCents.toNumber(); - - const hypotheticalUserYearlyEarningsCents = calculateYearlyEarningsForAssets({ - assets: hypotheticalAssets, - }); - - returnValues.hypotheticalPoolUserDailyEarningsCents = - hypotheticalUserYearlyEarningsCents && - calculateDailyEarningsCents(hypotheticalUserYearlyEarningsCents); - - returnValues.hypotheticalPoolUserHealthFactor = calculateHealthFactor({ - borrowBalanceCents, - liquidationThresholdCents, - }); - - return returnValues; - }, [asset, pool, action, toTokenAmountTokens, hypotheticalUserPrimeApys]); -}; - -export default useGetValues; diff --git a/apps/evm/src/containers/AccountData2/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/containers/AccountData2/__tests__/__snapshots__/index.spec.tsx.snap deleted file mode 100644 index c93957e59f..0000000000 --- a/apps/evm/src/containers/AccountData2/__tests__/__snapshots__/index.spec.tsx.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`AccountData > renders correctly 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.62HealthyDaily earnings$1"`; - -exports[`AccountData > renders correctly when passing a simulated pool 1`] = `"Borrow limit used:6.4%Limit:$1.92K.Health factor15.6215.62HealthyDaily earnings$1$1"`; diff --git a/apps/evm/src/containers/AccountData2/__tests__/index.spec.tsx b/apps/evm/src/containers/AccountData2/__tests__/index.spec.tsx deleted file mode 100644 index 674ef999ce..0000000000 --- a/apps/evm/src/containers/AccountData2/__tests__/index.spec.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { poolData } from '__mocks__/models/pools'; -import { renderComponent } from 'testUtils/render'; -import { AccountData } from '..'; - -describe('AccountData', () => { - it('renders correctly', async () => { - const { container } = renderComponent(); - - expect(container.textContent).toMatchSnapshot(); - }); - - it('renders correctly when passing a simulated pool', async () => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }); -}); diff --git a/apps/evm/src/containers/AccountData2/index.tsx b/apps/evm/src/containers/AccountData2/index.tsx deleted file mode 100644 index 33e8817c24..0000000000 --- a/apps/evm/src/containers/AccountData2/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { cn } from '@venusprotocol/ui'; -import { AccountHealthBar, HealthFactorPill, LabeledInlineContent, ValueUpdate } from 'components'; -import PLACEHOLDER_KEY from 'constants/placeholderKey'; -import { useTranslation } from 'libs/translations'; -import { memo } from 'react'; -import type { Pool } from 'types'; -import { calculateDailyEarningsCents, formatCentsToReadableValue } from 'utilities'; - -const formatToReadableDailyEarnings = ({ - yearlyEarningsCents, -}: { yearlyEarningsCents: BigNumber | undefined }) => { - const dailyEarningsCents = - yearlyEarningsCents && calculateDailyEarningsCents(yearlyEarningsCents); - - return formatCentsToReadableValue({ value: dailyEarningsCents }); -}; - -const MemoizedAccountHealthBar = memo(AccountHealthBar); - -export interface AccountDataProps { - pool: Pool; - simulatedPool?: Pool; - className?: string; -} - -export const AccountData: React.FC = ({ pool, simulatedPool, className }) => { - const { t } = useTranslation(); - - const shouldShowHealth = - !!pool.userBorrowBalanceCents?.isGreaterThan(0) || - !!simulatedPool?.userBorrowLimitCents?.isGreaterThan(0); - - return ( -
- {shouldShowHealth && ( -
- - - - - ) : ( - PLACEHOLDER_KEY - ) - } - update={ - simulatedPool?.userHealthFactor !== undefined ? ( - - ) : undefined - } - /> - -
- )} - - - -
- ); -}; diff --git a/apps/evm/src/containers/SwapDetails/index.tsx b/apps/evm/src/containers/SwapDetails/index.tsx index 656fdb6266..9834e0b88f 100644 --- a/apps/evm/src/containers/SwapDetails/index.tsx +++ b/apps/evm/src/containers/SwapDetails/index.tsx @@ -1,6 +1,7 @@ import { TertiaryButton, cn } from '@venusprotocol/ui'; import { Icon, LabeledInlineContent, Modal, TextField, type TextFieldProps } from 'components'; +import PLACEHOLDER_KEY from 'constants/placeholderKey'; import { DEFAULT_SLIPPAGE_TOLERANCE_PERCENTAGE, HIGH_PRICE_IMPACT_THRESHOLD_PERCENTAGE, @@ -18,18 +19,19 @@ import { export const slippageToleranceOptions = ['0.1', String(DEFAULT_SLIPPAGE_TOLERANCE_PERCENTAGE), '1']; const MAX_SLIPPAGE_TOLERANCE_DECIMALS = 2; -export interface SwapDetailsProps { - exchangeRate: BigNumber; +export interface SwapDetailsProps extends React.HTMLAttributes { fromToken: Token; toToken: Token; - priceImpactPercentage: number; + exchangeRate?: BigNumber; + priceImpactPercentage?: number; } export const SwapDetails: React.FC = ({ - exchangeRate, fromToken, toToken, + exchangeRate, priceImpactPercentage, + ...otherProps }) => { const { t } = useTranslation(); @@ -78,13 +80,15 @@ export const SwapDetails: React.FC = ({ return ( <> -
+
- {t('swapDetails.exchangeRate.value', { - fromTokenSymbol: fromToken.symbol, - toTokenSymbol: toToken.symbol, - rate: readableExchangeRate, - })} + {exchangeRate !== undefined + ? t('swapDetails.exchangeRate.value', { + fromTokenSymbol: fromToken.symbol, + toTokenSymbol: toToken.symbol, + rate: readableExchangeRate, + }) + : PLACEHOLDER_KEY} @@ -106,7 +110,9 @@ export const SwapDetails: React.FC = ({ = HIGH_PRICE_IMPACT_THRESHOLD_PERCENTAGE && 'text-red', + priceImpactPercentage !== undefined && + priceImpactPercentage >= HIGH_PRICE_IMPACT_THRESHOLD_PERCENTAGE && + 'text-red', )} > {readablePriceImpact} diff --git a/apps/evm/src/hooks/useDelegateApproval/index.ts b/apps/evm/src/hooks/useDelegateApproval/index.ts index c94f67b6b0..df44a4def2 100644 --- a/apps/evm/src/hooks/useDelegateApproval/index.ts +++ b/apps/evm/src/hooks/useDelegateApproval/index.ts @@ -11,7 +11,7 @@ export interface UseDelegateApprovalInput { export interface UseDelegateApprovalOutput { updatePoolDelegateStatus: ({ approvedStatus }: { approvedStatus: boolean }) => Promise; - isUseUpdatePoolDelegateStatusLoading: boolean; + isDelegateStatusLoading: boolean; isDelegateApproved: boolean | undefined; isDelegateApprovedLoading: boolean; } @@ -23,12 +23,10 @@ const useDelegateApproval = ({ }: UseDelegateApprovalInput): UseDelegateApprovalOutput => { const { accountAddress } = useAccountAddress(); - const { - mutateAsync: updatePoolDelegateStatusMutation, - isPending: isUseUpdatePoolDelegateStatusLoading, - } = useUpdatePoolDelegateStatus({ - waitForConfirmation: true, - }); + const { mutateAsync: updatePoolDelegateStatusMutation, isPending: isDelegateStatusLoading } = + useUpdatePoolDelegateStatus({ + waitForConfirmation: true, + }); const updatePoolDelegateStatus = (input: { approvedStatus: boolean }) => updatePoolDelegateStatusMutation({ @@ -53,7 +51,7 @@ const useDelegateApproval = ({ return { updatePoolDelegateStatus, - isUseUpdatePoolDelegateStatusLoading, + isDelegateStatusLoading, isDelegateApproved, isDelegateApprovedLoading, }; diff --git a/apps/evm/src/hooks/useFormatTokensToReadableValue.ts b/apps/evm/src/hooks/useFormatTokensToReadableValue.ts index 46583e7f68..54cac2be7d 100644 --- a/apps/evm/src/hooks/useFormatTokensToReadableValue.ts +++ b/apps/evm/src/hooks/useFormatTokensToReadableValue.ts @@ -5,6 +5,9 @@ import { type FormatTokensToReadableValueInput, formatTokensToReadableValue } fr export type UseFormatTokensToReadableValueInput = FormatTokensToReadableValueInput; +/** + * @deprecated Use formatTokensToReadableValue instead + */ const useFormatTokensToReadableValue = (params: UseFormatTokensToReadableValueInput) => useMemo(() => (params.value ? formatTokensToReadableValue(params) : PLACEHOLDER_KEY), [params]); diff --git a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/__snapshots__/index.spec.tsx.snap deleted file mode 100644 index c3711471d1..0000000000 --- a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/__snapshots__/index.spec.tsx.snap +++ /dev/null @@ -1,252 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'borrow', amountTokens: +0 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'borrow', amountTokens: +0 } 2`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'borrow', amountTokens: 50 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "50000000", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'borrow', amountTokens: 50 } 2`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'repay', amountTokens: +0 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'repay', amountTokens: +0 } 2`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'repay', amountTokens: 50 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'repay', amountTokens: 50 } 2`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'supply', amountTokens: +0 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'supply', amountTokens: +0 } 2`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'supply', amountTokens: 50 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "150000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'supply', amountTokens: 50 } 2`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'swapAndSupply', amountTokens: +0 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'swapAndSupply', amountTokens: +0 } 2`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'swapAndSupply', amountTokens: 50 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "150000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'swapAndSupply', amountTokens: 50 } 2`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'withdraw', amountTokens: +0 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "100000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'withdraw', amountTokens: +0 } 2`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'withdraw', amountTokens: 50 } 1`] = ` -[ - { - "accountAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848", - "userBorrowBalanceMantissa": "0", - "userSupplyBalanceMantissa": "50000000", - "userXvsStakedMantissa": "1000000000000000", - "vTokenAddress": "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - }, - { - "enabled": true, - "placeholderData": [Function], - }, -] -`; - -exports[`useGetHypotheticalUserPrimeApys > returns correct values: { action: 'withdraw', amountTokens: 50 } 2`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns undefined when connected user does not hold a Prime token 1`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns undefined when passed toTokenAmountTokens parameter equals 0 1`] = ` -{ - "borrowApy": undefined, - "supplyApy": undefined, -} -`; - -exports[`useGetHypotheticalUserPrimeApys > returns undefined when user is disconnected 1`] = ` -{ - "borrowApy": "10.4", - "supplyApy": "13.4", -} -`; diff --git a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/index.spec.tsx b/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/index.spec.tsx deleted file mode 100644 index 3028649f93..0000000000 --- a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/__tests__/index.spec.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import type { Mock } from 'vitest'; - -import fakeAccountAddress from '__mocks__/models/address'; -import { assetData } from '__mocks__/models/asset'; -import BigNumber from 'bignumber.js'; -import { - useGetHypotheticalPrimeApys, - useGetPrimeStatus, - useGetXvsVaultUserInfo, -} from 'clients/api'; -import { renderHook } from 'testUtils/render'; -import type { TokenAction } from 'types'; -import { useGetHypotheticalUserPrimeApys } from '..'; - -const fakeAsset = assetData[0]; -const fakeAssetWithPrimeDistribution = assetData[1]; - -describe('useGetHypotheticalUserPrimeApys', () => { - beforeEach(() => { - (useGetPrimeStatus as Mock).mockImplementation(() => ({ - data: { - xvsVaultPoolId: 1, - }, - })); - - (useGetXvsVaultUserInfo as Mock).mockImplementation(() => ({ - data: { - stakedAmountMantissa: new BigNumber('1000000000000000'), - }, - })); - - (useGetHypotheticalPrimeApys as Mock).mockImplementation(() => ({ - data: { - supplyApyPercentage: new BigNumber(13.4), - borrowApyPercentage: new BigNumber(10.4), - }, - })); - }); - - it('returns undefined when user is disconnected', async () => { - const { result } = renderHook(() => - useGetHypotheticalUserPrimeApys({ - asset: fakeAssetWithPrimeDistribution, - action: 'supply', - toTokenAmountTokens: new BigNumber(10), - }), - ); - - expect(result.current).toMatchSnapshot(); - }); - - it('returns undefined when connected user does not hold a Prime token', async () => { - const { result } = renderHook( - () => - useGetHypotheticalUserPrimeApys({ - asset: fakeAsset, // The hook detects that a user is not Prime by the fact the asset contains no distribution of the type "prime" - action: 'supply', - toTokenAmountTokens: new BigNumber(10), - }), - { - accountAddress: fakeAccountAddress, - }, - ); - - expect(result.current).toMatchSnapshot(); - }); - - it('returns undefined when passed toTokenAmountTokens parameter equals 0', async () => { - const { result } = renderHook( - () => - useGetHypotheticalUserPrimeApys({ - asset: fakeAssetWithPrimeDistribution, - action: 'supply', - toTokenAmountTokens: new BigNumber(0), - }), - { - accountAddress: fakeAccountAddress, - }, - ); - - expect(result.current).toMatchSnapshot(); - }); - - it.each([ - { action: 'supply', amountTokens: 0 }, - { action: 'supply', amountTokens: 50 }, - { action: 'swapAndSupply', amountTokens: 0 }, - { action: 'swapAndSupply', amountTokens: 50 }, - { action: 'withdraw', amountTokens: 0 }, - { action: 'withdraw', amountTokens: 50 }, - { action: 'borrow', amountTokens: 0 }, - { action: 'borrow', amountTokens: 50 }, - { action: 'repay', amountTokens: 0 }, - { action: 'repay', amountTokens: 50 }, - ] as { action: TokenAction; amountTokens: number }[])( - 'returns correct values: %s', - async ({ action, amountTokens }) => { - const { result } = renderHook( - () => - useGetHypotheticalUserPrimeApys({ - asset: fakeAssetWithPrimeDistribution, - action, - toTokenAmountTokens: new BigNumber(amountTokens), - }), - { - accountAddress: fakeAccountAddress, - }, - ); - - expect((useGetHypotheticalPrimeApys as Mock).mock.calls[0]).toMatchSnapshot(); - - expect(result.current).toMatchSnapshot(); - }, - ); -}); diff --git a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/index.ts b/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/index.ts deleted file mode 100644 index 75bdc4804b..0000000000 --- a/apps/evm/src/hooks/useGetHypotheticalUserPrimeApys/index.ts +++ /dev/null @@ -1,125 +0,0 @@ -import BigNumber from 'bignumber.js'; -import { useMemo } from 'react'; - -import { keepPreviousData } from '@tanstack/react-query'; -import { - useGetHypotheticalPrimeApys, - useGetPrimeStatus, - useGetXvsVaultUserInfo, -} from 'clients/api'; -import { NULL_ADDRESS } from 'constants/address'; -import { useGetToken } from 'libs/tokens'; -import { useAccountAddress } from 'libs/wallet'; -import type { Asset, TokenAction } from 'types'; -import { convertTokensToMantissa } from 'utilities'; - -export interface UseGetHypotheticalUserPrimeApysInput { - asset: Asset; - action: TokenAction; - toTokenAmountTokens: BigNumber; -} - -export const useGetHypotheticalUserPrimeApys = ({ - asset, - action, - toTokenAmountTokens, -}: UseGetHypotheticalUserPrimeApysInput) => { - const { accountAddress } = useAccountAddress(); - const xvs = useGetToken({ - symbol: 'XVS', - }); - - const { data: getPrimeStatusData } = useGetPrimeStatus( - { - accountAddress: accountAddress || NULL_ADDRESS, - }, - { - enabled: !!accountAddress, - }, - ); - const xvsVaultPoolIndex = getPrimeStatusData?.xvsVaultPoolId; - - const { data: getXvsVaultUserInfoData } = useGetXvsVaultUserInfo( - { - poolIndex: xvsVaultPoolIndex || 0, - rewardTokenAddress: xvs?.address || NULL_ADDRESS, - accountAddress: accountAddress || NULL_ADDRESS, - }, - { - enabled: !!accountAddress && !!xvs && typeof xvsVaultPoolIndex === 'number', - }, - ); - const userXvsStakedMantissa = getXvsVaultUserInfoData?.stakedAmountMantissa; - - const shouldFetchHypotheticalUserPrimeApy = asset.borrowTokenDistributions - .concat(asset.supplyTokenDistributions) - .some(distribution => distribution.type === 'prime'); - - const { userBorrowBalanceMantissa, userSupplyBalanceMantissa } = useMemo(() => { - let hypotheticalUserBorrowBalanceTokens = asset.userBorrowBalanceTokens; - let hypotheticalUserSupplyBalanceTokens = asset.userSupplyBalanceTokens; - - if (action === 'borrow') { - hypotheticalUserBorrowBalanceTokens = asset.userBorrowBalanceTokens.plus(toTokenAmountTokens); - } else if (action === 'repay') { - hypotheticalUserBorrowBalanceTokens = - asset.userBorrowBalanceTokens.minus(toTokenAmountTokens); - } else if (action === 'supply' || action === 'swapAndSupply') { - hypotheticalUserSupplyBalanceTokens = asset.userSupplyBalanceTokens.plus(toTokenAmountTokens); - } else if (action === 'withdraw') { - hypotheticalUserSupplyBalanceTokens = - asset.userSupplyBalanceTokens.minus(toTokenAmountTokens); - } - - if (hypotheticalUserBorrowBalanceTokens.isLessThan(0)) { - hypotheticalUserBorrowBalanceTokens = new BigNumber(0); - } - - if (hypotheticalUserSupplyBalanceTokens.isLessThan(0)) { - hypotheticalUserSupplyBalanceTokens = new BigNumber(0); - } - - return { - userBorrowBalanceMantissa: convertTokensToMantissa({ - value: hypotheticalUserBorrowBalanceTokens, - token: asset.vToken.underlyingToken, - }), - userSupplyBalanceMantissa: convertTokensToMantissa({ - value: hypotheticalUserSupplyBalanceTokens, - token: asset.vToken.underlyingToken, - }), - }; - }, [ - asset.vToken.underlyingToken, - asset.userBorrowBalanceTokens, - asset.userSupplyBalanceTokens, - action, - toTokenAmountTokens, - ]); - - const { data: getHypotheticalPrimeApysData } = useGetHypotheticalPrimeApys( - { - accountAddress, - vTokenAddress: asset.vToken.address, - userBorrowBalanceMantissa, - userSupplyBalanceMantissa, - userXvsStakedMantissa: userXvsStakedMantissa || new BigNumber(0), - }, - { - enabled: shouldFetchHypotheticalUserPrimeApy && !!userXvsStakedMantissa, - placeholderData: keepPreviousData, - }, - ); - - if (toTokenAmountTokens.isEqualTo(0)) { - return { - borrowApy: undefined, - supplyApy: undefined, - }; - } - - return { - borrowApy: getHypotheticalPrimeApysData?.borrowApyPercentage, - supplyApy: getHypotheticalPrimeApysData?.supplyApyPercentage, - }; -}; diff --git a/apps/evm/src/libs/translations/translations/en.json b/apps/evm/src/libs/translations/translations/en.json index f8f8caeaef..db10632ed8 100644 --- a/apps/evm/src/libs/translations/translations/en.json +++ b/apps/evm/src/libs/translations/translations/en.json @@ -129,11 +129,8 @@ }, "accountData": { "balanceUpdate": { - "borrowBalance": "Borrow balance ({{ tokenSymbol }})", - "supplyBalance": "Supply balance ({{ tokenSymbol }})" - }, - "borrowBalance": { - "label": "Borrow balance" + "borrowBalance": "Borrow balance", + "supplyBalance": "Supply balance" }, "dailyEarnings": { "label": "Daily earnings" @@ -141,9 +138,6 @@ "healthFactor": { "label": "Health factor", "tooltip": "Liquidation at < 1.0" - }, - "supplyBalance": { - "label": "Supply balance" } }, "accountHealth": { @@ -230,6 +224,27 @@ } } }, + "apyBreakdown": { + "borrowApy": "Borrow APY", + "distributionApy": "Distribution APY", + "distributionTooltip": "Distribution rewards are initiated and implemented by the decentralized Venus community. The Venus protocol does not guarantee them and accepts no liability.", + "externalDistributionApy": "{{description}} APY", + "intrinsicApy": "Intrinsic APY", + "intrinsicApyTooltip": "APY earned directly from the asset itself (e.g. staking or external rewards), separate from Venus lending yield. The Venus protocol does not guarantee them and accepts no liability.", + "netApy": { + "label": "Net APY", + "tooltip": "Sum of the APYs of all the markets involved" + }, + "offChainApy": "Off-chain APY", + "offChainApyTooltip": "Additional yield from off-chain activities, such as airdrop or promotional campaigns, managed by third parties. The Venus protocol does not guarantee them and accepts no liability.", + "primeApy": "Prime APY", + "supplyApy": "Supply APY", + "totalApy": { + "borrowApyTooltip": "Borrow APY - Distribution APYs", + "label": "Total APY", + "supplyApyTooltip": "Supply APY + Distribution APYs" + } + }, "apyChart": { "tooltipItemLabels": { "borrowApy": "Borrow APY", @@ -258,23 +273,6 @@ "borrow": "Your E-mode settings do not allow you to borrow from this market. Manage E-mode" } }, - "assetInfo": { - "borrowApy": "Borrow APY", - "distributionApy": "Distribution APY ({{tokenSymbol}})", - "distributionTooltip": "Distribution rewards are initiated and implemented by the decentralized Venus community. The Venus protocol does not guarantee them and accepts no liability.", - "externalDistributionApy": "{{description}} APY ({{tokenSymbol}})", - "intrinsicApy": "Intrinsic APY ({{tokenSymbol}})", - "intrinsicApyTooltip": "APY earned directly from the asset itself (e.g. staking or external rewards), separate from Venus lending yield. The Venus protocol does not guarantee them and accepts no liability.", - "offChainApy": "Off-chain APY ({{tokenSymbol}})", - "offChainApyTooltip": "Additional yield from off-chain activities, such as airdrop or promotional campaigns, managed by third parties. The Venus protocol does not guarantee them and accepts no liability.", - "primeApy": "Prime APY ({{tokenSymbol}})", - "supplyApy": "Supply APY", - "totalApy": { - "borrowApyTooltip": "Borrow APY - Distribution APYs", - "label": "Total APY", - "supplyApyTooltip": "Supply APY + Distribution APYs" - } - }, "assetWarning": { "borrowDescription": "You may borrow {{tokenSymbol}} according to your collateralized supply in the {{poolName}} pool. ", "modalTitle": "{{ poolName }} tokens", diff --git a/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/__snapshots__/index.spec.tsx.snap new file mode 100644 index 0000000000..2743a3615b --- /dev/null +++ b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/__snapshots__/index.spec.tsx.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ApyBreakdown > renders correct values: 'borrow' 1`] = `"Borrow APY-4.97%Distribution APY0.52%Total APY6.50%"`; + +exports[`ApyBreakdown > renders correct values: 'multiple mutations' 1`] = `"Supply APY0.05%Distribution APY0.12%Borrow APY-4.97%Distribution APY0.52%Net APY7.67%"`; + +exports[`ApyBreakdown > renders correct values: 'no mutations' 1`] = `"Total APY0%"`; + +exports[`ApyBreakdown > renders correct values: 'repay' 1`] = `"Borrow APY-4.97%Distribution APY0.52%Total APY6.50%"`; + +exports[`ApyBreakdown > renders correct values: 'supply' 1`] = `"Supply APY0.05%Distribution APY0.12%Total APY1.17%"`; + +exports[`ApyBreakdown > renders correct values: 'withdraw' 1`] = `"Supply APY3.89%Distribution APY1.35%Prime APY0.75%1.75%Total APY7.99%"`; diff --git a/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/index.spec.tsx b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/index.spec.tsx new file mode 100644 index 0000000000..83ce8e003a --- /dev/null +++ b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/__tests__/index.spec.tsx @@ -0,0 +1,105 @@ +import BigNumber from 'bignumber.js'; + +import { poolData } from '__mocks__/models/pools'; +import { renderComponent } from 'testUtils/render'; +import type { BalanceMutation, Pool, TokenDistribution } from 'types'; +import { ApyBreakdown } from '..'; + +const bumpTokenDistributions = ({ + tokenDistributions, +}: { tokenDistributions: TokenDistribution[] }) => + tokenDistributions.map(distribution => ({ + ...distribution, + apyPercentage: distribution.apyPercentage.plus(1), + })); + +const fakePool = poolData[0]; + +const fakeSimulatedPool: Pool = { + ...fakePool, + assets: fakePool.assets.map(a => ({ + ...a, + supplyTokenDistributions: bumpTokenDistributions({ + tokenDistributions: a.supplyTokenDistributions, + }), + borrowTokenDistributions: bumpTokenDistributions({ + tokenDistributions: a.borrowTokenDistributions, + }), + })), +}; + +const fakeBalanceMutations: BalanceMutation[] = [ + { + type: 'vai', + amountTokens: new BigNumber(10), + action: 'borrow', + }, + { + type: 'asset', + vTokenAddress: fakeSimulatedPool.assets[0].vToken.address, + amountTokens: new BigNumber(10), + action: 'supply', + }, + { + type: 'asset', + vTokenAddress: fakeSimulatedPool.assets[1].vToken.address, + amountTokens: new BigNumber(1), + action: 'withdraw', + }, + { + type: 'asset', + vTokenAddress: fakeSimulatedPool.assets[2].vToken.address, + amountTokens: new BigNumber(4), + action: 'borrow', + }, + { + type: 'asset', + vTokenAddress: fakeSimulatedPool.assets[2].vToken.address, + amountTokens: new BigNumber(2), + action: 'repay', + }, +]; + +describe('ApyBreakdown', () => { + it.each([ + { label: 'no mutations', simulatedPool: undefined, balanceMutations: undefined }, + // Actions concerning multiple markets + { + label: 'multiple mutations', + simulatedPool: fakeSimulatedPool, + balanceMutations: [fakeBalanceMutations[0], fakeBalanceMutations[1], fakeBalanceMutations[3]], + }, + // Supply to one market + { + label: 'supply', + simulatedPool: fakeSimulatedPool, + balanceMutations: [fakeBalanceMutations[1]], + }, + // Withdraw to one market + { + label: 'withdraw', + simulatedPool: fakeSimulatedPool, + balanceMutations: [fakeBalanceMutations[2]], + }, + // Borrow from one market + { + label: 'borrow', + simulatedPool: fakeSimulatedPool, + balanceMutations: [fakeBalanceMutations[3]], + }, + // Repay to one market + { + label: 'repay', + simulatedPool: fakeSimulatedPool, + balanceMutations: [fakeBalanceMutations[4]], + }, + ] satisfies { + label: string; + simulatedPool?: Pool; + balanceMutations?: BalanceMutation[]; + }[])('renders correct values: $label', async props => { + const { container } = renderComponent(); + + expect(container.textContent).toMatchSnapshot(); + }); +}); diff --git a/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/formatRows/index.tsx b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/formatRows/index.tsx new file mode 100644 index 0000000000..592a31ebd0 --- /dev/null +++ b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/formatRows/index.tsx @@ -0,0 +1,110 @@ +import type { TFunction } from 'i18next'; + +import { type LabeledInlineContentProps, ValueUpdate } from 'components'; +import type { Asset, BalanceMutation } from 'types'; +import { formatPercentageToReadableValue } from 'utilities'; + +export const formatRows = ({ + balanceMutation, + asset, + simulatedAsset, + t, +}: { + balanceMutation: BalanceMutation; + asset: Asset; + t: TFunction<'translation', undefined>; + simulatedAsset?: Asset; +}) => { + const refAsset = simulatedAsset ?? asset; + + const isMutatingBorrowBalance = + balanceMutation.action === 'borrow' || balanceMutation.action === 'repay'; + + const rows: LabeledInlineContentProps[] = [ + { + label: isMutatingBorrowBalance ? t('apyBreakdown.borrowApy') : t('apyBreakdown.supplyApy'), + iconSrc: refAsset.vToken.underlyingToken, + children: formatPercentageToReadableValue( + isMutatingBorrowBalance ? refAsset.borrowApyPercentage : refAsset.supplyApyPercentage, + ), + }, + ]; + + const distributionRows = ( + isMutatingBorrowBalance ? asset.borrowTokenDistributions : asset.supplyTokenDistributions + ) + .filter(distribution => distribution.type !== 'primeSimulation' && distribution.isActive) + .reduce((acc, distribution) => { + if (distribution.type !== 'prime' && distribution.apyPercentage.isEqualTo(0)) { + return acc; + } + + let label = t('apyBreakdown.distributionApy'); + + if (distribution.type === 'prime') { + label = t('apyBreakdown.primeApy'); + } + + if (distribution.type === 'merkl') { + label = t('apyBreakdown.externalDistributionApy', { + description: distribution.rewardDetails.description, + tokenSymbol: distribution.token.symbol, + }); + } + + if (distribution.type === 'intrinsic') { + label = t('apyBreakdown.intrinsicApy'); + } + + if (distribution.type === 'off-chain') { + label = t('apyBreakdown.offChainApy'); + } + + let children: undefined | React.ReactNode; + + if (distribution.type === 'prime') { + const simulatedPrimeDistribution = ( + isMutatingBorrowBalance + ? simulatedAsset?.borrowTokenDistributions + : simulatedAsset?.supplyTokenDistributions + )?.find(d => d.type === 'prime'); + + children = ( + + ); + } else { + children = formatPercentageToReadableValue(distribution.apyPercentage); + } + + let tooltip = undefined; + + if (distribution.type === 'venus') { + tooltip = t('apyBreakdown.distributionTooltip'); + } + + if (distribution.type === 'intrinsic') { + tooltip = t('apyBreakdown.intrinsicApyTooltip'); + } + + if (distribution.type === 'off-chain') { + tooltip = t('apyBreakdown.offChainApyTooltip'); + } + + const row: LabeledInlineContentProps = { + label, + iconSrc: distribution.token, + tooltip, + children, + }; + + return [...acc, row]; + }, []); + + return rows.concat(distributionRows); +}; diff --git a/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/index.tsx b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/index.tsx new file mode 100644 index 0000000000..204b78f29c --- /dev/null +++ b/apps/evm/src/pages/Market/OperationForm/ApyBreakdown/index.tsx @@ -0,0 +1,125 @@ +import BigNumber from 'bignumber.js'; + +import { + Accordion, + InfoIcon, + LabeledInlineContent, + type LabeledInlineContentProps, +} from 'components'; +import { useTranslation } from 'libs/translations'; +import type { BalanceMutation, Pool } from 'types'; +import { + areAddressesEqual, + formatPercentageToReadableValue, + getCombinedDistributionApys, +} from 'utilities'; +import { formatRows } from './formatRows'; + +export interface ApyBreakdownProps { + pool: Pool; + simulatedPool?: Pool; + balanceMutations?: BalanceMutation[]; + renderType?: 'block' | 'accordion'; +} + +export const ApyBreakdown: React.FC = ({ + pool, + simulatedPool, + balanceMutations = [], + renderType = 'block', +}) => { + const { t } = useTranslation(); + + const { rows, totalApyPercentage } = balanceMutations.reduce<{ + rows: LabeledInlineContentProps[]; + totalApyPercentage: BigNumber; + }>( + (acc, balanceMutation) => { + // Skip VAI mutations + if (balanceMutation.type === 'vai') { + return acc; + } + + const asset = pool.assets.find(a => + areAddressesEqual(a.vToken.address, balanceMutation.vTokenAddress), + ); + + if (!asset) { + return acc; + } + + const simulatedAsset = simulatedPool?.assets.find(a => + areAddressesEqual(a.vToken.address, balanceMutation.vTokenAddress), + ); + + const tempRows = formatRows({ + asset, + simulatedAsset, + balanceMutation, + t, + }); + + const apys = getCombinedDistributionApys({ + asset: simulatedAsset ?? asset, + }); + + return { + rows: acc.rows.concat(tempRows), + totalApyPercentage: + balanceMutation.action === 'supply' || balanceMutation.action === 'withdraw' + ? acc.totalApyPercentage.plus(apys.totalSupplyApyPercentage) + : acc.totalApyPercentage.minus(apys.totalBorrowApyPercentage), + }; + }, + { + rows: [], + totalApyPercentage: new BigNumber(0), + }, + ); + + const readableTotalApy = formatPercentageToReadableValue(totalApyPercentage); + + let label = t('apyBreakdown.totalApy.label'); + let tooltip: undefined | string; + + if (balanceMutations.length > 1) { + label = t('apyBreakdown.netApy.label'); + tooltip = t('apyBreakdown.netApy.tooltip'); + } else { + const balanceMutationAction = balanceMutations[0]?.action; + + tooltip = + balanceMutationAction === 'supply' || balanceMutationAction === 'withdraw' + ? t('apyBreakdown.totalApy.supplyApyTooltip') + : t('apyBreakdown.totalApy.borrowApyTooltip'); + } + + const rowsDom = rows.map((row, i) => ); + + if (renderType === 'block') { + return ( +
+ {rowsDom} + + + {readableTotalApy} + +
+ ); + } + + return ( + +

{label}

+ + +
+ } + rightLabel={readableTotalApy} + > +
{rowsDom}
+ + ); +}; diff --git a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.prime.spec.tsx.snap b/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.prime.spec.tsx.snap deleted file mode 100644 index 4cb557a572..0000000000 --- a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.prime.spec.tsx.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`AssetInfo - Feature enabled: Prime > renders correct values when using swap: { action: 'borrow' } 1`] = `"Borrow APY-5.36%Distribution APY (XVS)1.67%Prime APY (USDC)0.91%Total APY-7.94%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values when using swap: { action: 'repay' } 1`] = `"Borrow APY-5.36%Distribution APY (XVS)1.67%Prime APY (USDC)0.91%Total APY-7.94%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values when using swap: { action: 'supply' } 1`] = `"Supply APY3.89%Distribution APY (XVS)1.35%Prime APY (USDC)0.75%Total APY5.99%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values when using swap: { action: 'withdraw' } 1`] = `"Supply APY3.89%Distribution APY (XVS)1.35%Prime APY (USDC)0.75%Total APY5.99%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values: { action: 'borrow' } 1`] = `"Borrow APY-5.36%Distribution APY (XVS)1.67%Prime APY (USDC)0.91%Total APY-7.94%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values: { action: 'repay' } 1`] = `"Borrow APY-5.36%Distribution APY (XVS)1.67%Prime APY (USDC)0.91%Total APY-7.94%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values: { action: 'supply' } 1`] = `"Supply APY3.89%Distribution APY (XVS)1.35%Prime APY (USDC)0.75%Total APY5.99%"`; - -exports[`AssetInfo - Feature enabled: Prime > renders correct values: { action: 'withdraw' } 1`] = `"Supply APY3.89%Distribution APY (XVS)1.35%Prime APY (USDC)0.75%Total APY5.99%"`; diff --git a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.spec.tsx.snap deleted file mode 100644 index 87522677b2..0000000000 --- a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/__snapshots__/index.spec.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`y > renders correct values: { action: 'borrow' } 1`] = `"Borrow APY-2.3%Distribution APY (XVS)4.17%Total APY-6.48%"`; - -exports[`y > renders correct values: { action: 'repay' } 1`] = `"Borrow APY-2.3%Distribution APY (XVS)4.17%Total APY-6.48%"`; - -exports[`y > renders correct values: { action: 'supply' } 1`] = `"Supply APY0.05%Distribution APY (XVS)0.12%Total APY0.17%"`; - -exports[`y > renders correct values: { action: 'withdraw' } 1`] = `"Supply APY0.05%Distribution APY (XVS)0.12%Total APY0.17%"`; diff --git a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.prime.spec.tsx b/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.prime.spec.tsx deleted file mode 100644 index 377a18e622..0000000000 --- a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.prime.spec.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import BigNumber from 'bignumber.js'; -import type { Mock } from 'vitest'; - -import { poolData } from '__mocks__/models/pools'; -import { exactAmountInSwap } from '__mocks__/models/swaps'; -import { useGetHypotheticalUserPrimeApys } from 'hooks/useGetHypotheticalUserPrimeApys'; -import { type UseIsFeatureEnabledInput, useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; -import { renderComponent } from 'testUtils/render'; -import { AssetInfo, type AssetInfoProps } from '..'; - -vi.mock('hooks/useGetHypotheticalUserPrimeApys'); - -describe('AssetInfo - Feature enabled: Prime', () => { - beforeEach(() => { - (useIsFeatureEnabled as Mock).mockImplementation( - ({ name }: UseIsFeatureEnabledInput) => name === 'prime', - ); - - (useGetHypotheticalUserPrimeApys as Mock).mockImplementation(() => ({ - supplyApyPercentage: new BigNumber(13.4), - borrowApyPercentage: new BigNumber(10.4), - })); - }); - - it('renders without crashing', async () => { - renderComponent(); - }); - - it.each([ - { action: 'supply' }, - { action: 'withdraw' }, - { action: 'borrow' }, - { action: 'repay' }, - ] as { action: AssetInfoProps['action']; amountToken: number }[])( - 'renders correct values: %s', - async ({ action }) => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }, - ); - - it.each([ - { action: 'supply' }, - { action: 'withdraw' }, - { action: 'borrow' }, - { action: 'repay' }, - ] as { action: AssetInfoProps['action']; amountToken: number }[])( - 'renders correct values when using swap: %s', - async ({ action }) => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }, - ); -}); diff --git a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.spec.tsx b/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.spec.tsx deleted file mode 100644 index 050471275c..0000000000 --- a/apps/evm/src/pages/Market/OperationForm/AssetInfo/__tests__/index.spec.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { poolData } from '__mocks__/models/pools'; -import { renderComponent } from 'testUtils/render'; -import { AssetInfo, type AssetInfoProps } from '..'; - -describe('y', () => { - it('renders without crashing', async () => { - renderComponent(); - }); - - it.each([ - { action: 'supply' }, - { action: 'withdraw' }, - { action: 'borrow' }, - { action: 'repay' }, - ] as { action: AssetInfoProps['action']; amountToken: number }[])( - 'renders correct values: %s', - async ({ action }) => { - const { container } = renderComponent( - , - ); - - expect(container.textContent).toMatchSnapshot(); - }, - ); -}); diff --git a/apps/evm/src/pages/Market/OperationForm/AssetInfo/index.tsx b/apps/evm/src/pages/Market/OperationForm/AssetInfo/index.tsx deleted file mode 100644 index 3fb916db61..0000000000 --- a/apps/evm/src/pages/Market/OperationForm/AssetInfo/index.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import BigNumber from 'bignumber.js'; -import { - Accordion, - InfoIcon, - LabeledInlineContent, - type LabeledInlineContentProps, - ValueUpdate, -} from 'components'; -import { useGetHypotheticalUserPrimeApys } from 'hooks/useGetHypotheticalUserPrimeApys'; -import { useTranslation } from 'libs/translations'; -import { useMemo } from 'react'; -import type { Asset, Swap, TokenAction } from 'types'; -import { - formatPercentageToReadableValue, - getCombinedDistributionApys, - getSwapToTokenAmountReceivedTokens, -} from 'utilities'; - -export interface AssetInfoProps { - asset: Asset; - action: TokenAction; - amountTokens?: BigNumber; - isUsingSwap?: boolean; - swap?: Swap; - renderType?: 'block' | 'accordion'; -} - -export const AssetInfo: React.FC = ({ - asset, - action, - swap, - isUsingSwap = false, - amountTokens = new BigNumber(0), - renderType = 'block', -}) => { - const { t } = useTranslation(); - - const toTokenAmountTokens = useMemo(() => { - if (isUsingSwap) { - return ( - getSwapToTokenAmountReceivedTokens(swap)?.swapToTokenAmountReceivedTokens ?? - new BigNumber(0) - ); - } - - return amountTokens; - }, [swap, amountTokens, isUsingSwap]); - - const hypotheticalUserPrimeApys = useGetHypotheticalUserPrimeApys({ - asset, - action, - toTokenAmountTokens, - }); - - const { totalBorrowApyBoostPercentage, totalSupplyApyBoostPercentage } = useMemo(() => { - const combinedDistributionApys = getCombinedDistributionApys({ - asset, - }); - - let tempTotalDistributionBorrowApyPercentage = - combinedDistributionApys.totalBorrowApyBoostPercentage; - - if (hypotheticalUserPrimeApys.borrowApy) { - tempTotalDistributionBorrowApyPercentage = tempTotalDistributionBorrowApyPercentage - .minus(combinedDistributionApys.borrowApyPrimePercentage) - .plus(hypotheticalUserPrimeApys.borrowApy); - } - - let tempTotalDistributionSupplyApyPercentage = - combinedDistributionApys.totalSupplyApyBoostPercentage; - if (hypotheticalUserPrimeApys.supplyApy) { - tempTotalDistributionSupplyApyPercentage = tempTotalDistributionSupplyApyPercentage - .minus(combinedDistributionApys.supplyApyPrimePercentage) - .plus(hypotheticalUserPrimeApys.supplyApy); - } - - return { - totalBorrowApyBoostPercentage: asset.borrowApyPercentage.minus( - tempTotalDistributionBorrowApyPercentage, - ), - totalSupplyApyBoostPercentage: asset.supplyApyPercentage.plus( - tempTotalDistributionSupplyApyPercentage, - ), - }; - }, [asset, hypotheticalUserPrimeApys]); - - const rows = useMemo(() => { - const apyBreakdownRows: LabeledInlineContentProps[] = [ - { - label: - action === 'borrow' || action === 'repay' - ? t('assetInfo.borrowApy') - : t('assetInfo.supplyApy'), - iconSrc: asset.vToken.underlyingToken, - children: formatPercentageToReadableValue( - action === 'borrow' || action === 'repay' - ? asset.borrowApyPercentage - : asset.supplyApyPercentage, - ), - }, - ]; - - const distributionRows = ( - action === 'borrow' || action === 'repay' - ? asset.borrowTokenDistributions - : asset.supplyTokenDistributions - ) - .filter(distribution => distribution.type !== 'primeSimulation' && distribution.isActive) - .reduce((acc, distribution) => { - if (distribution.type !== 'prime' && distribution.apyPercentage.isEqualTo(0)) { - return acc; - } - let label = t('assetInfo.distributionApy', { tokenSymbol: distribution.token.symbol }); - if (distribution.type === 'prime') { - label = t('assetInfo.primeApy', { tokenSymbol: distribution.token.symbol }); - } - if (distribution.type === 'merkl') { - label = t('assetInfo.externalDistributionApy', { - description: distribution.rewardDetails.description, - tokenSymbol: distribution.token.symbol, - }); - } - if (distribution.type === 'intrinsic') { - label = t('assetInfo.intrinsicApy', { tokenSymbol: distribution.token.symbol }); - } - if (distribution.type === 'off-chain') { - label = t('assetInfo.offChainApy', { tokenSymbol: distribution.token.symbol }); - } - const children = - distribution.type === 'prime' ? ( - - ) : ( - formatPercentageToReadableValue(distribution.apyPercentage) - ); - - let tooltip = undefined; - if (distribution.type === 'venus') { - tooltip = t('assetInfo.distributionTooltip'); - } - if (distribution.type === 'intrinsic') { - tooltip = t('assetInfo.intrinsicApyTooltip'); - } - if (distribution.type === 'off-chain') { - tooltip = t('assetInfo.offChainApyTooltip'); - } - - const row: LabeledInlineContentProps = { - label, - iconSrc: distribution.token, - tooltip, - children, - }; - - return [...acc, row]; - }, []); - - return apyBreakdownRows.concat(distributionRows); - }, [asset, action, t, hypotheticalUserPrimeApys]); - - if (renderType === 'block') { - return ( -
- {rows.map(row => ( - - ))} - - - {formatPercentageToReadableValue( - action === 'borrow' || action === 'repay' - ? totalBorrowApyBoostPercentage - : totalSupplyApyBoostPercentage, - )} - -
- ); - } - - return ( - -

{t('assetInfo.totalApy.label')}

- - -
- } - rightLabel={formatPercentageToReadableValue( - action === 'borrow' || action === 'repay' - ? totalBorrowApyBoostPercentage - : totalSupplyApyBoostPercentage, - )} - > -
- {rows.map(row => ( - - ))} -
- - ); -}; diff --git a/apps/evm/src/pages/Market/OperationForm/BorrowForm/index.tsx b/apps/evm/src/pages/Market/OperationForm/BorrowForm/index.tsx index 596ca891d4..3ad0ec3ed4 100644 --- a/apps/evm/src/pages/Market/OperationForm/BorrowForm/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/BorrowForm/index.tsx @@ -17,18 +17,23 @@ import { } from 'constants/healthFactor'; import { useChain } from 'hooks/useChain'; import useDelegateApproval from 'hooks/useDelegateApproval'; -import useFormatTokensToReadableValue from 'hooks/useFormatTokensToReadableValue'; import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; import { useTranslation } from 'libs/translations'; -import type { Asset, Pool } from 'types'; -import { calculateHealthFactor, convertTokensToMantissa } from 'utilities'; +import type { Asset, BalanceMutation, Pool } from 'types'; +import { + calculateHealthFactor, + convertTokensToMantissa, + formatTokensToReadableValue, +} from 'utilities'; import { NULL_ADDRESS } from 'constants/address'; import { ConnectWallet } from 'containers/ConnectWallet'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useGetContractAddress } from 'hooks/useGetContractAddress'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; import { useAnalytics } from 'libs/analytics'; import { useAccountAddress } from 'libs/wallet'; -import { AssetInfo } from '../AssetInfo'; +import { ApyBreakdown } from '../ApyBreakdown'; import { OperationDetails } from '../OperationDetails'; import { calculateAmountDollars } from '../calculateAmountDollars'; import SubmitSection from './SubmitSection'; @@ -165,11 +170,29 @@ export const BorrowFormUi: React.FC = ({ return [maxTokens, safeMaxTokens, moderateRiskMaxTokens]; }, [asset, pool]); - const readableLimit = useFormatTokensToReadableValue({ + const readableLimit = formatTokensToReadableValue({ value: limitTokens, token: asset.vToken.underlyingToken, }); + const _debouncedInputAmountTokens = useDebounceValue(formValues.amountTokens); + const debouncedInputAmountTokens = new BigNumber(_debouncedInputAmountTokens || 0); + + const balanceMutations: BalanceMutation[] = [ + { + type: 'asset', + vTokenAddress: asset.vToken.address, + action: 'borrow', + amountTokens: debouncedInputAmountTokens, + }, + ]; + + const { data: getSimulatedPoolData } = useSimulateBalanceMutations({ + pool, + balanceMutations, + }); + const simulatedPool = getSimulatedPoolData?.pool; + const { handleSubmit, isFormValid, formError } = useForm({ asset, pool, @@ -207,7 +230,7 @@ export const BorrowFormUi: React.FC = ({ amountTokens, maxSelected, }: { amountTokens: BigNumber | string; maxSelected: boolean; selectedPercentage?: number }) => { - if (Number(amountTokens.toString()) > 0) { + if (Number(formValues.amountTokens) > 0) { captureAnalyticEvent( 'borrow_amount_set', { @@ -298,7 +321,7 @@ export const BorrowFormUi: React.FC = ({ } /> - {!isUserConnected && } + {!isUserConnected && }
= ({ )} {shouldAskUserRiskAcknowledgement && ( @@ -407,7 +430,7 @@ const BorrowForm: React.FC = ({ asset, pool, onSubmitSuccess }) const { isDelegateApproved, isDelegateApprovedLoading, - isUseUpdatePoolDelegateStatusLoading, + isDelegateStatusLoading, updatePoolDelegateStatus, } = useDelegateApproval({ delegateeAddress: nativeTokenGatewayContractAddress || NULL_ADDRESS, @@ -446,7 +469,7 @@ const BorrowForm: React.FC = ({ asset, pool, onSubmitSuccess }) isSubmitting={isSubmitting} isDelegateApproved={isDelegateApproved} isDelegateApprovedLoading={isDelegateApprovedLoading} - isApproveDelegateLoading={isUseUpdatePoolDelegateStatusLoading} + isApproveDelegateLoading={isDelegateStatusLoading} approveDelegateAction={() => updatePoolDelegateStatus({ approvedStatus: true })} isWrapUnwrapNativeTokenEnabled={isWrapUnwrapNativeTokenEnabled} isEModeFeatureEnabled={isEModeFeatureEnabled} diff --git a/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/types.ts b/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/types.ts index 65a5c6b5a6..a21b684765 100644 --- a/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/types.ts +++ b/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/types.ts @@ -14,5 +14,4 @@ export type FormErrorCode = | 'HIGHER_THAN_BORROW_CAP' | 'HIGHER_THAN_LIQUIDITY' | 'HIGHER_THAN_AVAILABLE_AMOUNT' - | 'TOO_RISKY' | 'REQUIRES_RISK_ACKNOWLEDGEMENT'; diff --git a/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/useFormValidation.ts b/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/useFormValidation.ts index 77574dd4c3..d1687a64b2 100644 --- a/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/useFormValidation.ts +++ b/apps/evm/src/pages/Market/OperationForm/BorrowForm/useForm/useFormValidation.ts @@ -96,7 +96,7 @@ const useFormValidation = ({ ); if (fromTokenAmountTokens.isGreaterThan(assetLiquidityTokens)) { - // User is trying to withdraw more than available liquidity + // User is trying to borrow more than available liquidity return { code: 'HIGHER_THAN_LIQUIDITY', message: t('operationForm.error.higherThanAvailableLiquidity'), diff --git a/apps/evm/src/pages/Market/OperationForm/OperationDetails/SwapDetails/index.tsx b/apps/evm/src/pages/Market/OperationForm/OperationDetails/SwapDetails/index.tsx index 7e5d0f9328..c4987a3cec 100644 --- a/apps/evm/src/pages/Market/OperationForm/OperationDetails/SwapDetails/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/OperationDetails/SwapDetails/index.tsx @@ -59,5 +59,3 @@ export const SwapDetails: React.FC = ({ swap, action, ...other ); }; - -export default SwapDetails; diff --git a/apps/evm/src/pages/Market/OperationForm/OperationDetails/index.tsx b/apps/evm/src/pages/Market/OperationForm/OperationDetails/index.tsx index dc58ef3b8b..e6da30e0df 100644 --- a/apps/evm/src/pages/Market/OperationForm/OperationDetails/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/OperationDetails/index.tsx @@ -1,33 +1,29 @@ -import type BigNumber from 'bignumber.js'; -import { Delimiter } from 'components'; +import { BalanceUpdates, Delimiter } from 'components'; import { AccountData } from 'containers/AccountData'; -import type { Asset, Pool, Swap, TokenAction } from 'types'; -import { AssetInfo } from '../AssetInfo'; -import SwapDetails from './SwapDetails'; +import type { BalanceMutation, Pool, Swap, TokenAction } from 'types'; +import { ApyBreakdown } from '../ApyBreakdown'; +import { SwapDetails } from './SwapDetails'; export interface OperationDetailsProps { - amountTokens: BigNumber; - asset: Asset; action: TokenAction; - isUsingSwap?: boolean; pool: Pool; + balanceMutations: BalanceMutation[]; + simulatedPool?: Pool; + isUsingSwap?: boolean; swap?: Swap; } export const OperationDetails: React.FC = ({ swap, - asset, action, isUsingSwap = false, - amountTokens, pool, + simulatedPool, + balanceMutations, }) => { - const shouldShowAccountData = - pool.userSupplyBalanceCents?.isGreaterThan(0) || - (action === 'supply' && amountTokens.isGreaterThan(0)); - return (
+ {/* TODO: move to submit section */} {isUsingSwap && swap && (action === 'supply' || action === 'repay') && ( <> @@ -36,29 +32,24 @@ export const OperationDetails: React.FC = ({ )} - + + + + - {shouldShowAccountData && ( - <> - + - - - )} +
); }; diff --git a/apps/evm/src/pages/Market/OperationForm/RepayForm/index.tsx b/apps/evm/src/pages/Market/OperationForm/RepayForm/index.tsx index e1414ea55b..a42af52338 100644 --- a/apps/evm/src/pages/Market/OperationForm/RepayForm/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/RepayForm/index.tsx @@ -19,19 +19,22 @@ import useTokenApproval from 'hooks/useTokenApproval'; import { VError } from 'libs/errors'; import { useTranslation } from 'libs/translations'; import { useAccountAddress } from 'libs/wallet'; -import type { Asset, Pool, Swap, SwapError, TokenBalance } from 'types'; +import type { Asset, BalanceMutation, Pool, Swap, SwapError, TokenBalance } from 'types'; import { areTokensEqual, convertMantissaToTokens, convertTokensToMantissa, formatPercentageToReadableValue, + getSwapToTokenAmountReceivedTokens, getUniqueTokenBalances, } from 'utilities'; import { ConnectWallet } from 'containers/ConnectWallet'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useGetContractAddress } from 'hooks/useGetContractAddress'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; import { useAnalytics } from 'libs/analytics'; -import { AssetInfo } from '../AssetInfo'; +import { ApyBreakdown } from '../ApyBreakdown'; import { OperationDetails } from '../OperationDetails'; import { calculateAmountDollars } from '../calculateAmountDollars'; import Notice from './Notice'; @@ -134,6 +137,28 @@ export const RepayFormUi: React.FC = ({ isWrappingNativeToken, ]); + const debouncedFormAmountTokens = useDebounceValue(formValues.amountTokens); + + let toTokenAmountTokens = isUsingSwap + ? getSwapToTokenAmountReceivedTokens(swap) + : debouncedFormAmountTokens; + toTokenAmountTokens = new BigNumber(toTokenAmountTokens || 0); + + const balanceMutations: BalanceMutation[] = [ + { + type: 'asset', + vTokenAddress: asset.vToken.address, + action: 'supply', + amountTokens: toTokenAmountTokens, + }, + ]; + + const { data: getSimulatedPoolData } = useSimulateBalanceMutations({ + pool, + balanceMutations, + }); + const simulatedPool = getSimulatedPoolData?.pool; + const { handleSubmit, isFormValid, formError } = useForm({ asset, poolName: pool.name, @@ -355,7 +380,7 @@ export const RepayFormUi: React.FC = ({ ))} - {!isUserConnected && } + {!isUserConnected && } = ({ @@ -612,9 +637,12 @@ const RepayForm: React.FC = ({ const swapDirection = formValues.fixedRepayPercentage ? 'exactAmountOut' : 'exactAmountIn'; + const debouncedFormAmountTokens = useDebounceValue(formValues.amountTokens); + const swapInfo = useGetSwapInfo({ fromToken: formValues.fromToken || asset.vToken.underlyingToken, - fromTokenAmountTokens: swapDirection === 'exactAmountIn' ? formValues.amountTokens : undefined, + fromTokenAmountTokens: + swapDirection === 'exactAmountIn' ? debouncedFormAmountTokens : undefined, toToken: asset.vToken.underlyingToken, toTokenAmountTokens: formValues.fixedRepayPercentage ? calculatePercentageOfUserBorrowBalance({ diff --git a/apps/evm/src/pages/Market/OperationForm/RepayForm/useForm/useFormValidation.ts b/apps/evm/src/pages/Market/OperationForm/RepayForm/useForm/useFormValidation.ts index 8edd2966c4..47ede29c97 100644 --- a/apps/evm/src/pages/Market/OperationForm/RepayForm/useForm/useFormValidation.ts +++ b/apps/evm/src/pages/Market/OperationForm/RepayForm/useForm/useFormValidation.ts @@ -81,7 +81,7 @@ const useFormValidation = ({ } const toTokensAmountRepaidTokens = isUsingSwap - ? getSwapToTokenAmountReceivedTokens(swap).swapToTokenAmountReceivedTokens + ? getSwapToTokenAmountReceivedTokens(swap) : fromTokenAmountTokens; if ( diff --git a/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/index.spec.tsx b/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/index.spec.tsx index d5e902e160..fe19abd11e 100644 --- a/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/index.spec.tsx +++ b/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/index.spec.tsx @@ -12,10 +12,12 @@ import { useSupply } from 'clients/api'; import { useCollateral } from 'hooks/useCollateral'; import useTokenApproval from 'hooks/useTokenApproval'; import { en } from 'libs/translations'; -import { type Asset, ChainId } from 'types'; +import { type Asset, type BalanceMutation, ChainId, type Pool } from 'types'; import { chains } from '@venusprotocol/chains'; import MAX_UINT256 from 'constants/maxUint256'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; +import { areTokensEqual } from 'utilities'; import SupplyForm from '..'; import { fakeAsset, fakePool } from '../__testUtils__/fakeData'; import TEST_IDS from '../testIds'; @@ -153,6 +155,32 @@ describe('SupplyForm', () => { supplyBalanceTokens: new BigNumber(10), }; + const fakeSupplyBalanceTokens = customFakeAsset.supplyCapTokens + // Add one token too many + .plus(1); + + const fakeSimulatedPool: Pool = { + ...fakePool, + assets: fakePool.assets.map(a => ({ + ...a, + supplyBalanceTokens: areTokensEqual(a.vToken, customFakeAsset.vToken) + ? fakeSupplyBalanceTokens + : a.supplyBalanceTokens, + })), + }; + + (useSimulateBalanceMutations as Mock).mockImplementation( + ({ balanceMutations }: { balanceMutations: BalanceMutation[] }) => ({ + isLoading: false, + data: { + pool: + balanceMutations.filter(b => b.amountTokens.isGreaterThan(0)).length > 0 + ? fakeSimulatedPool + : undefined, + }, + }), + ); + const { getByTestId, getByText } = renderComponent( , { @@ -160,11 +188,7 @@ describe('SupplyForm', () => { }, ); - const incorrectValueTokens = customFakeAsset - .supplyCapTokens!.minus(customFakeAsset.supplyBalanceTokens) - // Add one token too much - .plus(1) - .toFixed(); + const incorrectValueTokens = fakeSupplyBalanceTokens.toFixed(); // Enter amount in input const tokenTextInput = await waitFor(() => getByTestId(TEST_IDS.tokenTextField)); diff --git a/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/indexIntegratedSwap.spec.tsx b/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/indexIntegratedSwap.spec.tsx index f991a9bc33..2a65e6fa2d 100644 --- a/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/indexIntegratedSwap.spec.tsx +++ b/apps/evm/src/pages/Market/OperationForm/SupplyForm/__tests__/indexIntegratedSwap.spec.tsx @@ -19,8 +19,10 @@ import useGetSwapInfo from 'hooks/useGetSwapInfo'; import useGetSwapTokenUserBalances from 'hooks/useGetSwapTokenUserBalances'; import { type UseIsFeatureEnabledInput, useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; import { en } from 'libs/translations'; -import type { Asset, Swap, TokenBalance } from 'types'; +import type { Asset, BalanceMutation, Pool, Swap, TokenBalance } from 'types'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; +import { areTokensEqual } from 'utilities'; import Supply from '..'; import OPERATION_DETAILS_TEST_IDS from '../../OperationDetails/testIds'; import SWAP_SUMMARY_TEST_IDS from '../../SwapSummary/testIds'; @@ -246,6 +248,32 @@ describe('SupplyForm - Feature flag enabled: integratedSwap', () => { isLoading: false, })); + const fakeSupplyBalanceTokens = fakeAsset.supplyCapTokens + // Add one token too many + .plus(1); + + const fakeSimulatedPool: Pool = { + ...fakePool, + assets: fakePool.assets.map(a => ({ + ...a, + supplyBalanceTokens: areTokensEqual(a.vToken, fakeAsset.vToken) + ? fakeSupplyBalanceTokens + : a.supplyBalanceTokens, + })), + }; + + (useSimulateBalanceMutations as Mock).mockImplementation( + ({ balanceMutations }: { balanceMutations: BalanceMutation[] }) => ({ + isLoading: false, + data: { + pool: + balanceMutations.filter(b => b.amountTokens.isGreaterThan(0)).length > 0 + ? fakeSimulatedPool + : undefined, + }, + }), + ); + const { container, getByTestId, getByText } = renderComponent( , { diff --git a/apps/evm/src/pages/Market/OperationForm/SupplyForm/index.tsx b/apps/evm/src/pages/Market/OperationForm/SupplyForm/index.tsx index fd6d97e490..5c3e7da52d 100644 --- a/apps/evm/src/pages/Market/OperationForm/SupplyForm/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/SupplyForm/index.tsx @@ -20,20 +20,23 @@ import useTokenApproval from 'hooks/useTokenApproval'; import { VError, handleError } from 'libs/errors'; import { useTranslation } from 'libs/translations'; import { useAccountAddress, useAccountChainId, useChainId } from 'libs/wallet'; -import type { Asset, Pool, Swap, SwapError, TokenBalance } from 'types'; +import type { Asset, BalanceMutation, Pool, Swap, SwapError, TokenBalance } from 'types'; import { areTokensEqual, convertMantissaToTokens, convertTokensToMantissa, + getSwapToTokenAmountReceivedTokens, getUniqueTokenBalances, isCollateralActionDisabled, } from 'utilities'; import { ConnectWallet } from 'containers/ConnectWallet'; import { SwitchChainNotice } from 'containers/SwitchChainNotice'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useGetContractAddress } from 'hooks/useGetContractAddress'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; import { useAnalytics } from 'libs/analytics'; -import { AssetInfo } from '../AssetInfo'; +import { ApyBreakdown } from '../ApyBreakdown'; import { OperationDetails } from '../OperationDetails'; import { calculateAmountDollars } from '../calculateAmountDollars'; import Notice from './Notice'; @@ -164,6 +167,28 @@ export const SupplyFormUi: React.FC = ({ asset.supplyCapTokens, ]); + const debouncedFormAmountTokens = useDebounceValue(formValues.amountTokens); + + let toTokenAmountTokens = isUsingSwap + ? getSwapToTokenAmountReceivedTokens(swap) + : debouncedFormAmountTokens; + toTokenAmountTokens = new BigNumber(toTokenAmountTokens || 0); + + const balanceMutations: BalanceMutation[] = [ + { + type: 'asset', + vTokenAddress: asset.vToken.address, + action: 'supply', + amountTokens: toTokenAmountTokens, + }, + ]; + + const { data: getSimulatedPoolData } = useSimulateBalanceMutations({ + pool, + balanceMutations, + }); + const simulatedPool = getSimulatedPoolData?.pool; + const { handleSubmit, isFormValid, formError } = useForm({ asset, fromTokenUserWalletBalanceTokens, @@ -196,7 +221,7 @@ export const SupplyFormUi: React.FC = ({ amountTokens, maxSelected, }: { amountTokens: BigNumber | string; maxSelected: boolean }) => { - if (Number(amountTokens.toString()) > 0) { + if (Number(formValues.amountTokens) > 0) { captureAnalyticEvent( 'supply_amount_set', { @@ -325,7 +350,7 @@ export const SupplyFormUi: React.FC = ({ /> )} - {!isUserConnected && } + {!isUserConnected && } = ({ @@ -589,9 +614,11 @@ const SupplyForm: React.FC = ({ ], ); + const debouncedFormAmountTokens = useDebounceValue(formValues.amountTokens); + const swapInfo = useGetSwapInfo({ fromToken: formValues.fromToken, - fromTokenAmountTokens: formValues.amountTokens, + fromTokenAmountTokens: debouncedFormAmountTokens, toToken: asset.vToken.underlyingToken, direction: 'exactAmountIn', }); diff --git a/apps/evm/src/pages/Market/OperationForm/SupplyForm/useForm/useFormValidation.ts b/apps/evm/src/pages/Market/OperationForm/SupplyForm/useForm/useFormValidation.ts index 91b2529505..75a1212b45 100644 --- a/apps/evm/src/pages/Market/OperationForm/SupplyForm/useForm/useFormValidation.ts +++ b/apps/evm/src/pages/Market/OperationForm/SupplyForm/useForm/useFormValidation.ts @@ -98,7 +98,7 @@ const useFormValidation = ({ } const toTokensAmountSuppliedTokens = isUsingSwap - ? getSwapToTokenAmountReceivedTokens(swap).swapToTokenAmountReceivedTokens + ? getSwapToTokenAmountReceivedTokens(swap) : fromTokenAmountTokens; if ( diff --git a/apps/evm/src/pages/Market/OperationForm/WithdrawForm/index.tsx b/apps/evm/src/pages/Market/OperationForm/WithdrawForm/index.tsx index ea73cf9f6c..995b52d626 100644 --- a/apps/evm/src/pages/Market/OperationForm/WithdrawForm/index.tsx +++ b/apps/evm/src/pages/Market/OperationForm/WithdrawForm/index.tsx @@ -17,7 +17,7 @@ import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; import { VError } from 'libs/errors'; import { useTranslation } from 'libs/translations'; import { useAccountAddress } from 'libs/wallet'; -import type { Asset, Pool } from 'types'; +import type { Asset, BalanceMutation, Pool } from 'types'; import { calculateHealthFactor, convertTokensToMantissa } from 'utilities'; import { NULL_ADDRESS } from 'constants/address'; @@ -26,9 +26,11 @@ import { HEALTH_FACTOR_SAFE_MAX_THRESHOLD, } from 'constants/healthFactor'; import { ConnectWallet } from 'containers/ConnectWallet'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useGetContractAddress } from 'hooks/useGetContractAddress'; +import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; import { useAnalytics } from 'libs/analytics'; -import { AssetInfo } from '../AssetInfo'; +import { ApyBreakdown } from '../ApyBreakdown'; import { OperationDetails } from '../OperationDetails'; import { calculateAmountDollars } from '../calculateAmountDollars'; import SubmitSection from './SubmitSection'; @@ -140,6 +142,7 @@ export const WithdrawFormUi: React.FC = ({ if ( !asset.isCollateralOfUser || + asset.userCollateralFactor === 0 || !pool.userBorrowLimitCents || !pool.userLiquidationThresholdCents || !pool.userBorrowBalanceCents || @@ -201,6 +204,24 @@ export const WithdrawFormUi: React.FC = ({ return [maxTokens, safeMaxTokens, moderateRiskMaxTokens]; }, [asset, pool]); + const _debouncedInputAmountTokens = useDebounceValue(formValues.amountTokens); + const debouncedInputAmountTokens = new BigNumber(_debouncedInputAmountTokens || 0); + + const balanceMutations: BalanceMutation[] = [ + { + type: 'asset', + vTokenAddress: asset.vToken.address, + action: 'withdraw', + amountTokens: debouncedInputAmountTokens, + }, + ]; + + const { data: getSimulatedPoolData } = useSimulateBalanceMutations({ + pool, + balanceMutations, + }); + const simulatedPool = getSimulatedPoolData?.pool; + const { handleSubmit, isFormValid, formError } = useForm({ asset, poolName: pool.name, @@ -223,7 +244,7 @@ export const WithdrawFormUi: React.FC = ({ amountTokens, maxSelected, }: { amountTokens: BigNumber | string; maxSelected: boolean; selectedPercentage?: number }) => { - if (Number(amountTokens.toString()) > 0) { + if (Number(formValues.amountTokens) > 0) { captureAnalyticEvent( 'withdraw_amount_set', { @@ -303,7 +324,13 @@ export const WithdrawFormUi: React.FC = ({ } /> - {!isUserConnected && } + {!isUserConnected && ( + + )} = ({ )} {shouldAskUserRiskAcknowledgement && ( @@ -421,7 +448,7 @@ const WithdrawForm: React.FC = ({ asset, pool, onSubmitSucces const { isDelegateApproved, isDelegateApprovedLoading, - isUseUpdatePoolDelegateStatusLoading, + isDelegateStatusLoading, updatePoolDelegateStatus, } = useDelegateApproval({ delegateeAddress: nativeTokenGatewayContractAddress || NULL_ADDRESS, @@ -469,7 +496,7 @@ const WithdrawForm: React.FC = ({ asset, pool, onSubmitSucces isSubmitting={isWithdrawLoading} isDelegateApproved={isDelegateApproved} isDelegateApprovedLoading={isDelegateApprovedLoading} - isApproveDelegateLoading={isUseUpdatePoolDelegateStatusLoading} + isApproveDelegateLoading={isDelegateStatusLoading} approveDelegateAction={() => updatePoolDelegateStatus({ approvedStatus: true })} /> ); diff --git a/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap index fd1c9c34da..b2fb71a0fa 100644 --- a/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Market > displays content correctly 1`] = `"Supplying XVS to the Venus pool will enable you to borrow tokens from this pool exclusively. Show all marketsSupplyWithdrawBorrowRepayCollateralMAXSupply APY0.05%Distribution APY (XVS)0.12%Total APY0.17%Connect walletSupply info0%Total supplied> $100T / > $100T> 100T / > 100T XVSCurrent APY0.17%Borrow info< 0.01%Total borrowed$2.36M / > $100T1.85M / > 100T XVSCurrent APY-6.48%Liquidation Threshold50%Liquidation Penalty4%Interest Rate ModelUtilization rateBorrow APYSupply APYE-mode infoStablecoinsCollateralBorrowableCollateralBorrowableMax LTV60%Liquidation threshold62%Liquidation penalty0%GameFiCollateralBorrowableCollateralBorrowableMax LTV60%Liquidation threshold62%Liquidation penalty0%Market infoDaily supplying interests$3.98Daily borrowing interests-$44.81Daily XVS distributed19.99MReserve factor25%Exchange rate1 XVS=49.589181 vXVS"`; +exports[`Market > displays content correctly 1`] = `"Supplying XVS to the Venus pool will enable you to borrow tokens from this pool exclusively. Show all marketsSupplyWithdrawBorrowRepayCollateralMAXSupply APY0.05%Distribution APY0.12%Total APY0.17%Connect walletSupply info0%Total supplied> $100T / > $100T> 100T / > 100T XVSCurrent APY0.17%Borrow info< 0.01%Total borrowed$2.36M / > $100T1.85M / > 100T XVSCurrent APY-6.48%Liquidation Threshold50%Liquidation Penalty4%Interest Rate ModelUtilization rateBorrow APYSupply APYE-mode infoStablecoinsCollateralBorrowableCollateralBorrowableMax LTV60%Liquidation threshold62%Liquidation penalty0%GameFiCollateralBorrowableCollateralBorrowableMax LTV60%Liquidation threshold62%Liquidation penalty0%Market infoDaily supplying interests$3.98Daily borrowing interests-$44.81Daily XVS distributed19.99MReserve factor25%Exchange rate1 XVS=49.589181 vXVS"`; diff --git a/apps/evm/src/pages/Swap/index.tsx b/apps/evm/src/pages/Swap/index.tsx index 5515a1efb8..0dbb867528 100644 --- a/apps/evm/src/pages/Swap/index.tsx +++ b/apps/evm/src/pages/Swap/index.tsx @@ -231,6 +231,7 @@ const SwapPageUi: React.FC = ({ } data-testid={TEST_IDS.fromTokenSelectTokenTextField} disabled={isSubmitting} + displayCommonTokenButtons onChange={onFromInputChange} onChangeSelectedToken={token => setFormValues(currentFormValues => ({ @@ -288,6 +289,7 @@ const SwapPageUi: React.FC = ({ value={formValues.toTokenAmountTokens} disabled={isSubmitting} data-testid={TEST_IDS.toTokenSelectTokenTextField} + displayCommonTokenButtons onChange={amount => setFormValues(currentFormValues => ({ ...currentFormValues, diff --git a/apps/evm/src/pages/Vai/Borrow/__tests__/index.spec.tsx b/apps/evm/src/pages/Vai/Borrow/__tests__/index.spec.tsx index 99b72a52a7..15d2af6123 100644 --- a/apps/evm/src/pages/Vai/Borrow/__tests__/index.spec.tsx +++ b/apps/evm/src/pages/Vai/Borrow/__tests__/index.spec.tsx @@ -180,7 +180,10 @@ describe('Borrow', () => { ({ balanceMutations }: { balanceMutations: BalanceMutation[] }) => ({ isLoading: false, data: { - pool: balanceMutations.length > 0 ? customFakePool : fakePool, + pool: + balanceMutations.filter(b => b.amountTokens.isGreaterThan(0)).length > 0 + ? customFakePool + : undefined, }, }), ); diff --git a/apps/evm/src/pages/Vai/Borrow/index.tsx b/apps/evm/src/pages/Vai/Borrow/index.tsx index d88c37f2fd..0667c12bee 100644 --- a/apps/evm/src/pages/Vai/Borrow/index.tsx +++ b/apps/evm/src/pages/Vai/Borrow/index.tsx @@ -37,9 +37,10 @@ import { HEALTH_FACTOR_MODERATE_THRESHOLD, HEALTH_FACTOR_SAFE_MAX_THRESHOLD, } from 'constants/healthFactor'; -import { AccountData } from 'containers/AccountData2'; +import { AccountData } from 'containers/AccountData'; import { RhfSubmitButton, RhfTokenTextField } from 'containers/Form'; import { useChain } from 'hooks/useChain'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useSimulateBalanceMutations } from 'hooks/useSimulateBalanceMutations'; import type { BalanceMutation } from 'types'; import TEST_IDS from './testIds'; @@ -137,17 +138,16 @@ export const Borrow: React.FC = () => { }); const inputValue = watch('amountTokens'); - const inputAmountTokens = new BigNumber(inputValue || 0); + const _debouncedInputAmountTokens = useDebounceValue(inputValue); + const debouncedInputAmountTokens = new BigNumber(_debouncedInputAmountTokens || 0); - const balanceMutations: BalanceMutation[] = []; - - if (inputAmountTokens.isGreaterThan(0)) { - balanceMutations.push({ + const balanceMutations: BalanceMutation[] = [ + { type: 'vai', - amountTokens: new BigNumber(inputAmountTokens), + amountTokens: debouncedInputAmountTokens, action: 'borrow', - }); - } + }, + ]; const { data: getSimulatedPoolData } = useSimulateBalanceMutations({ pool: legacyPool, @@ -156,8 +156,8 @@ export const Borrow: React.FC = () => { const simulatedPool = getSimulatedPoolData?.pool; const feeTokens = useMemo( - () => feePercentage && inputAmountTokens.multipliedBy(feePercentage).dividedBy(100), - [feePercentage, inputAmountTokens], + () => feePercentage && debouncedInputAmountTokens.multipliedBy(feePercentage).dividedBy(100), + [feePercentage, debouncedInputAmountTokens], ); const readableFee = useMemo(() => { diff --git a/apps/evm/src/pages/Vai/Repay/index.tsx b/apps/evm/src/pages/Vai/Repay/index.tsx index a183c23e48..2bd33cfd46 100644 --- a/apps/evm/src/pages/Vai/Repay/index.tsx +++ b/apps/evm/src/pages/Vai/Repay/index.tsx @@ -13,10 +13,11 @@ import { } from 'components'; import { NULL_ADDRESS } from 'constants/address'; import MAX_UINT256 from 'constants/maxUint256'; -import { AccountData } from 'containers/AccountData2'; +import { AccountData } from 'containers/AccountData'; import { RhfSubmitButton, RhfTokenTextField } from 'containers/Form'; import { useChain } from 'hooks/useChain'; import useConvertMantissaToReadableTokenString from 'hooks/useConvertMantissaToReadableTokenString'; +import useDebounceValue from 'hooks/useDebounceValue'; import { useGetContractAddress } from 'hooks/useGetContractAddress'; import useTokenApproval from 'hooks/useTokenApproval'; import { handleError } from 'libs/errors'; @@ -108,25 +109,29 @@ export const Repay: React.FC = () => { }); const inputValue = watch('amountTokens'); - const inputAmountTokens = new BigNumber(inputValue || 0); + const _debouncedInputAmountTokens = useDebounceValue(inputValue); + const debouncedInputAmountTokens = new BigNumber(_debouncedInputAmountTokens || 0); - const balanceMutations: BalanceMutation[] = []; - - if (inputAmountTokens.isGreaterThan(0)) { - balanceMutations.push({ + const balanceMutations: BalanceMutation[] = [ + { type: 'vai', - amountTokens: new BigNumber(inputAmountTokens), + amountTokens: debouncedInputAmountTokens, action: 'repay', - }); - } + }, + ]; - const { data: getSimulatedPoolData } = useGetSimulatedPool({ - pool: legacyPool, - balanceMutations, - }); + const { data: getSimulatedPoolData } = useGetSimulatedPool( + { + pool: legacyPool, + balanceMutations, + }, + { + enabled: debouncedInputAmountTokens.isGreaterThan(0), + }, + ); const simulatedPool = getSimulatedPoolData?.pool; - const isRepayingFullLoan = !!userVaiBorrowBalanceTokens?.isEqualTo(inputAmountTokens); + const isRepayingFullLoan = !!userVaiBorrowBalanceTokens?.isEqualTo(debouncedInputAmountTokens); const errorMessage = useMemo(() => { const errorCode = formState.errors.amountTokens?.message; diff --git a/apps/evm/src/utilities/convertMantissaToTokens.ts b/apps/evm/src/utilities/convertMantissaToTokens.ts index 73a8b4c29a..2225d52a92 100644 --- a/apps/evm/src/utilities/convertMantissaToTokens.ts +++ b/apps/evm/src/utilities/convertMantissaToTokens.ts @@ -30,7 +30,7 @@ export function convertMantissaToTokens({ } const valueTokens = new BigNumber(typeof value === 'bigint' ? value.toString() : value.toFixed()) - .dividedBy(10 ** token.decimals) + .shiftedBy(-token.decimals) .decimalPlaces(token.decimals); if (returnInReadableFormat) { diff --git a/apps/evm/src/utilities/convertTokensToMantissa.ts b/apps/evm/src/utilities/convertTokensToMantissa.ts index 36a6a544c5..ce7701b8bf 100644 --- a/apps/evm/src/utilities/convertTokensToMantissa.ts +++ b/apps/evm/src/utilities/convertTokensToMantissa.ts @@ -3,6 +3,6 @@ import type BigNumber from 'bignumber.js'; import type { Token } from 'types'; export const convertTokensToMantissa = ({ value, token }: { value: BigNumber; token: Token }) => - value.multipliedBy(10 ** token.decimals).dp(0); + value.shiftedBy(token.decimals).dp(0); export default convertTokensToMantissa; diff --git a/apps/evm/src/utilities/getSwapToTokenAmountReceived/index.ts b/apps/evm/src/utilities/getSwapToTokenAmountReceived/index.ts index 2f40f53025..ab73328f44 100644 --- a/apps/evm/src/utilities/getSwapToTokenAmountReceived/index.ts +++ b/apps/evm/src/utilities/getSwapToTokenAmountReceived/index.ts @@ -1,21 +1,18 @@ -import type BigNumber from 'bignumber.js'; import type { Swap } from 'types'; import { convertMantissaToTokens } from 'utilities'; export const getSwapToTokenAmountReceivedTokens = (swap?: Swap) => { - let swapToTokenAmountReceivedTokens: BigNumber | undefined; - if (swap) { const swapToTokenAmountReceivedMantissa = swap.direction === 'exactAmountOut' ? swap.toTokenAmountReceivedMantissa : swap.expectedToTokenAmountReceivedMantissa; - swapToTokenAmountReceivedTokens = convertMantissaToTokens({ + return convertMantissaToTokens({ value: swapToTokenAmountReceivedMantissa, token: swap.toToken, }); } - return { swapToTokenAmountReceivedTokens }; + return undefined; };