diff --git a/.circleci/config.yml b/.circleci/config.yml index 58291091d..650736c92 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,17 +22,19 @@ commands: - &yarn_state_cache yarn-install-state-{{ .Environment.NODE_MODULES_VERSION }}-{{ checksum "/tmp/month.txt" }}-{{ checksum "yarn.lock" }} - yarn-install-state-{{ .Environment.NODE_MODULES_VERSION }}-{{ checksum "/tmp/month.txt" }}- - - run: - name: Fallback to installing node modules in case of cache corruption - command: |- - # Check if we restored cache and have node_modules already - if [ -f "node_modules/.yarn-state.yml" ] && [ -f ".yarn/install-state.gz" ]; then - echo "node_modules restored from cache" - exit 0 - else - echo "node_modules could not be restored from cache, activating fallback installation" - yarn install --immutable - fi + - run: yarn install --immutable + + # - run: + # name: Fallback to installing node modules in case of cache corruption + # command: |- + # # Check if we restored cache and have node_modules already + # if [ -f "node_modules/.yarn-state.yml" ] && [ -f ".yarn/instalUSDC_l-state.gz" ]; then + # echo "node_modules restored from cache" + # exit 0 + # else + # echo "node_modules could not be restored from cache, activating fallback installation" + # yarn install --immutable + # fi - save_cache: key: *yarn_cache diff --git a/liquidity/components/ClaimModal/ClaimModal.tsx b/liquidity/components/ClaimModal/ClaimModal.tsx index 42c659c7f..0a24d9c2d 100644 --- a/liquidity/components/ClaimModal/ClaimModal.tsx +++ b/liquidity/components/ClaimModal/ClaimModal.tsx @@ -13,14 +13,12 @@ import { useLiquidityPosition } from '@snx-v3/useLiquidityPosition'; import { type PositionPageSchemaType, useParams } from '@snx-v3/useParams'; import { useSystemToken } from '@snx-v3/useSystemToken'; import { wei } from '@synthetixio/wei'; -import { useQueryClient } from '@tanstack/react-query'; import { useCallback, useContext, useMemo } from 'react'; import { LiquidityPositionUpdated } from '../../ui/src/components/Manage/LiquidityPositionUpdated'; export function ClaimModal({ onClose }: { onClose: () => void }) { const [params] = useParams(); const { debtChange, setDebtChange } = useContext(ManagePositionContext); - const queryClient = useQueryClient(); const { network } = useNetwork(); const { data: collateralType } = useCollateralType(params.collateralSymbol); const { data: liquidityPosition } = useLiquidityPosition({ @@ -58,25 +56,12 @@ export function ClaimModal({ onClose }: { onClose: () => void }) { const execBorrowWithErrorParser = useCallback(async () => { try { await execBorrow(); - - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - exact: false, - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'AccountCollateralUnlockDate'], - }); - setDebtChange(ZEROWEI); } catch (error: any) { const contractError = errorParser(error); if (contractError) { console.error(new Error(contractError.name), contractError); } - toast.closeAll(); toast({ title: isBorrow ? 'Borrow' : 'Claim' + ' failed', @@ -91,16 +76,7 @@ export function ClaimModal({ onClose }: { onClose: () => void }) { }); throw Error(isBorrow ? 'Borrow' : 'Claim' + ' failed', { cause: error }); } - }, [ - execBorrow, - queryClient, - network?.id, - network?.preset, - setDebtChange, - errorParser, - toast, - isBorrow, - ]); + }, [execBorrow, setDebtChange, errorParser, toast, isBorrow]); const symbol = network?.preset === 'andromeda' ? collateralType?.displaySymbol : systemToken?.symbol; diff --git a/liquidity/components/ClaimModal/package.json b/liquidity/components/ClaimModal/package.json index e4d21f0b4..f7170af62 100644 --- a/liquidity/components/ClaimModal/package.json +++ b/liquidity/components/ClaimModal/package.json @@ -19,7 +19,6 @@ "@snx-v3/useParams": "workspace:*", "@snx-v3/useSystemToken": "workspace:*", "@synthetixio/wei": "^2.74.4", - "@tanstack/react-query": "^5.8.3", "react": "^18.2.0" } } diff --git a/liquidity/components/DepositModal/DepositModal.tsx b/liquidity/components/DepositModal/DepositModal.tsx index 029288255..ff69d8c59 100644 --- a/liquidity/components/DepositModal/DepositModal.tsx +++ b/liquidity/components/DepositModal/DepositModal.tsx @@ -17,10 +17,9 @@ import { useLiquidityPosition } from '@snx-v3/useLiquidityPosition'; import { type PositionPageSchemaType, useParams } from '@snx-v3/useParams'; import { usePool } from '@snx-v3/usePools'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; -import { useSynthTokens } from '@snx-v3/useSynthTokens'; +import { useSynthToken } from '@snx-v3/useSynthToken'; import { useWrapEth } from '@snx-v3/useWrapEth'; import { Wei, wei } from '@synthetixio/wei'; -import { useQueryClient } from '@tanstack/react-query'; import { useMachine } from '@xstate/react'; import { ethers } from 'ethers'; import React from 'react'; @@ -38,7 +37,6 @@ export function DepositModal({ title?: string; }) { const [params, setParams] = useParams(); - const queryClient = useQueryClient(); const { network } = useNetwork(); const { collateralChange, setCollateralChange } = React.useContext(ManagePositionContext); const { data: CoreProxy } = useCoreProxy(); @@ -50,12 +48,7 @@ export function DepositModal({ collateralType, }); - const { data: synthTokens } = useSynthTokens(); - const synth = synthTokens?.find( - (synth) => - collateralType?.tokenAddress?.toLowerCase() === synth?.address?.toLowerCase() || - collateralType?.tokenAddress?.toLowerCase() === synth?.token?.address.toLowerCase() - ); + const { data: synthToken } = useSynthToken(collateralType); const currentCollateral = liquidityPosition?.collateralAmount ?? ZEROWEI; const availableCollateral = liquidityPosition?.availableCollateral ?? ZEROWEI; @@ -87,16 +80,16 @@ export function DepositModal({ //Collateral Approval const { approve, requireApproval } = useApprove({ contractAddress: - network?.preset === 'andromeda' ? synth?.token?.address : collateralType?.tokenAddress, + network?.preset === 'andromeda' ? synthToken?.token?.address : collateralType?.tokenAddress, amount: collateralChange.lte(availableCollateral) ? wei(0).toBN() - : network?.preset === 'andromeda' && synth + : network?.preset === 'andromeda' && synthToken && synthToken.token ? collateralChange .sub(availableCollateral) .toBN() // Reduce precision for approval of USDC on Andromeda - .mul(ethers.utils.parseUnits('1', synth.token.decimals)) + .mul(ethers.utils.parseUnits('1', synthToken.token.decimals)) .div(D18) : collateralChange.sub(availableCollateral).toBN(), spender: network?.preset === 'andromeda' ? SpotMarketProxy?.address : CoreProxy?.address, @@ -118,7 +111,7 @@ export function DepositModal({ accountId: params.accountId, newAccountId, poolId: params.poolId, - collateralTypeAddress: synth?.token.address, + collateralTypeAddress: synthToken?.token?.address, collateralChange, currentCollateral, availableCollateral, @@ -169,7 +162,9 @@ export function DepositModal({ toast({ title: `Approve collateral for transfer`, description: `Approve ${ - network?.preset === 'andromeda' ? synth?.token?.address : collateralType?.tokenAddress + network?.preset === 'andromeda' + ? synthToken?.token?.address + : collateralType?.tokenAddress } transfer`, status: 'info', variant: 'left-accent', @@ -220,27 +215,6 @@ export function DepositModal({ await execDeposit(); } - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'EthBalance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TransferableSynthetix'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Allowance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPositions'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Accounts'], - }); setCollateralChange(ZEROWEI); toast.closeAll(); @@ -256,7 +230,6 @@ export function DepositModal({ if (contractError) { console.error(new Error(contractError.name), contractError); } - toast.closeAll(); toast({ title: 'Could not complete locking collateral', diff --git a/liquidity/components/DepositModal/StataDepositModal.tsx b/liquidity/components/DepositModal/StataDepositModal.tsx index cde0fb748..7252f4738 100644 --- a/liquidity/components/DepositModal/StataDepositModal.tsx +++ b/liquidity/components/DepositModal/StataDepositModal.tsx @@ -7,7 +7,6 @@ import { currency } from '@snx-v3/format'; import { ManagePositionContext } from '@snx-v3/ManagePositionContext'; import { Multistep } from '@snx-v3/Multistep'; import { useApprove } from '@snx-v3/useApprove'; -import { useNetwork } from '@snx-v3/useBlockchain'; import { useCollateralType } from '@snx-v3/useCollateralTypes'; import { useContractErrorParser } from '@snx-v3/useContractErrorParser'; import { useConvertStataUSDC } from '@snx-v3/useConvertStataUSDC'; @@ -18,11 +17,10 @@ import { usePool } from '@snx-v3/usePools'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { useStaticAaveUSDC } from '@snx-v3/useStaticAaveUSDC'; import { useStaticAaveUSDCRate } from '@snx-v3/useStaticAaveUSDCRate'; -import { useSynthTokens } from '@snx-v3/useSynthTokens'; +import { useSynthToken } from '@snx-v3/useSynthToken'; import { useTokenBalance } from '@snx-v3/useTokenBalance'; import { useUSDC } from '@snx-v3/useUSDC'; import { Wei, wei } from '@synthetixio/wei'; -import { useQueryClient } from '@tanstack/react-query'; import { useMachine } from '@xstate/react'; import { ethers } from 'ethers'; import React from 'react'; @@ -41,8 +39,6 @@ export function StataDepositModal({ title?: string; }) { const [params, setParams] = useParams(); - const queryClient = useQueryClient(); - const { network } = useNetwork(); const { collateralChange, setCollateralChange } = React.useContext(ManagePositionContext); const { data: SpotMarketProxy } = useSpotMarketProxy(); @@ -57,12 +53,7 @@ export function StataDepositModal({ const { data: staticAaveUSDCRate } = useStaticAaveUSDCRate(); // log('staticAaveUSDCRate', staticAaveUSDCRate, `${staticAaveUSDCRate}`); - const { data: synthTokens } = useSynthTokens(); - const synth = synthTokens?.find( - (synth) => - collateralType?.tokenAddress?.toLowerCase() === synth?.address?.toLowerCase() || - collateralType?.tokenAddress?.toLowerCase() === synth?.token?.address.toLowerCase() - ); + const { data: synthToken } = useSynthToken(collateralType); const { data: stataUSDCTokenBalanceRaw } = useTokenBalance(StaticAaveUSDC?.address); // log( @@ -130,17 +121,18 @@ export function StataDepositModal({ ? collateralChange.sub(liquidityPosition.availableCollateral) : wei(0); const { approve: approveStata, requireApproval: requireApprovalStata } = useApprove({ - contractAddress: synth?.token?.address, - amount: synth - ? stataApprovalNeeded - .toBN() - .mul(ethers.utils.parseUnits('1', synth.token.decimals)) - .div(D18) - - // extra 1% approval - .mul(101) - .div(100) - : undefined, + contractAddress: synthToken?.token?.address, + amount: + synthToken && synthToken.token + ? stataApprovalNeeded + .toBN() + .mul(ethers.utils.parseUnits('1', synthToken.token.decimals)) + .div(D18) + + // extra 1% approval + .mul(101) + .div(100) + : undefined, spender: SpotMarketProxy?.address, }); // log('requireApprovalStata', requireApprovalStata); @@ -152,7 +144,7 @@ export function StataDepositModal({ accountId: params.accountId, newAccountId, poolId: params.poolId, - collateralTypeAddress: synth?.token.address, + collateralTypeAddress: synthToken?.token?.address, collateralChange, currentCollateral: liquidityPosition?.collateralAmount, availableCollateral: liquidityPosition?.availableCollateral, @@ -196,6 +188,7 @@ export function StataDepositModal({ ), status: 'error', variant: 'left-accent', + duration: 3_600_000, }); throw Error('Approve failed', { cause: error }); } @@ -224,6 +217,7 @@ export function StataDepositModal({ ), status: 'error', variant: 'left-accent', + duration: 3_600_000, }); throw Error('Wrap USDC failed', { cause: error }); } @@ -236,7 +230,7 @@ export function StataDepositModal({ } toast({ title: `Approve collateral for transfer`, - description: `Approve ${synth?.token?.address} transfer`, + description: `Approve ${synthToken?.token?.address} transfer`, status: 'info', variant: 'left-accent', }); @@ -286,24 +280,6 @@ export function StataDepositModal({ await depositBaseAndromeda(); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TransferableSynthetix'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Allowance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPositions'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Accounts'], - }); setCollateralChange(ZEROWEI); toast.closeAll(); diff --git a/liquidity/components/DepositModal/package.json b/liquidity/components/DepositModal/package.json index 2175b34e1..7144d86f8 100644 --- a/liquidity/components/DepositModal/package.json +++ b/liquidity/components/DepositModal/package.json @@ -26,12 +26,11 @@ "@snx-v3/useSpotMarketProxy": "workspace:*", "@snx-v3/useStaticAaveUSDC": "workspace:*", "@snx-v3/useStaticAaveUSDCRate": "workspace:*", - "@snx-v3/useSynthTokens": "workspace:*", + "@snx-v3/useSynthToken": "workspace:*", "@snx-v3/useTokenBalance": "workspace:*", "@snx-v3/useUSDC": "workspace:*", "@snx-v3/useWrapEth": "workspace:*", "@synthetixio/wei": "^2.74.4", - "@tanstack/react-query": "^5.8.3", "@xstate/react": "^3.2.2", "ethers": "^5.7.2", "react": "^18.2.0", diff --git a/liquidity/components/RepayModal/RepayModal.tsx b/liquidity/components/RepayModal/RepayModal.tsx index 9701d9d27..f8751c19c 100644 --- a/liquidity/components/RepayModal/RepayModal.tsx +++ b/liquidity/components/RepayModal/RepayModal.tsx @@ -19,7 +19,6 @@ import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { useSynthTokens } from '@snx-v3/useSynthTokens'; import { useSystemToken } from '@snx-v3/useSystemToken'; import { useTokenBalance } from '@snx-v3/useTokenBalance'; -import { useQueryClient } from '@tanstack/react-query'; import { useMachine } from '@xstate/react'; import React from 'react'; import { LiquidityPositionUpdated } from '../../ui/src/components/Manage/LiquidityPositionUpdated'; @@ -30,7 +29,6 @@ export function RepayModal({ onClose }: { onClose: () => void }) { const [params] = useParams(); const { network } = useNetwork(); - const queryClient = useQueryClient(); const { data: collateralType } = useCollateralType(params.collateralSymbol); @@ -135,18 +133,6 @@ export function RepayModal({ onClose }: { onClose: () => void }) { await execRepay(); } - await Promise.all([ - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }), - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Allowance'], - }), - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - }), - ]); - setDebtChange(ZEROWEI); toast.closeAll(); diff --git a/liquidity/components/RepayModal/package.json b/liquidity/components/RepayModal/package.json index 2261b35e2..63c56aa68 100644 --- a/liquidity/components/RepayModal/package.json +++ b/liquidity/components/RepayModal/package.json @@ -25,7 +25,6 @@ "@snx-v3/useSynthTokens": "workspace:*", "@snx-v3/useSystemToken": "workspace:*", "@snx-v3/useTokenBalance": "workspace:*", - "@tanstack/react-query": "^5.8.3", "@xstate/react": "^3.2.2", "react": "^18.2.0", "xstate": "^4.38.3" diff --git a/liquidity/components/UndelegateModal/UndelegateModal.tsx b/liquidity/components/UndelegateModal/UndelegateModal.tsx index 87542adcd..ba8c94268 100644 --- a/liquidity/components/UndelegateModal/UndelegateModal.tsx +++ b/liquidity/components/UndelegateModal/UndelegateModal.tsx @@ -17,7 +17,6 @@ import { useUndelegate } from '@snx-v3/useUndelegate'; import { useUndelegateBaseAndromeda } from '@snx-v3/useUndelegateBaseAndromeda'; import { useUSDC } from '@snx-v3/useUSDC'; import { Wei, wei } from '@synthetixio/wei'; -import { useQueryClient } from '@tanstack/react-query'; import { useMachine } from '@xstate/react'; import React from 'react'; import { ChangeStat } from '../../ui/src/components/ChangeStat/ChangeStat'; @@ -30,9 +29,6 @@ export function UndelegateModal({ onClose }: { onClose: () => void }) { const [params] = useParams(); const { collateralChange, setCollateralChange } = React.useContext(ManagePositionContext); const { network } = useNetwork(); - - const queryClient = useQueryClient(); - const { data: collateralType } = useCollateralType(params.collateralSymbol); const { data: liquidityPosition } = useLiquidityPosition({ accountId: params.accountId, @@ -98,21 +94,6 @@ export function UndelegateModal({ onClose }: { onClose: () => void }) { await execUndelegate(); } - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - exact: false, - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPositions'], - }); - queryClient.invalidateQueries({ - queryKey: [ - `${network?.id}-${network?.preset}`, - 'AccountCollateralUnlockDate', - { accountId: params.accountId }, - ], - }); - setCollateralChange(ZEROWEI); toast.closeAll(); diff --git a/liquidity/components/UndelegateModal/package.json b/liquidity/components/UndelegateModal/package.json index 44e6a64b3..aed8b5997 100644 --- a/liquidity/components/UndelegateModal/package.json +++ b/liquidity/components/UndelegateModal/package.json @@ -23,7 +23,6 @@ "@snx-v3/useUndelegate": "workspace:*", "@snx-v3/useUndelegateBaseAndromeda": "workspace:*", "@synthetixio/wei": "^2.74.4", - "@tanstack/react-query": "^5.8.3", "@xstate/react": "^3.2.2", "react": "^18.2.0", "xstate": "^4.38.3" diff --git a/liquidity/components/WithdrawModal/WithdrawModal.tsx b/liquidity/components/WithdrawModal/WithdrawModal.tsx index 86aaa3d07..4c174c9ae 100644 --- a/liquidity/components/WithdrawModal/WithdrawModal.tsx +++ b/liquidity/components/WithdrawModal/WithdrawModal.tsx @@ -5,7 +5,7 @@ import { ZEROWEI } from '@snx-v3/constants'; import { ContractError } from '@snx-v3/ContractError'; import { ManagePositionContext } from '@snx-v3/ManagePositionContext'; import { Multistep } from '@snx-v3/Multistep'; -import { useProvider, useNetwork, useWallet } from '@snx-v3/useBlockchain'; +import { useNetwork, useProvider, useWallet } from '@snx-v3/useBlockchain'; import { useCollateralType } from '@snx-v3/useCollateralTypes'; import { useContractErrorParser } from '@snx-v3/useContractErrorParser'; import { useIsSynthStataUSDC } from '@snx-v3/useIsSynthStataUSDC'; @@ -15,7 +15,6 @@ import { useSystemToken } from '@snx-v3/useSystemToken'; import { useUnwrapStataUSDC } from '@snx-v3/useUnwrapStataUSDC'; import { useWithdraw } from '@snx-v3/useWithdraw'; import { useWithdrawBaseAndromeda } from '@snx-v3/useWithdrawBaseAndromeda'; -import { useQueryClient } from '@tanstack/react-query'; import { ethers } from 'ethers'; import React, { useContext, useState } from 'react'; import { LiquidityPositionUpdated } from '../../ui/src/components/Manage/LiquidityPositionUpdated'; @@ -37,7 +36,6 @@ export function WithdrawModal({ const [params] = useParams(); const toast = useToast({ isClosable: true, duration: 9000 }); const { network } = useNetwork(); - const queryClient = useQueryClient(); const { withdrawAmount, setWithdrawAmount } = useContext(ManagePositionContext); @@ -119,22 +117,6 @@ export function WithdrawModal({ }); } - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'AccountSpecificCollateral'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPositions'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'AccountCollateralUnlockDate'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); - setWithdrawAmount(ZEROWEI); } catch (error) { setTxState((state) => ({ diff --git a/liquidity/components/WithdrawModal/package.json b/liquidity/components/WithdrawModal/package.json index ef13c637a..6d3ddcaf1 100644 --- a/liquidity/components/WithdrawModal/package.json +++ b/liquidity/components/WithdrawModal/package.json @@ -21,7 +21,6 @@ "@snx-v3/useUnwrapStataUSDC": "workspace:*", "@snx-v3/useWithdraw": "workspace:*", "@snx-v3/useWithdrawBaseAndromeda": "workspace:*", - "@tanstack/react-query": "^5.8.3", "ethers": "^5.7.2", "react": "^18.2.0" } diff --git a/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js b/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js index 97a7344d0..2a9065e8d 100644 --- a/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js @@ -43,9 +43,20 @@ describe(__filename, () => { cy.get('[data-cy="claim form"]', { timeout: 180_000 }).should('exist'); cy.contains('[data-status="info"]', 'You can take an interest-free loan up to').should('exist'); - cy.get('[data-cy="claim amount input"]').should('exist'); + cy.get('[data-cy="stats collateral"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '150 SNX'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '$0.17'); + cy.get('[data-cy="claim amount input"]').type('10'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]') + .should('exist') + .and('include.text', '$10.17'); + cy.contains( '[data-status="warning"]', 'Assets will be available to withdraw 24 hours after your last interaction with this position.' @@ -66,5 +77,10 @@ describe(__filename, () => { cy.contains('[data-status="success"]', 'Debt successfully Updated', { timeout: 180_000, }).should('exist'); + + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '$10.17'); }); }); diff --git a/liquidity/cypress/cypress/e2e/42161-main/ARB_Claim_rewards.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/ARB_Claim_rewards.e2e.js index 5df071c0c..6e92cc0dd 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/ARB_Claim_rewards.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/ARB_Claim_rewards.e2e.js @@ -36,19 +36,28 @@ describe(__filename, () => { })}` ); - cy.get('[data-cy="claim rewards submit"]').should('be.enabled'); + cy.get('[data-cy="claim rewards submit"]', { timeout: 180_000 }).should('be.enabled'); cy.get('[data-cy="claim rewards submit"]').click(); - cy.get('[data-cy="claim rewards dialog"]').should('exist'); - cy.get('[data-cy="claim rewards info"]') .should('exist') - .and('include.text', 'Claim your rewards'); + .and('include.text', 'Claiming 53.87 ARB') + .and('include.text', 'Claiming 0.55 USDe') + .and('include.text', 'Claiming 0.0078 WETH') + .and('include.text', 'Claiming 0.0000004 tBTC'); + + cy.get('[data-cy="claim rewards info"]', { timeout: 180_000 }) + .should('exist') + .and('include.text', 'Claimed 53.87 ARB') + .and('include.text', 'Claimed 0.55 USDe') + .and('include.text', 'Claimed 0.0078 WETH') + .and('include.text', 'Claimed 0.0000004 tBTC'); + cy.contains('[data-status="success"]', 'Your rewards have been claimed').should('exist'); cy.get('[data-cy="transaction hash"]').should('exist'); - cy.contains('[data-status="success"]', 'Your rewards have been claimed', { - timeout: 180_000, - }).should('exist'); + cy.contains('[data-cy="claim rewards dialog"] button', 'Done').click(); + cy.get('[data-cy="rewards table"]').should('include.text', 'No Rewards Available'); + cy.get('[data-cy="claim rewards submit"]').should('be.disabled'); }); }); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Synths_Unwrapping.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/Synths_Unwrapping.e2e.js index cfdb7aa14..5e5c1eac9 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Synths_Unwrapping.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/Synths_Unwrapping.e2e.js @@ -37,11 +37,15 @@ describe(__filename, () => { .should('exist') .and('include.text', 'Unwrapping 0.00000041 WETH'); - cy.get('[data-cy="unwrap synths info"]', { timeout: 180_000 }).and( + cy.get('[data-cy="unwrap synths info"]', { timeout: 180_000 }).should( 'include.text', 'Unwrapped 0.00000041 WETH' ); cy.contains('[data-status="success"]', 'Your synths have been unwrapped').should('exist'); cy.get('[data-cy="transaction hash"]').should('exist'); + + cy.contains('[data-cy="unwrap synths dialog"] button', 'Done').click(); + cy.get('[data-cy="synths table"]').should('include.text', 'You do not have any synths'); + cy.get('[data-cy="unwrap synths submit"]').should('be.disabled'); }); }); diff --git a/liquidity/cypress/cypress/e2e/42161-main/USDC_Borrow.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/USDC_Borrow.e2e.js index 9798bd3b0..24f41ef84 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/USDC_Borrow.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/USDC_Borrow.e2e.js @@ -43,9 +43,20 @@ describe(__filename, () => { .and('include.text', 'Max'); cy.contains('[data-status="info"]', 'You can take an interest-free loan up to').should('exist'); + cy.get('[data-cy="stats collateral"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '249 USDC'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '$1.28'); cy.get('[data-cy="claim amount input"]').type('10'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]') + .should('exist') + .and('include.text', '$11.28'); + cy.contains( '[data-status="warning"]', 'Assets will be available to withdraw 24 hours after your last interaction with this position.' @@ -66,5 +77,10 @@ describe(__filename, () => { cy.contains('[data-status="success"]', 'Debt successfully Updated', { timeout: 180_000, }).should('exist'); + + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '$11.28'); }); }); diff --git a/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js index c816649b9..b2ab45421 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js @@ -45,8 +45,20 @@ describe(__filename, () => { cy.get('[data-cy="claim form"]', { timeout: 180_000 }).should('exist'); cy.contains('[data-status="info"]', 'You can take an interest-free loan up to').should('exist'); + cy.get('[data-cy="stats collateral"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '10 WETH'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '-$0.0000098'); + cy.get('[data-cy="claim amount input"]').type('100'); + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]') + .should('exist') + .and('include.text', '$100'); + cy.contains( '[data-status="warning"]', 'Assets will be available to withdraw 24 hours after your last interaction with this position.' @@ -67,5 +79,10 @@ describe(__filename, () => { cy.contains('[data-status="success"]', 'Debt successfully Updated', { timeout: 180_000, }).should('exist'); + + cy.get('[data-cy="stats debt"] [data-cy="change stats new"]').should('not.exist'); + cy.get('[data-cy="stats debt"] [data-cy="change stats current"]') + .should('exist') + .and('include.text', '$100'); }); }); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Claim_rewards.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Claim_rewards.e2e.js index 61f69224c..f202fa350 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Claim_rewards.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Claim_rewards.e2e.js @@ -25,7 +25,6 @@ describe(__filename, () => { it(__filename, () => { cy.setEthBalance({ balance: 100 }); - cy.getUSDC({ amount: 500 }); cy.visit( `?${makeSearch({ @@ -37,19 +36,25 @@ describe(__filename, () => { })}` ); - cy.get('[data-cy="claim rewards submit"]', { - timeout: 180_000, - }).should('be.enabled'); + cy.get('[data-cy="claim rewards submit"]', { timeout: 180_000 }).should('be.enabled'); cy.get('[data-cy="claim rewards submit"]').click(); cy.get('[data-cy="claim rewards dialog"]').should('exist'); cy.get('[data-cy="claim rewards info"]') .should('exist') - .and('include.text', 'Claim your rewards'); + .and('include.text', 'Claiming 640.24 USDC') + .and('include.text', 'Claiming 449.22 SNX'); - cy.contains('[data-status="success"]', 'Your rewards have been claimed', { - timeout: 180_000, - }).should('exist'); + cy.get('[data-cy="claim rewards info"]', { timeout: 180_000 }) + .should('include.text', 'Claimed 640.24 USDC') + .and('include.text', 'Claimed 449.22 SNX'); + + cy.contains('[data-status="success"]', 'Your rewards have been claimed').should('exist'); + cy.get('[data-cy="transaction hash"]').should('exist'); + + cy.contains('[data-cy="claim rewards dialog"] button', 'Done').click(); + cy.get('[data-cy="rewards table"]').should('include.text', 'No Rewards Available'); + cy.get('[data-cy="claim rewards submit"]').should('be.disabled'); }); }); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Claim_rewards.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Claim_rewards.e2e.js index 6d98a9bf6..ef2bd234e 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Claim_rewards.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Claim_rewards.e2e.js @@ -26,7 +26,6 @@ describe(__filename, () => { it(__filename, () => { cy.setEthBalance({ balance: 100 }); - cy.getUSDC({ amount: 500 }); cy.visit( `?${makeSearch({ @@ -38,19 +37,25 @@ describe(__filename, () => { })}` ); - cy.get('[data-cy="claim rewards submit"]', { - timeout: 180_000, - }).should('be.enabled'); + cy.get('[data-cy="claim rewards submit"]', { timeout: 180_000 }).should('be.enabled'); cy.get('[data-cy="claim rewards submit"]').click(); cy.get('[data-cy="claim rewards dialog"]').should('exist'); cy.get('[data-cy="claim rewards info"]') .should('exist') - .and('include.text', 'Claim your rewards'); + .and('include.text', 'Claiming 0.016 USDC') + .and('include.text', 'Claiming 0.013 SNX'); - cy.contains('[data-status="success"]', 'Your rewards have been claimed', { - timeout: 180_000, - }).should('exist'); + cy.get('[data-cy="claim rewards info"]', { timeout: 180_000 }) + .should('include.text', 'Claimed 0.016 USDC') + .and('include.text', 'Claimed 0.013 SNX'); + + cy.contains('[data-status="success"]', 'Your rewards have been claimed').should('exist'); + cy.get('[data-cy="transaction hash"]').should('exist'); + + cy.contains('[data-cy="claim rewards dialog"] button', 'Done').click(); + cy.get('[data-cy="rewards table"]').should('include.text', 'No Rewards Available'); + cy.get('[data-cy="claim rewards submit"]').should('be.disabled'); }); }); diff --git a/liquidity/cypress/cypress/support/commands/approveCollateral.js b/liquidity/cypress/cypress/support/commands/approveCollateral.js index 483a2a3d6..048831a86 100644 --- a/liquidity/cypress/cypress/support/commands/approveCollateral.js +++ b/liquidity/cypress/cypress/support/commands/approveCollateral.js @@ -20,7 +20,8 @@ export async function approveCollateral({ signer ); - const tx = await contract.approve(meta.contracts[spender], ethers.constants.MaxUint256); - const result = await tx.wait(); - console.log('approveCollateral', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const txn = await contract.approve(meta.contracts[spender], ethers.constants.MaxUint256); + const receipt = await txn.wait(); + console.log('approveCollateral', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/borrowUsd.js b/liquidity/cypress/cypress/support/commands/borrowUsd.js index 6c00a3556..7485a3d95 100644 --- a/liquidity/cypress/cypress/support/commands/borrowUsd.js +++ b/liquidity/cypress/cypress/support/commands/borrowUsd.js @@ -31,9 +31,9 @@ export async function borrowUsd({ console.log('borrowUsd ERROR', parseContractError({ error, AllErrors })); return ethers.BigNumber.from(10_000_000); }); - const tx = await CoreProxyContract.mintUsd(...args, { gasLimit: gasLimit.mul(2) }); - const result = await tx.wait(); - console.log('borrowUsd', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const txn = await CoreProxyContract.mintUsd(...args, { gasLimit: gasLimit.mul(2) }); + const receipt = await txn.wait(); + console.log('borrowUsd', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const debt = await CoreProxyContract.callStatic.getPositionDebt( accountId, @@ -41,5 +41,5 @@ export async function borrowUsd({ config.tokenAddress ); console.log('borrowUsd', { debt }); - return debt; + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/clearDebt.js b/liquidity/cypress/cypress/support/commands/clearDebt.js index 20f87adec..d56853de8 100644 --- a/liquidity/cypress/cypress/support/commands/clearDebt.js +++ b/liquidity/cypress/cypress/support/commands/clearDebt.js @@ -61,7 +61,8 @@ export async function clearDebt({ console.log('clearDebt ERROR', parseContractError({ error, AllErrors })); return ethers.BigNumber.from(10_000_000); }); - const tx = await DebtRepayerContract.depositDebtToRepay(...args, { gasLimit: gasLimit.mul(2) }); - const result = await tx.wait(); - console.log('clearDebt', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const txn = await DebtRepayerContract.depositDebtToRepay(...args, { gasLimit: gasLimit.mul(2) }); + const receipt = await txn.wait(); + console.log('clearDebt', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/delegateCollateral.js b/liquidity/cypress/cypress/support/commands/delegateCollateral.js index c17adeb51..80fa47603 100644 --- a/liquidity/cypress/cypress/support/commands/delegateCollateral.js +++ b/liquidity/cypress/cypress/support/commands/delegateCollateral.js @@ -35,8 +35,8 @@ export async function delegateCollateral({ console.log('delegateCollateral ERROR', parseContractError({ error, AllErrors })); return ethers.BigNumber.from(10_000_000); }); - const tx = await CoreProxyContract.delegateCollateral(...args, { gasLimit: gasLimit.mul(2) }); - const result = await tx.wait(); - console.log('delegateCollateral', { txEvents: result.events.filter((e) => Boolean(e.event)) }); - return result; + const txn = await CoreProxyContract.delegateCollateral(...args, { gasLimit: gasLimit.mul(2) }); + const receipt = await txn.wait(); + console.log('delegateCollateral', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/depositCollateral.js b/liquidity/cypress/cypress/support/commands/depositCollateral.js index d56637f95..b351d462d 100644 --- a/liquidity/cypress/cypress/support/commands/depositCollateral.js +++ b/liquidity/cypress/cypress/support/commands/depositCollateral.js @@ -29,7 +29,8 @@ export async function depositCollateral({ console.log('delegateCollateral ERROR', parseContractError({ error, AllErrors })); return ethers.BigNumber.from(10_000_000); }); - const tx = await CoreProxyContract.deposit(...args, { gasLimit: gasLimit.mul(2) }); - const result = await tx.wait(); - console.log('depositCollateral', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const txn = await CoreProxyContract.deposit(...args, { gasLimit: gasLimit.mul(2) }); + const receipt = await txn.wait(); + console.log('depositCollateral', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/getSNX.js b/liquidity/cypress/cypress/support/commands/getSNX.js index 5861e1011..4091e04fc 100644 --- a/liquidity/cypress/cypress/support/commands/getSNX.js +++ b/liquidity/cypress/cypress/support/commands/getSNX.js @@ -44,13 +44,14 @@ export async function getSNX({ address = Cypress.env('walletAddress'), amount }) console.log('getSNX', { whale, whaleBalance }); const signer = provider.getSigner(whale); - const tx = await SNXContract.connect(signer).transfer( + const txn = await SNXContract.connect(signer).transfer( address, ethers.utils.parseEther(`${amount}`) ); - const result = await tx.wait(); - console.log('getSNX', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const receipt = await txn.wait(); + console.log('getSNX', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const newBalance = parseFloat(ethers.utils.formatUnits(await SNXContract.balanceOf(address))); console.log('getSNX', { address, newBalance }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/getSUSD.js b/liquidity/cypress/cypress/support/commands/getSUSD.js index 9a2f29e1b..06b1b9baa 100644 --- a/liquidity/cypress/cypress/support/commands/getSUSD.js +++ b/liquidity/cypress/cypress/support/commands/getSUSD.js @@ -45,12 +45,13 @@ export async function getSUSD({ address = Cypress.env('walletAddress'), amount } console.log('getSUSD', { whale, whaleBalance }); const signer = provider.getSigner(whale); - const tx = await sUSDContract + const txn = await sUSDContract .connect(signer) .transfer(address, ethers.utils.parseEther(`${amount}`)); - const result = await tx.wait(); - console.log('getSUSD', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const receipt = await txn.wait(); + console.log('getSUSD', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const newBalance = parseFloat(ethers.utils.formatUnits(await sUSDContract.balanceOf(address))); console.log('getSUSD', { address, newBalance }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/getSystemToken.js b/liquidity/cypress/cypress/support/commands/getSystemToken.js index 163404fd5..6ace90c6c 100644 --- a/liquidity/cypress/cypress/support/commands/getSystemToken.js +++ b/liquidity/cypress/cypress/support/commands/getSystemToken.js @@ -53,13 +53,14 @@ export async function getSystemToken({ address = Cypress.env('walletAddress'), a console.log('getSystemToken', { whale, whaleBalance }); const signer = provider.getSigner(whale); - const tx = await TokenContract.connect(signer).transfer( + const txn = await TokenContract.connect(signer).transfer( address, ethers.utils.parseEther(`${amount}`) ); - const result = await tx.wait(); - console.log('getSystemToken', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const receipt = await txn.wait(); + console.log('getSystemToken', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const newBalance = parseFloat(ethers.utils.formatUnits(await TokenContract.balanceOf(address))); console.log('getSystemToken', { address, newBalance }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/getUSDC.js b/liquidity/cypress/cypress/support/commands/getUSDC.js index e7a098f5f..62dee3fd1 100644 --- a/liquidity/cypress/cypress/support/commands/getUSDC.js +++ b/liquidity/cypress/cypress/support/commands/getUSDC.js @@ -37,13 +37,14 @@ export async function getUSDC({ address = Cypress.env('walletAddress'), amount } console.log('getUSDC', { whale, token: collateralConfig.address, whaleBalance }); const signer = provider.getSigner(whale); - const tx = await USDCContract.connect(signer).transfer( + const txn = await USDCContract.connect(signer).transfer( address, ethers.utils.parseUnits(`${amount}`, 6) ); - const result = await tx.wait(); - console.log('getUSDC', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const receipt = await txn.wait(); + console.log('getUSDC', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const newBalance = parseFloat(ethers.utils.formatUnits(await USDCContract.balanceOf(address), 6)); console.log('getUSDC', { address, token: collateralConfig.address, newBalance }); + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/setEthBalance.js b/liquidity/cypress/cypress/support/commands/setEthBalance.js index cc4eb6dd2..61c316a8b 100644 --- a/liquidity/cypress/cypress/support/commands/setEthBalance.js +++ b/liquidity/cypress/cypress/support/commands/setEthBalance.js @@ -11,4 +11,5 @@ export async function setEthBalance({ address = Cypress.env('walletAddress'), ba ]); const newBalance = parseFloat(ethers.utils.formatUnits(await provider.getBalance(address))); console.log('setEthBalance', { address, newBalance }); + return newBalance; } diff --git a/liquidity/cypress/cypress/support/commands/setWithdrawTimeout.js b/liquidity/cypress/cypress/support/commands/setWithdrawTimeout.js index cafad5e56..3056d3d00 100644 --- a/liquidity/cypress/cypress/support/commands/setWithdrawTimeout.js +++ b/liquidity/cypress/cypress/support/commands/setWithdrawTimeout.js @@ -12,11 +12,11 @@ export async function setWithdrawTimeout(timeout) { const owner = await CoreProxyContract.owner(); const signer = provider.getSigner(owner); - const tx = await CoreProxyContract.connect(signer).setConfig( + const txn = await CoreProxyContract.connect(signer).setConfig( ethers.utils.formatBytes32String('accountTimeoutWithdraw'), ethers.utils.formatBytes32String(timeout) ); - await tx.wait(); + const receipt = await txn.wait(); const accountTimeoutWithdraw = await CoreProxyContract.getConfig( ethers.utils.formatBytes32String('accountTimeoutWithdraw') @@ -25,4 +25,6 @@ export async function setWithdrawTimeout(timeout) { console.log('accountTimeoutWithdraw', { accountTimeoutWithdraw: accountTimeoutWithdraw.toString(), }); + + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/wrapCollateral.js b/liquidity/cypress/cypress/support/commands/wrapCollateral.js index a0e3a8238..348dfa734 100644 --- a/liquidity/cypress/cypress/support/commands/wrapCollateral.js +++ b/liquidity/cypress/cypress/support/commands/wrapCollateral.js @@ -31,9 +31,9 @@ export async function wrapCollateral({ address = Cypress.env('walletAddress'), s console.log('wrapCollateral ERROR', parseContractError({ error, AllErrors })); return ethers.BigNumber.from(10_000_000); }); - const tx = await SpotMarketProxyContract.wrap(...args, { gasLimit: gasLimit.mul(2) }); - const result = await tx.wait(); - console.log('wrapCollateral', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const txn = await SpotMarketProxyContract.wrap(...args, { gasLimit: gasLimit.mul(2) }); + const receipt = await txn.wait(); + console.log('wrapCollateral', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const CollateralContract = new ethers.Contract( synthConfig.address, @@ -45,4 +45,6 @@ export async function wrapCollateral({ address = Cypress.env('walletAddress'), s symbol, synthBalance: ethers.utils.formatUnits(await CollateralContract.balanceOf(address), 18), }); + + return receipt; } diff --git a/liquidity/cypress/cypress/support/commands/wrapEth.js b/liquidity/cypress/cypress/support/commands/wrapEth.js index 246663dbd..c1f5fc6f4 100644 --- a/liquidity/cypress/cypress/support/commands/wrapEth.js +++ b/liquidity/cypress/cypress/support/commands/wrapEth.js @@ -23,13 +23,13 @@ export async function wrapEth({ address = Cypress.env('walletAddress'), amount } return oldBalance; } - const tx = await WETHContract.deposit({ + const txn = await WETHContract.deposit({ value: ethers.utils.hexValue(ethers.utils.parseEther(`${amount}`).toHexString()), }); - const result = await tx.wait(); - console.log('wrapEth', { txEvents: result.events.filter((e) => Boolean(e.event)) }); + const receipt = await txn.wait(); + console.log('wrapEth', { txEvents: receipt.events.filter((e) => Boolean(e.event)) }); const newBalance = parseFloat(ethers.utils.formatUnits(await WETHContract.balanceOf(address))); console.log('wrapEth', { address, newBalance }); - return newBalance; + return receipt; } diff --git a/liquidity/cypress/cypress/support/e2e.js b/liquidity/cypress/cypress/support/e2e.js index e2461802b..fcca7286a 100644 --- a/liquidity/cypress/cypress/support/e2e.js +++ b/liquidity/cypress/cypress/support/e2e.js @@ -15,30 +15,36 @@ import { setWithdrawTimeout } from './commands/setWithdrawTimeout'; import { getSUSD } from './commands/getSUSD'; import { getSystemToken } from './commands/getSystemToken'; -Cypress.Commands.add('approveCollateral', approveCollateral); -Cypress.Commands.add('borrowUsd', borrowUsd); -Cypress.Commands.add('clearDebt', clearDebt); -Cypress.Commands.add('delegateCollateral', (...args) => { - cy.wrap(delegateCollateral(...args), { timeout: 180_000 }) - .should('be.an', 'object') - .and('include.keys', ['transactionHash', 'logs', 'events']) - .as('delegateCollateral'); -}); -Cypress.Commands.add('depositCollateral', depositCollateral); -Cypress.Commands.add('getSNX', getSNX); -Cypress.Commands.add('getUSDC', getUSDC); -Cypress.Commands.add('setEthBalance', setEthBalance); -Cypress.Commands.add('wrapCollateral', wrapCollateral); -Cypress.Commands.add('wrapEth', wrapEth); -Cypress.Commands.add('setWithdrawTimeout', setWithdrawTimeout); -Cypress.Commands.add('getSUSD', getSUSD); -Cypress.Commands.add('getSystemToken', getSystemToken); - installLogsCollector({ enableExtendedCollector: true, enableContinuousLogging: true, }); +function addTxnCommand(name, command, options) { + Cypress.Commands.add(name, (...args) => { + cy.wrap( + command(...args).then( + () => `${name} ok`, + (e) => `${name} error ${e.message}` + ), + options + ).should('be.eq', `${name} ok`); + }); +} +addTxnCommand('approveCollateral', approveCollateral, { timeout: 30_000 }); +addTxnCommand('borrowUsd', borrowUsd, { timeout: 180_000 }); +addTxnCommand('clearDebt', clearDebt, { timeout: 180_000 }); +addTxnCommand('delegateCollateral', delegateCollateral, { timeout: 180_000 }); +addTxnCommand('depositCollateral', depositCollateral, { timeout: 60_000 }); +addTxnCommand('getSNX', getSNX, { timeout: 60_000 }); +addTxnCommand('getSUSD', getSUSD, { timeout: 60_000 }); +addTxnCommand('getSystemToken', getSystemToken, { timeout: 60_000 }); +addTxnCommand('getUSDC', getUSDC, { timeout: 60_000 }); +addTxnCommand('setEthBalance', setEthBalance, { timeout: 30_000 }); +addTxnCommand('setWithdrawTimeout', setWithdrawTimeout, { timeout: 60_000 }); +addTxnCommand('wrapCollateral', wrapCollateral, { timeout: 120_000 }); +addTxnCommand('wrapEth', wrapEth, { timeout: 60_000 }); + function subgraph(req) { const body = JSON.parse(req.body); if (body.query.trim().startsWith('query pool($id: String)')) { diff --git a/liquidity/cypress/cypress/tasks/anvil.js b/liquidity/cypress/cypress/tasks/anvil.js index 9c6fcb4d3..5a0a4ee00 100644 --- a/liquidity/cypress/cypress/tasks/anvil.js +++ b/liquidity/cypress/cypress/tasks/anvil.js @@ -6,7 +6,7 @@ let anvilProcess; const execPromised = promisify(exec); export async function startAnvil({ chainId, forkUrl, block }) { - console.log('pkill anvil'); + console.log('pkill -9 anvil'); await execPromised('pkill -9 anvil').catch(() => console.log('No other anvil processes to kill')); const cmd = 'anvil'; const args = [ diff --git a/liquidity/lib/contracts/importers/importAccountProxy.js b/liquidity/lib/contracts/importers/importAccountProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importAccountProxy.js rename to liquidity/lib/contracts/importers/importAccountProxy.ts index 832bd11c7..f2d124389 100644 --- a/liquidity/lib/contracts/importers/importAccountProxy.js +++ b/liquidity/lib/contracts/importers/importAccountProxy.ts @@ -1,4 +1,7 @@ -export async function importAccountProxy(chainId, preset) { +export async function importAccountProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importAllErrors.js b/liquidity/lib/contracts/importers/importAllErrors.ts similarity index 93% rename from liquidity/lib/contracts/importers/importAllErrors.js rename to liquidity/lib/contracts/importers/importAllErrors.ts index 074f5c65b..fd5e4889c 100644 --- a/liquidity/lib/contracts/importers/importAllErrors.js +++ b/liquidity/lib/contracts/importers/importAllErrors.ts @@ -1,4 +1,7 @@ -export async function importAllErrors(chainId, preset) { +export async function importAllErrors( + chainId?: number, + preset?: string +): Promise<{ address: undefined; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importClosePosition.js b/liquidity/lib/contracts/importers/importClosePosition.ts similarity index 95% rename from liquidity/lib/contracts/importers/importClosePosition.js rename to liquidity/lib/contracts/importers/importClosePosition.ts index d3a0fae7f..593109f61 100644 --- a/liquidity/lib/contracts/importers/importClosePosition.js +++ b/liquidity/lib/contracts/importers/importClosePosition.ts @@ -10,7 +10,10 @@ const abi = [ 'function closePosition(address coreProxyAddress, address accountProxyAddress, uint128 accountId, uint128 poolId, address collateralType)', ]; -export async function importClosePosition(chainId, preset) { +export async function importClosePosition( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '1-main': { diff --git a/liquidity/lib/contracts/importers/importCollateralTokens.js b/liquidity/lib/contracts/importers/importCollateralTokens.ts similarity index 76% rename from liquidity/lib/contracts/importers/importCollateralTokens.js rename to liquidity/lib/contracts/importers/importCollateralTokens.ts index 0b7a170bf..6aaa604d0 100644 --- a/liquidity/lib/contracts/importers/importCollateralTokens.js +++ b/liquidity/lib/contracts/importers/importCollateralTokens.ts @@ -1,4 +1,27 @@ -export async function importCollateralTokens(chainId, preset) { +export async function importCollateralTokens( + chainId?: number, + preset?: string +): Promise< + { + address: string; + symbol: string; + name: string; + decimals: number; + depositingEnabled: boolean; + issuanceRatioD18: string; + liquidationRatioD18: string; + liquidationRewardD18: string; + oracleNodeId: string; + tokenAddress: string; + minDelegationD18: string; + oracle: { + constPrice?: string; + externalContract?: string; + stalenessTolerance?: string; + pythFeedId?: string; + }; + }[] +> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importCoreProxy.js b/liquidity/lib/contracts/importers/importCoreProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importCoreProxy.js rename to liquidity/lib/contracts/importers/importCoreProxy.ts index 53b35626b..9651a779a 100644 --- a/liquidity/lib/contracts/importers/importCoreProxy.js +++ b/liquidity/lib/contracts/importers/importCoreProxy.ts @@ -1,4 +1,7 @@ -export async function importCoreProxy(chainId, preset) { +export async function importCoreProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importDebtRepayer.js b/liquidity/lib/contracts/importers/importDebtRepayer.ts similarity index 86% rename from liquidity/lib/contracts/importers/importDebtRepayer.js rename to liquidity/lib/contracts/importers/importDebtRepayer.ts index fbe520c56..efd17ca01 100644 --- a/liquidity/lib/contracts/importers/importDebtRepayer.js +++ b/liquidity/lib/contracts/importers/importDebtRepayer.ts @@ -2,7 +2,10 @@ const abi = [ 'function depositDebtToRepay(address synthetixCore, address spotMarket, address accountProxy, uint128 accountId, uint128 poolId, address collateralType, uint128 spotMarketId)', ]; -export async function importDebtRepayer(chainId, preset) { +export async function importDebtRepayer( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '8453-andromeda': { diff --git a/liquidity/lib/contracts/importers/importExtras.js b/liquidity/lib/contracts/importers/importExtras.ts similarity index 93% rename from liquidity/lib/contracts/importers/importExtras.js rename to liquidity/lib/contracts/importers/importExtras.ts index 279578659..3fd382a63 100644 --- a/liquidity/lib/contracts/importers/importExtras.js +++ b/liquidity/lib/contracts/importers/importExtras.ts @@ -1,4 +1,9 @@ -export async function importExtras(chainId, preset) { +export async function importExtras( + chainId?: number, + preset?: string +): Promise<{ + [key: string]: string; +}> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importLegacyMarket.js b/liquidity/lib/contracts/importers/importLegacyMarket.ts similarity index 90% rename from liquidity/lib/contracts/importers/importLegacyMarket.js rename to liquidity/lib/contracts/importers/importLegacyMarket.ts index 5eb14226d..440bbf320 100644 --- a/liquidity/lib/contracts/importers/importLegacyMarket.js +++ b/liquidity/lib/contracts/importers/importLegacyMarket.ts @@ -1,4 +1,7 @@ -export async function importLegacyMarket(chainId, preset) { +export async function importLegacyMarket( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importMeta.js b/liquidity/lib/contracts/importers/importMeta.ts similarity index 84% rename from liquidity/lib/contracts/importers/importMeta.js rename to liquidity/lib/contracts/importers/importMeta.ts index 1f9285b8c..b27051268 100644 --- a/liquidity/lib/contracts/importers/importMeta.js +++ b/liquidity/lib/contracts/importers/importMeta.ts @@ -1,4 +1,18 @@ -export async function importMeta(chainId, preset) { +export async function importMeta( + chainId?: number, + preset?: string +): Promise<{ + chainId: number; + name: string; + preset: string; + version: string; + generator: string; + timestamp: number; + miscUrl: string; + contracts: { + [key: string]: string; + }; +}> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importMintableTokens.js b/liquidity/lib/contracts/importers/importMintableTokens.ts similarity index 90% rename from liquidity/lib/contracts/importers/importMintableTokens.js rename to liquidity/lib/contracts/importers/importMintableTokens.ts index cdd0f9901..9d0d49ebe 100644 --- a/liquidity/lib/contracts/importers/importMintableTokens.js +++ b/liquidity/lib/contracts/importers/importMintableTokens.ts @@ -1,4 +1,14 @@ -export async function importMintableTokens(chainId, preset) { +export async function importMintableTokens( + chainId?: number, + preset?: string +): Promise< + { + address: string; + symbol: string; + name: string; + decimals: number; + }[] +> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importMulticall3.js b/liquidity/lib/contracts/importers/importMulticall3.ts similarity index 97% rename from liquidity/lib/contracts/importers/importMulticall3.js rename to liquidity/lib/contracts/importers/importMulticall3.ts index a62e108ad..06b6bff90 100644 --- a/liquidity/lib/contracts/importers/importMulticall3.js +++ b/liquidity/lib/contracts/importers/importMulticall3.ts @@ -17,7 +17,10 @@ const abi = [ 'function aggregate3Value(tuple(address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)', ]; -export async function importMulticall3(chainId, preset) { +export async function importMulticall3( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '1-main': { diff --git a/liquidity/lib/contracts/importers/importOracleManagerProxy.js b/liquidity/lib/contracts/importers/importOracleManagerProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importOracleManagerProxy.js rename to liquidity/lib/contracts/importers/importOracleManagerProxy.ts index 29944be31..40b45040c 100644 --- a/liquidity/lib/contracts/importers/importOracleManagerProxy.js +++ b/liquidity/lib/contracts/importers/importOracleManagerProxy.ts @@ -1,4 +1,7 @@ -export async function importOracleManagerProxy(chainId, preset) { +export async function importOracleManagerProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importPerpsAccountProxy.js b/liquidity/lib/contracts/importers/importPerpsAccountProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importPerpsAccountProxy.js rename to liquidity/lib/contracts/importers/importPerpsAccountProxy.ts index 3a52549fd..0930ec9a9 100644 --- a/liquidity/lib/contracts/importers/importPerpsAccountProxy.js +++ b/liquidity/lib/contracts/importers/importPerpsAccountProxy.ts @@ -1,4 +1,7 @@ -export async function importPerpsAccountProxy(chainId, preset) { +export async function importPerpsAccountProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importPerpsMarketProxy.js b/liquidity/lib/contracts/importers/importPerpsMarketProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importPerpsMarketProxy.js rename to liquidity/lib/contracts/importers/importPerpsMarketProxy.ts index 036c0c456..1554fcabf 100644 --- a/liquidity/lib/contracts/importers/importPerpsMarketProxy.js +++ b/liquidity/lib/contracts/importers/importPerpsMarketProxy.ts @@ -1,4 +1,7 @@ -export async function importPerpsMarketProxy(chainId, preset) { +export async function importPerpsMarketProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importPythERC7412Wrapper.js b/liquidity/lib/contracts/importers/importPythERC7412Wrapper.ts similarity index 93% rename from liquidity/lib/contracts/importers/importPythERC7412Wrapper.js rename to liquidity/lib/contracts/importers/importPythERC7412Wrapper.ts index 6ba825fab..3973bd604 100644 --- a/liquidity/lib/contracts/importers/importPythERC7412Wrapper.js +++ b/liquidity/lib/contracts/importers/importPythERC7412Wrapper.ts @@ -1,4 +1,7 @@ -export async function importPythERC7412Wrapper(chainId, preset) { +export async function importPythERC7412Wrapper( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importPythFeeds.js b/liquidity/lib/contracts/importers/importPythFeeds.ts similarity index 94% rename from liquidity/lib/contracts/importers/importPythFeeds.js rename to liquidity/lib/contracts/importers/importPythFeeds.ts index 137560dc0..f68e81391 100644 --- a/liquidity/lib/contracts/importers/importPythFeeds.js +++ b/liquidity/lib/contracts/importers/importPythFeeds.ts @@ -1,4 +1,4 @@ -export async function importPythFeeds(chainId, preset) { +export async function importPythFeeds(chainId?: number, preset?: string): Promise { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importPythVerfier.js b/liquidity/lib/contracts/importers/importPythVerfier.ts similarity index 98% rename from liquidity/lib/contracts/importers/importPythVerfier.js rename to liquidity/lib/contracts/importers/importPythVerfier.ts index 3b72de552..6ad81d6e4 100644 --- a/liquidity/lib/contracts/importers/importPythVerfier.js +++ b/liquidity/lib/contracts/importers/importPythVerfier.ts @@ -75,7 +75,10 @@ // ] const abi = ['function updatePriceFeeds(bytes[] updateData) payable']; -export async function importPythVerfier(chainId, preset) { +export async function importPythVerfier( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importRewardDistributor.js b/liquidity/lib/contracts/importers/importRewardDistributor.js deleted file mode 100644 index 4777bf3ce..000000000 --- a/liquidity/lib/contracts/importers/importRewardDistributor.js +++ /dev/null @@ -1,56 +0,0 @@ -const abi = [ - 'constructor(address rewardManager_, uint128 poolId_, address collateralType_, address payoutToken_, uint8 payoutTokenDecimals_, string name_)', - 'function SYSTEM_PRECISION() view returns (uint256)', - 'function collateralType() view returns (address)', - 'function distributeRewards(uint128 poolId_, address collateralType_, uint256 amount_, uint64 start_, uint32 duration_)', - 'function name() view returns (string)', - 'function onPositionUpdated(uint128, uint128, address, uint256)', - 'function payout(uint128, uint128 poolId_, address collateralType_, address payoutTarget_, uint256 payoutAmount_) returns (bool)', - 'function payoutToken() view returns (address)', - 'function poolId() view returns (uint128)', - 'function precision() view returns (uint256)', - 'function rewardManager() view returns (address)', - 'function rewardsAmount() view returns (uint256)', - 'function setShouldFailPayout(bool shouldFailPayout_)', - 'function shouldFailPayout() view returns (bool)', - 'function supportsInterface(bytes4 interfaceId) view returns (bool)', - 'function token() view returns (address)', - 'error FailedTransfer(address from, address to, uint256 value)', - 'error InvalidParameter(string parameter, string reason)', - 'error NotEnoughBalance(uint256 amountRequested, uint256 currentBalance)', - 'error NotEnoughRewardsLeft(uint256 amountRequested, uint256 amountLeft)', - 'error Unauthorized(address addr)', -]; - -export async function importRewardDistributor(chainId, preset) { - if (!preset) { - throw new Error(`Missing preset`); - } - const deployment = `${Number(chainId).toFixed(0)}-${preset}`; - switch (deployment) { - case '1-main': { - return { address: null, abi }; - } - case '11155111-main': { - return { address: null, abi }; - } - case '10-main': { - return { address: null, abi }; - } - case '8453-andromeda': { - return { address: null, abi }; - } - case '84532-andromeda': { - return { address: null, abi }; - } - case '42161-main': { - return { address: null, abi }; - } - case '421614-main': { - return { address: null, abi }; - } - default: { - throw new Error(`Unsupported deployment ${deployment} for RewardsDistributor`); - } - } -} diff --git a/liquidity/lib/contracts/importers/importRewardsDistributors.js b/liquidity/lib/contracts/importers/importRewardsDistributors.ts similarity index 78% rename from liquidity/lib/contracts/importers/importRewardsDistributors.js rename to liquidity/lib/contracts/importers/importRewardsDistributors.ts index decf014c0..935e220fc 100644 --- a/liquidity/lib/contracts/importers/importRewardsDistributors.js +++ b/liquidity/lib/contracts/importers/importRewardsDistributors.ts @@ -1,4 +1,30 @@ -export async function importRewardsDistributors(chainId, preset) { +export async function importRewardsDistributors( + chainId?: number, + preset?: string +): Promise< + { + address: string; + name: string; + poolId: string; + + // undefined for Pool-level distributors + collateralType?: { + address: string; + symbol: string; + name: string; + decimals: number; + }; + + payoutToken: { + address: string; + symbol: string; + name: string; + decimals: number; + }; + rewardManager: string; + isRegistered: boolean; + }[] +> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importSNX.js b/liquidity/lib/contracts/importers/importSNX.ts similarity index 92% rename from liquidity/lib/contracts/importers/importSNX.js rename to liquidity/lib/contracts/importers/importSNX.ts index 406b5e05c..20d37a74b 100644 --- a/liquidity/lib/contracts/importers/importSNX.js +++ b/liquidity/lib/contracts/importers/importSNX.ts @@ -2,7 +2,11 @@ const extraAbi = [ 'function transferableSynthetix(address account) view returns (uint256 transferable)', 'function collateral(address account) view returns (uint256 collateral)', ]; -export async function importSNX(chainId, preset) { + +export async function importSNX( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '1-main': { diff --git a/liquidity/lib/contracts/importers/importSpotMarketProxy.js b/liquidity/lib/contracts/importers/importSpotMarketProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importSpotMarketProxy.js rename to liquidity/lib/contracts/importers/importSpotMarketProxy.ts index 68198c0a1..a4af147b1 100644 --- a/liquidity/lib/contracts/importers/importSpotMarketProxy.js +++ b/liquidity/lib/contracts/importers/importSpotMarketProxy.ts @@ -1,4 +1,7 @@ -export async function importSpotMarketProxy(chainId, preset) { +export async function importSpotMarketProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importStaticAaveUSDC.js b/liquidity/lib/contracts/importers/importStaticAaveUSDC.ts similarity index 97% rename from liquidity/lib/contracts/importers/importStaticAaveUSDC.js rename to liquidity/lib/contracts/importers/importStaticAaveUSDC.ts index 5fbae9628..87e306606 100644 --- a/liquidity/lib/contracts/importers/importStaticAaveUSDC.js +++ b/liquidity/lib/contracts/importers/importStaticAaveUSDC.ts @@ -65,7 +65,10 @@ const abi = [ 'function withdraw(uint256 assets, address receiver, address owner) returns (uint256)', ]; -export async function importStaticAaveUSDC(chainId, preset) { +export async function importStaticAaveUSDC( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importSynthTokens.js b/liquidity/lib/contracts/importers/importSynthTokens.ts similarity index 81% rename from liquidity/lib/contracts/importers/importSynthTokens.js rename to liquidity/lib/contracts/importers/importSynthTokens.ts index ed34f738c..0d7cf6644 100644 --- a/liquidity/lib/contracts/importers/importSynthTokens.js +++ b/liquidity/lib/contracts/importers/importSynthTokens.ts @@ -1,4 +1,23 @@ -export async function importSynthTokens(chainId, preset) { +export async function importSynthTokens( + chainId?: number, + preset?: string +): Promise< + { + synthMarketId: string; + address: string; + symbol: string; + name: string; + decimals: number; + + // undefined for some synths (like Synthetic USDe on Mainnet) + token?: { + address: string; + symbol: string; + name: string; + decimals: number; + }; + }[] +> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importSystemToken.js b/liquidity/lib/contracts/importers/importSystemToken.ts similarity index 90% rename from liquidity/lib/contracts/importers/importSystemToken.js rename to liquidity/lib/contracts/importers/importSystemToken.ts index 02920c3c3..e3f554398 100644 --- a/liquidity/lib/contracts/importers/importSystemToken.js +++ b/liquidity/lib/contracts/importers/importSystemToken.ts @@ -1,4 +1,12 @@ -export async function importSystemToken(chainId, preset) { +export async function importSystemToken( + chainId?: number, + preset?: string +): Promise<{ + address: string; + symbol: string; + name: string; + decimals: number; +}> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importUSDC.js b/liquidity/lib/contracts/importers/importUSDC.ts similarity index 94% rename from liquidity/lib/contracts/importers/importUSDC.js rename to liquidity/lib/contracts/importers/importUSDC.ts index 4678563a4..ae79fb649 100644 --- a/liquidity/lib/contracts/importers/importUSDC.js +++ b/liquidity/lib/contracts/importers/importUSDC.ts @@ -16,7 +16,10 @@ const abi = [ 'function transferFrom(address from, address to, uint256 amount) returns (bool)', ]; -export async function importUSDC(chainId, preset) { +export async function importUSDC( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '1-main': { diff --git a/liquidity/lib/contracts/importers/importUSDProxy.js b/liquidity/lib/contracts/importers/importUSDProxy.ts similarity index 95% rename from liquidity/lib/contracts/importers/importUSDProxy.js rename to liquidity/lib/contracts/importers/importUSDProxy.ts index cf288f038..42e73c355 100644 --- a/liquidity/lib/contracts/importers/importUSDProxy.js +++ b/liquidity/lib/contracts/importers/importUSDProxy.ts @@ -1,4 +1,7 @@ -export async function importUSDProxy(chainId, preset) { +export async function importUSDProxy( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importV2x.js b/liquidity/lib/contracts/importers/importV2x.ts similarity index 90% rename from liquidity/lib/contracts/importers/importV2x.js rename to liquidity/lib/contracts/importers/importV2x.ts index e67d0d5d6..1183c9027 100644 --- a/liquidity/lib/contracts/importers/importV2x.js +++ b/liquidity/lib/contracts/importers/importV2x.ts @@ -1,4 +1,7 @@ -export async function importV2x(chainId, preset) { +export async function importV2x( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { if (!preset) { throw new Error(`Missing preset`); } diff --git a/liquidity/lib/contracts/importers/importWETH.js b/liquidity/lib/contracts/importers/importWETH.ts similarity index 93% rename from liquidity/lib/contracts/importers/importWETH.js rename to liquidity/lib/contracts/importers/importWETH.ts index 22017b8c7..ffcc1c619 100644 --- a/liquidity/lib/contracts/importers/importWETH.js +++ b/liquidity/lib/contracts/importers/importWETH.ts @@ -16,7 +16,10 @@ const abi = [ 'event Withdrawal(address indexed src, uint256 wad)', ]; -export async function importWETH(chainId, preset) { +export async function importWETH( + chainId?: number, + preset?: string +): Promise<{ address: string; abi: string[] }> { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '1-main': { diff --git a/liquidity/lib/contracts/importers/index.d.ts b/liquidity/lib/contracts/importers/index.d.ts deleted file mode 100644 index 3646d9412..000000000 --- a/liquidity/lib/contracts/importers/index.d.ts +++ /dev/null @@ -1,177 +0,0 @@ -declare module '@snx-v3/contracts' { - function importCoreProxy( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importAccountProxy( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importUSDProxy( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importV2x( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importSpotMarketProxy( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importMulticall3( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importLegacyMarket( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importOracleManagerProxy( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importPythERC7412Wrapper( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importRewardsDistributors( - chainId?: number, - preset?: string - ): Promise< - { - address: string; - name: string; - poolId: string; - - // undefined for Pool-level distributors - collateralType?: { - address: string; - symbol: string; - name: string; - decimals: number; - }; - - payoutToken: { - address: string; - symbol: string; - name: string; - decimals: number; - }; - rewardManager: string; - isRegistered: boolean; - }[] - >; - - function importExtras( - chainId?: number, - preset?: string - ): Promise<{ - [key: string]: string; - }>; - - function importCollateralTokens( - chainId?: number, - preset?: string - ): Promise< - { - address: string; - symbol: string; - name: string; - decimals: number; - depositingEnabled: boolean; - issuanceRatioD18: string; - liquidationRatioD18: string; - liquidationRewardD18: string; - oracleNodeId: string; - tokenAddress: string; - minDelegationD18: string; - oracle: { - constPrice?: string; - externalContract?: string; - stalenessTolerance?: string; - pythFeedId?: string; - }; - }[] - >; - - function importSystemToken( - chainId?: number, - preset?: string - ): Promise<{ - address: string; - symbol: string; - name: string; - decimals: number; - }>; - - function importSynthTokens( - chainId?: number, - preset?: string - ): Promise< - { - synthMarketId: string; - address: string; - symbol: string; - name: string; - decimals: number; - token: { - address: string; - symbol: string; - name: string; - decimals: number; - }; - }[] - >; - - function importAllErrors( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importStaticAaveUSDC( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importWETH( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importSNX( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importUSDC( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importDebtRepayer( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importPythFeeds(chainId?: number, preset?: string): Promise; - - function importPythVerfier( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; - - function importClosePosition( - chainId?: number, - preset?: string - ): Promise<{ address: string; abi: string[] }>; -} diff --git a/liquidity/lib/contracts/importers/index.js b/liquidity/lib/contracts/importers/index.ts similarity index 92% rename from liquidity/lib/contracts/importers/index.js rename to liquidity/lib/contracts/importers/index.ts index 4c53db4e8..bb0853bf5 100644 --- a/liquidity/lib/contracts/importers/index.js +++ b/liquidity/lib/contracts/importers/index.ts @@ -1,31 +1,27 @@ -// Contracts +export * from './importAccountProxy'; export * from './importAllErrors'; +export * from './importClosePosition'; +export * from './importCollateralTokens'; export * from './importCoreProxy'; -export * from './importAccountProxy'; -export * from './importUSDProxy'; -export * from './importOracleManagerProxy'; +export * from './importDebtRepayer'; +export * from './importExtras'; export * from './importLegacyMarket'; -export * from './importV2x'; +export * from './importMeta'; +export * from './importMintableTokens'; export * from './importMulticall3'; -export * from './importRewardDistributor'; -export * from './importSpotMarketProxy'; -export * from './importPerpsMarketProxy'; +export * from './importOracleManagerProxy'; export * from './importPerpsAccountProxy'; +export * from './importPerpsMarketProxy'; export * from './importPythERC7412Wrapper'; +export * from './importPythFeeds'; export * from './importPythVerfier'; - -// Deployment extras -export * from './importExtras'; -export * from './importCollateralTokens'; -export * from './importMintableTokens'; export * from './importRewardsDistributors'; +export * from './importSNX'; +export * from './importSpotMarketProxy'; +export * from './importStaticAaveUSDC'; export * from './importSynthTokens'; export * from './importSystemToken'; -export * from './importStaticAaveUSDC'; -export * from './importWETH'; -export * from './importSNX'; export * from './importUSDC'; -export * from './importPythFeeds'; -export * from './importDebtRepayer'; -export * from './importClosePosition'; -export * from './importMeta'; +export * from './importUSDProxy'; +export * from './importV2x'; +export * from './importWETH'; diff --git a/liquidity/lib/contracts/importers/package.json b/liquidity/lib/contracts/importers/package.json index 03bf4aeb1..e2b15c8c8 100644 --- a/liquidity/lib/contracts/importers/package.json +++ b/liquidity/lib/contracts/importers/package.json @@ -2,7 +2,7 @@ "name": "@snx-v3/contracts", "private": true, "version": "0.0.0", - "main": "index.js", + "main": "index.ts", "types": "index.d.ts", "dependencies": { "@synthetixio/v3-contracts": "^7.1.0", diff --git a/liquidity/lib/format/format.ts b/liquidity/lib/format/format.ts index 1d3bc805b..d9bbde7e2 100644 --- a/liquidity/lib/format/format.ts +++ b/liquidity/lib/format/format.ts @@ -1,8 +1,8 @@ -import { BigNumberish, utils } from 'ethers'; +import { ethers } from 'ethers'; import { wei, WeiSource } from '@synthetixio/wei'; -export const formatValue = (value: BigNumberish, decimals = 18) => - parseFloat(utils.formatUnits(value, decimals)); +export const formatValue = (value: ethers.BigNumberish, decimals = 18) => + parseFloat(ethers.utils.formatUnits(value, decimals)); export const parseUnits = (value: WeiSource, decimals = 18) => wei(value, decimals).toBN(); @@ -15,3 +15,15 @@ export const prettyString = (text: string, startLength = 6, endLength = 4) => { } return `${text.substring(0, startLength)}...${text.substring(text.length - endLength)}`; }; + +export function renderAccountId(accountId?: ethers.BigNumber) { + if (!accountId) { + return '---'; + } + const hex = accountId.toHexString(); + // auto-generated 0x80000000000000000000000000000008 value + if (hex.length === 34) { + return `${hex.slice(0, 5)}...${hex.slice(-6)}`; + } + return `#${accountId}`; +} diff --git a/liquidity/lib/parseContractError/parseContractError.ts b/liquidity/lib/parseContractError/parseContractError.ts index fa5a35b66..e698d51b1 100644 --- a/liquidity/lib/parseContractError/parseContractError.ts +++ b/liquidity/lib/parseContractError/parseContractError.ts @@ -82,7 +82,7 @@ export function parseContractError({ extraAbi, }: { error?: any; - AllErrors?: { address: string; abi: string[] }; + AllErrors?: { abi: string[] }; extraAbi?: string[]; }): ContractErrorType | void { const errorData = extractErrorData(error); diff --git a/liquidity/lib/useAccountInfo/useAccountPermissions.ts b/liquidity/lib/useAccountInfo/useAccountPermissions.ts index 35bc44060..ad79ad9a6 100644 --- a/liquidity/lib/useAccountInfo/useAccountPermissions.ts +++ b/liquidity/lib/useAccountInfo/useAccountPermissions.ts @@ -5,7 +5,7 @@ import { useCoreProxy } from '@snx-v3/useCoreProxy'; import { useQuery } from '@tanstack/react-query'; import { ethers } from 'ethers'; -export function useAccountPermissions(accountId: string | undefined) { +export function useAccountPermissions(accountId?: ethers.BigNumber) { const { data: CoreProxy } = useCoreProxy(); const { network } = useNetwork(); const provider = useProvider(); @@ -36,7 +36,7 @@ export function useAccountPermissions(accountId: string | undefined) { }); } -export function useAccountOwner(accountId: string | undefined) { +export function useAccountOwner(accountId?: ethers.BigNumber) { const { data: AccountProxy } = useAccountProxy(); const { network } = useNetwork(); const provider = useProvider(); diff --git a/liquidity/lib/useAccounts/package.json b/liquidity/lib/useAccounts/package.json index d56f2ec02..92abe7fa4 100644 --- a/liquidity/lib/useAccounts/package.json +++ b/liquidity/lib/useAccounts/package.json @@ -7,9 +7,9 @@ "@snx-v3/tsHelpers": "workspace:*", "@snx-v3/useAccountProxy": "workspace:*", "@snx-v3/useBlockchain": "workspace:*", - "@snx-v3/useCoreProxy": "workspace:*", "@snx-v3/useMulticall3": "workspace:*", "@tanstack/react-query": "^5.8.3", + "debug": "^4.3.7", "ethers": "^5.7.2" } } diff --git a/liquidity/lib/useAccounts/useAccounts.ts b/liquidity/lib/useAccounts/useAccounts.ts index e87897571..32e8d1169 100644 --- a/liquidity/lib/useAccounts/useAccounts.ts +++ b/liquidity/lib/useAccounts/useAccounts.ts @@ -1,13 +1,13 @@ +import { contractsHash } from '@snx-v3/tsHelpers'; import { useAccountProxy } from '@snx-v3/useAccountProxy'; -import { useNetwork, useWallet } from '@snx-v3/useBlockchain'; -import { useCoreProxy } from '@snx-v3/useCoreProxy'; +import { useNetwork, useProvider, useWallet } from '@snx-v3/useBlockchain'; import { useMulticall3 } from '@snx-v3/useMulticall3'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { BigNumber } from 'ethers'; -import { useProvider, useSigner } from '@snx-v3/useBlockchain'; -import { contractsHash } from '@snx-v3/tsHelpers'; +import { useQuery } from '@tanstack/react-query'; +import debug from 'debug'; import { ethers } from 'ethers'; +const log = debug('snx:useAccounts'); + export function useAccounts() { const { activeWallet } = useWallet(); const { network } = useNetwork(); @@ -24,7 +24,7 @@ export function useAccounts() { { contractsHash: contractsHash([AccountProxy, Multicall3]) }, ], enabled: Boolean(provider && walletAddress && AccountProxy && Multicall3), - queryFn: async function () { + queryFn: async function (): Promise { if (!(provider && walletAddress && AccountProxy && Multicall3)) throw 'OMFG'; const AccountProxyContract = new ethers.Contract( @@ -34,74 +34,39 @@ export function useAccounts() { ); const Multicall3Contract = new ethers.Contract(Multicall3.address, Multicall3.abi, provider); - const numberOfAccountTokens = await AccountProxyContract.balanceOf(activeWallet.address); + log('walletAddress', walletAddress); + const numberOfAccountTokens = await AccountProxyContract.balanceOf(walletAddress); + log('numberOfAccountTokens', numberOfAccountTokens); if (numberOfAccountTokens.eq(0)) { // No accounts created yet return []; } - const accountIndexes = Array.from(Array(numberOfAccountTokens.toNumber()).keys()); + const accountIndexes: number[] = Array.from(Array(numberOfAccountTokens.toNumber()).keys()); + log('accountIndexes', accountIndexes); const calls = accountIndexes.map((index) => ({ target: AccountProxy.address, callData: AccountProxyContract.interface.encodeFunctionData('tokenOfOwnerByIndex', [ - activeWallet.address, + walletAddress, index, ]), })); - const { returnData } = await Multicall3Contract.callStatic.aggregate(calls); - const accounts = (returnData as string[]).map( - (data) => - AccountProxyContract.interface.decodeFunctionResult('tokenOfOwnerByIndex', data)[0] - ) as BigNumber[]; + const multicallResponse = await Multicall3Contract.callStatic.aggregate3(calls); + log('multicallResponse', multicallResponse); + + const accounts = accountIndexes.map((index) => { + const { returnData } = multicallResponse[index]; + const [tokenOfOwnerByIndex] = AccountProxyContract.interface.decodeFunctionResult( + 'tokenOfOwnerByIndex', + returnData + ); + return tokenOfOwnerByIndex; + }); + log('accounts', accounts); - return accounts.map((accountId) => accountId.toString()); + return accounts; }, }); } - -export function useCreateAccount() { - const { data: CoreProxy } = useCoreProxy(); - const signer = useSigner(); - const { network } = useNetwork(); - const provider = useProvider(); - - const client = useQueryClient(); - return { - enabled: Boolean(network && CoreProxy), - mutation: useMutation({ - mutationFn: async function () { - try { - if (!(CoreProxy && signer && provider)) throw 'OMFG'; - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const tx = await CoreProxyContract['createAccount()'](); - const receipt = await provider.waitForTransaction(tx.hash); - - await client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Accounts'], - }); - - let newAccountId: string | undefined; - - receipt.logs.forEach((log: any) => { - if (log.topics[0] === CoreProxyContract.interface.getEventTopic('AccountCreated')) { - const accountId = CoreProxyContract.interface.decodeEventLog( - 'AccountCreated', - log.data, - log.topics - )?.accountId; - newAccountId = accountId?.toString(); - } - }); - - return [newAccountId]; - } catch (error) { - console.error(error); - throw error; - } - }, - }), - }; -} diff --git a/liquidity/lib/useApprove/useApprove.ts b/liquidity/lib/useApprove/useApprove.ts index 2b5c7b8ff..7b29e4b88 100644 --- a/liquidity/lib/useApprove/useApprove.ts +++ b/liquidity/lib/useApprove/useApprove.ts @@ -1,10 +1,10 @@ import { initialState, reducer } from '@snx-v3/txnReducer'; import { useAllowance } from '@snx-v3/useAllowance'; -import { useProvider, useSigner } from '@snx-v3/useBlockchain'; +import { useProvider, useSigner, useNetwork } from '@snx-v3/useBlockchain'; import { formatGasPriceForTransaction } from '@snx-v3/useGasOptions'; import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -13,30 +13,25 @@ const log = debug('snx:useApprove'); export const approveAbi = ['function approve(address spender, uint256 amount) returns (bool)']; -export const useApprove = ( - { - contractAddress, - amount, - spender, - }: { - contractAddress?: string; - amount?: ethers.BigNumber; - spender?: string; - }, - eventHandlers?: { - onSuccess?: () => void; - onMutate?: () => void; - onError?: (e: Error) => void; - } -) => { +export const useApprove = ({ + contractAddress, + amount, + spender, +}: { + contractAddress?: string; + amount?: ethers.BigNumber; + spender?: string; +}) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: allowance, refetch: refetchAllowance } = useAllowance({ contractAddress, spender }); const sufficientAllowance = allowance && amount && allowance.gte(amount); + const { network } = useNetwork(); const signer = useSigner(); const { gasSpeed } = useGasSpeed(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async (infiniteApproval: boolean) => { if (!signer || !contractAddress || !spender || !provider) @@ -50,43 +45,53 @@ export const useApprove = ( log(`spender`, spender); log(`amount`, amount); - try { - dispatch({ type: 'prompting' }); + dispatch({ type: 'prompting' }); - const contract = new ethers.Contract(contractAddress, approveAbi, signer); - const amountToApprove = infiniteApproval ? ethers.constants.MaxUint256 : amount; - log(`amountToApprove`, amountToApprove); + const contract = new ethers.Contract(contractAddress, approveAbi, signer); + const amountToApprove = infiniteApproval ? ethers.constants.MaxUint256 : amount; + log(`amountToApprove`, amountToApprove); - const gasPricesPromised = getGasPrice({ provider }); - const gasLimitPromised = contract.estimateGas.approve(spender, amountToApprove); - const populatedTxnPromised = contract.populateTransaction.approve(spender, amountToApprove); + const gasPricesPromised = getGasPrice({ provider }); + const gasLimitPromised = contract.estimateGas.approve(spender, amountToApprove); + const populatedTxnPromised = contract.populateTransaction.approve(spender, amountToApprove); - const [gasPrices, gasLimit, populatedTxn] = await Promise.all([ - gasPricesPromised, - gasLimitPromised, - populatedTxnPromised, - ]); + const [gasPrices, gasLimit, populatedTxn] = await Promise.all([ + gasPricesPromised, + gasLimitPromised, + populatedTxnPromised, + ]); - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); - const txn = await signer.sendTransaction({ ...populatedTxn, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + const txn = await signer.sendTransaction({ ...populatedTxn, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - refetchAllowance(); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; - } + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + dispatch({ type: 'success' }); + return receipt; + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'Allowance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, - ...eventHandlers, }); return { mutation, diff --git a/liquidity/lib/useBlockchain/magic.ts b/liquidity/lib/useBlockchain/magic.ts index 7a6f6c293..f38c75f17 100644 --- a/liquidity/lib/useBlockchain/magic.ts +++ b/liquidity/lib/useBlockchain/magic.ts @@ -38,6 +38,10 @@ export class MagicProvider extends ethers.providers.JsonRpcProvider { return [this.magicWallet]; } try { + // if (method === 'eth_getTransactionReceipt') { + // // mine extra block before getting receipt + // await super.send('evm_mine', []); + // } const result = await super.send(method, params); // eslint-disable-next-line no-console console.log('MAGIC.send', { method, params, result }); diff --git a/liquidity/lib/useBorrow/useBorrow.tsx b/liquidity/lib/useBorrow/useBorrow.tsx index f8222a165..a3ce88e88 100644 --- a/liquidity/lib/useBorrow/useBorrow.tsx +++ b/liquidity/lib/useBorrow/useBorrow.tsx @@ -7,7 +7,7 @@ import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -27,13 +27,14 @@ export const useBorrow = ({ }) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const signer = useSigner(); const { gasSpeed } = useGasSpeed(); const provider = useProvider(); const { network } = useNetwork(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if ( @@ -54,54 +55,69 @@ export const useBorrow = ({ return; } - try { - dispatch({ type: 'prompting' }); + dispatch({ type: 'prompting' }); - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const populatedTxnPromised = CoreProxyContract.populateTransaction.mintUsd( - ethers.BigNumber.from(accountId), - ethers.BigNumber.from(poolId), - collateralTypeAddress, - debtChange.abs().toBN() - ); + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const populatedTxnPromised = CoreProxyContract.populateTransaction.mintUsd( + ethers.BigNumber.from(accountId), + ethers.BigNumber.from(poolId), + collateralTypeAddress, + debtChange.abs().toBN() + ); - const callsPromise = Promise.all([populatedTxnPromised]); - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + const callsPromise = Promise.all([populatedTxnPromised]); + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); + } - const walletAddress = await signer.getAddress(); - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useBorrow', - walletAddress - ); + const walletAddress = await signer.getAddress(); + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useBorrow', + walletAddress + ); - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; - } + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + 'AccountCollateralUnlockDate', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useClaimAllRewards/useClaimAllRewards.tsx b/liquidity/lib/useClaimAllRewards/useClaimAllRewards.tsx index 40ba5e0bb..549c36fcc 100644 --- a/liquidity/lib/useClaimAllRewards/useClaimAllRewards.tsx +++ b/liquidity/lib/useClaimAllRewards/useClaimAllRewards.tsx @@ -38,7 +38,7 @@ export function useClaimAllRewards({ const signer = useSigner(); const { data: CoreProxy } = useCoreProxy(); const [txnState, dispatch] = React.useReducer(reducer, initialState); - const client = useQueryClient(); + const queryClient = useQueryClient(); const provider = useProvider(); const { data: synthTokens } = useSynthTokens(); @@ -88,7 +88,7 @@ export function useClaimAllRewards({ ) ); const synthToken = synthTokens.find( - (synth) => synth.address.toUpperCase() === distributor.payoutToken.address.toUpperCase() + (synth) => synth.address.toLowerCase() === distributor.payoutToken.address.toLowerCase() ); log('synthToken', synthToken); log('claimableAmount', claimableAmount); @@ -132,7 +132,6 @@ export function useClaimAllRewards({ dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); const receipt = await provider.waitForTransaction(txn.hash); log('receipt', receipt); - dispatch({ type: 'success' }); return receipt; }, @@ -158,13 +157,20 @@ export function useClaimAllRewards({ }); }, - onSuccess() { - client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Rewards'], - }); - client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'Rewards', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + toast.closeAll(); toast({ title: 'Success', diff --git a/liquidity/lib/useClearDebt/useClearDebt.tsx b/liquidity/lib/useClearDebt/useClearDebt.tsx index 0a0ac948b..13823457b 100644 --- a/liquidity/lib/useClearDebt/useClearDebt.tsx +++ b/liquidity/lib/useClearDebt/useClearDebt.tsx @@ -10,7 +10,7 @@ import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { withERC7412 } from '@snx-v3/withERC7412'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import React from 'react'; @@ -30,7 +30,7 @@ export const useClearDebt = ({ const { data: CoreProxy } = useCoreProxy(); const { data: SpotMarketProxy } = useSpotMarketProxy(); const { data: AccountProxy } = useAccountProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const signer = useSigner(); const { network } = useNetwork(); @@ -39,6 +39,7 @@ export const useClearDebt = ({ const { data: DebtRepayer } = useDebtRepayer(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -56,73 +57,84 @@ export const useClearDebt = ({ return; } - try { - dispatch({ type: 'prompting' }); - - const AccountProxyContract = new ethers.Contract( - AccountProxy.address, - AccountProxy.abi, - signer - ); - const DebtRepayerContract = new ethers.Contract( - DebtRepayer.address, - DebtRepayer.abi, - signer - ); - - const approveAccountTx = AccountProxyContract.populateTransaction.approve( - DebtRepayer.address, - accountId - ); - - const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( - CoreProxy.address, - SpotMarketProxy.address, - AccountProxy.address, - accountId, - poolId, - collateralTypeAddress, - USDC_BASE_MARKET - ); - - const callsPromise = Promise.all([approveAccountTx, depositDebtToRepay]); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const walletAddress = await signer.getAddress(); - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useRepay', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + + const AccountProxyContract = new ethers.Contract( + AccountProxy.address, + AccountProxy.abi, + signer + ); + const DebtRepayerContract = new ethers.Contract(DebtRepayer.address, DebtRepayer.abi, signer); + + const approveAccountTx = AccountProxyContract.populateTransaction.approve( + DebtRepayer.address, + accountId + ); + + const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( + CoreProxy.address, + SpotMarketProxy.address, + AccountProxy.address, + accountId, + poolId, + collateralTypeAddress, + USDC_BASE_MARKET + ); + + const callsPromise = Promise.all([approveAccountTx, depositDebtToRepay]); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const walletAddress = await signer.getAddress(); + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useRepay', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + dispatch({ type: 'success' }); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useConvertStataUSDC/useConvertStataUSDC.ts b/liquidity/lib/useConvertStataUSDC/useConvertStataUSDC.ts index 1068ec610..4579d31ff 100644 --- a/liquidity/lib/useConvertStataUSDC/useConvertStataUSDC.ts +++ b/liquidity/lib/useConvertStataUSDC/useConvertStataUSDC.ts @@ -75,13 +75,17 @@ export function useConvertStataUSDC({ return receipt; }, + onSuccess: async () => { - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Allowance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'TokenBalance', + 'EthBalance', + 'Allowance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); }, }); } diff --git a/liquidity/lib/useCreateAccount/index.ts b/liquidity/lib/useCreateAccount/index.ts new file mode 100644 index 000000000..01a35a62a --- /dev/null +++ b/liquidity/lib/useCreateAccount/index.ts @@ -0,0 +1 @@ +export * from './useCreateAccount'; diff --git a/liquidity/lib/useCreateAccount/package.json b/liquidity/lib/useCreateAccount/package.json new file mode 100644 index 000000000..529154dc2 --- /dev/null +++ b/liquidity/lib/useCreateAccount/package.json @@ -0,0 +1,13 @@ +{ + "name": "@snx-v3/useCreateAccount", + "private": true, + "main": "index.ts", + "version": "0.0.2", + "dependencies": { + "@snx-v3/useBlockchain": "workspace:*", + "@snx-v3/useCoreProxy": "workspace:*", + "@tanstack/react-query": "^5.8.3", + "debug": "^4.3.7", + "ethers": "^5.7.2" + } +} diff --git a/liquidity/lib/useCreateAccount/useCreateAccount.ts b/liquidity/lib/useCreateAccount/useCreateAccount.ts new file mode 100644 index 000000000..f2f8b9d69 --- /dev/null +++ b/liquidity/lib/useCreateAccount/useCreateAccount.ts @@ -0,0 +1,60 @@ +import { useNetwork, useProvider, useSigner } from '@snx-v3/useBlockchain'; +import { useCoreProxy } from '@snx-v3/useCoreProxy'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import debug from 'debug'; +import { ethers } from 'ethers'; + +const log = debug('snx:useCreateAccount'); + +export function useCreateAccount() { + const { data: CoreProxy } = useCoreProxy(); + const signer = useSigner(); + const { network } = useNetwork(); + const provider = useProvider(); + + const queryClient = useQueryClient(); + return { + enabled: Boolean(network && CoreProxy), + mutation: useMutation({ + mutationFn: async function () { + if (!(CoreProxy && signer && provider)) throw 'OMFG'; + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const txn = await CoreProxyContract['createAccount()'](); + log('txn', txn); + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + + let newAccountId; + + receipt.logs.forEach((log) => { + if (log.topics[0] === CoreProxyContract.interface.getEventTopic('AccountCreated')) { + const [accountId] = CoreProxyContract.interface.decodeEventLog( + 'AccountCreated', + log.data, + log.topics + ); + newAccountId = accountId; + } + }); + log('newAccountId', newAccountId); + + if (newAccountId) { + return newAccountId; + } else { + throw new Error('Could not find new account id'); + } + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'Accounts', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + }, + }), + }; +} diff --git a/liquidity/lib/useDeposit/useDeposit.tsx b/liquidity/lib/useDeposit/useDeposit.tsx index f7f1610b9..bd05680c2 100644 --- a/liquidity/lib/useDeposit/useDeposit.tsx +++ b/liquidity/lib/useDeposit/useDeposit.tsx @@ -8,7 +8,7 @@ import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei, { wei } from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -34,7 +34,7 @@ export const useDeposit = ({ }) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { gasSpeed } = useGasSpeed(); @@ -42,6 +42,7 @@ export const useDeposit = ({ const signer = useSigner(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if ( @@ -61,81 +62,96 @@ export const useDeposit = ({ return; } - try { - dispatch({ type: 'prompting' }); - const walletAddress = await signer.getAddress(); - const id = accountId ?? newAccountId; - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - - // create account only when no account exists - const createAccount = accountId - ? undefined - : CoreProxyContract.populateTransaction['createAccount(uint128)']( - ethers.BigNumber.from(id) - ); - - log('collateralChange', collateralChange); - log('availableCollateral', availableCollateral); - - const amountNeeded = collateralChange.sub(availableCollateral); - log('amountNeeded', amountNeeded); - - // optionally deposit if available collateral not enough - const deposit = amountNeeded.gt(0) - ? CoreProxyContract.populateTransaction.deposit( - ethers.BigNumber.from(id), - collateralTypeAddress, - amountNeeded.toBN() // only deposit what's needed - ) - : undefined; - - log('currentCollateral', currentCollateral); - log('collateralChange', collateralChange); - log('newDelegation', currentCollateral.add(collateralChange)); - const delegate = CoreProxyContract.populateTransaction.delegateCollateral( - ethers.BigNumber.from(id), - ethers.BigNumber.from(poolId), - collateralTypeAddress, - currentCollateral.add(collateralChange).toBN(), - wei(1).toBN() - ); - const callsPromise = Promise.all([createAccount, deposit, delegate].filter(notNil)); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useDeposit', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + const walletAddress = await signer.getAddress(); + const id = accountId ?? newAccountId; + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + + // create account only when no account exists + const createAccount = accountId + ? undefined + : CoreProxyContract.populateTransaction['createAccount(uint128)']( + ethers.BigNumber.from(id) + ); + + log('collateralChange', collateralChange); + log('availableCollateral', availableCollateral); + + const amountNeeded = collateralChange.sub(availableCollateral); + log('amountNeeded', amountNeeded); + + // optionally deposit if available collateral not enough + const deposit = amountNeeded.gt(0) + ? CoreProxyContract.populateTransaction.deposit( + ethers.BigNumber.from(id), + collateralTypeAddress, + amountNeeded.toBN() // only deposit what's needed + ) + : undefined; + + log('currentCollateral', currentCollateral); + log('collateralChange', collateralChange); + log('newDelegation', currentCollateral.add(collateralChange)); + const delegate = CoreProxyContract.populateTransaction.delegateCollateral( + ethers.BigNumber.from(id), + ethers.BigNumber.from(poolId), + collateralTypeAddress, + currentCollateral.add(collateralChange).toBN(), + wei(1).toBN() + ); + const callsPromise = Promise.all([createAccount, deposit, delegate].filter(notNil)); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useDeposit', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'Accounts', + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useDepositBaseAndromeda/package.json b/liquidity/lib/useDepositBaseAndromeda/package.json index 8e43cf734..fab41276d 100644 --- a/liquidity/lib/useDepositBaseAndromeda/package.json +++ b/liquidity/lib/useDepositBaseAndromeda/package.json @@ -15,9 +15,8 @@ "@snx-v3/useGasOptions": "workspace:*", "@snx-v3/useGasPrice": "workspace:*", "@snx-v3/useGasSpeed": "workspace:*", - "@snx-v3/useGetUSDTokens": "workspace:*", "@snx-v3/useSpotMarketProxy": "workspace:*", - "@snx-v3/useSynthTokens": "workspace:*", + "@snx-v3/useSynthToken": "workspace:*", "@snx-v3/withERC7412": "workspace:*", "@synthetixio/wei": "^2.74.4", "@tanstack/react-query": "^5.8.3", diff --git a/liquidity/lib/useDepositBaseAndromeda/useDepositBaseAndromeda.tsx b/liquidity/lib/useDepositBaseAndromeda/useDepositBaseAndromeda.tsx index 673686994..5fa9b16dc 100644 --- a/liquidity/lib/useDepositBaseAndromeda/useDepositBaseAndromeda.tsx +++ b/liquidity/lib/useDepositBaseAndromeda/useDepositBaseAndromeda.tsx @@ -9,15 +9,14 @@ import { useCoreProxy } from '@snx-v3/useCoreProxy'; import { formatGasPriceForTransaction } from '@snx-v3/useGasOptions'; import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; -import { useGetUSDTokens } from '@snx-v3/useGetUSDTokens'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; -import { useSynthTokens } from '@snx-v3/useSynthTokens'; +import { useSynthToken } from '@snx-v3/useSynthToken'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; -import { useReducer } from 'react'; +import React from 'react'; const log = debug('snx:useDepositBaseAndromeda'); @@ -40,16 +39,12 @@ export const useDepositBaseAndromeda = ({ collateralChange: Wei; collateralSymbol?: string; }) => { - const [txnState, dispatch] = useReducer(reducer, initialState); + const [txnState, dispatch] = React.useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); const { data: SpotMarketProxy } = useSpotMarketProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); - const { data: usdTokens } = useGetUSDTokens(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { data: collateralType } = useCollateralType(collateralSymbol); - const { data: synthTokens } = useSynthTokens(); - const synth = synthTokens?.find( - (synth) => synth?.address?.toLowerCase() === collateralType?.tokenAddress?.toLowerCase() - ); + const { data: synthToken } = useSynthToken(collateralType); const { gasSpeed } = useGasSpeed(); @@ -57,6 +52,7 @@ export const useDepositBaseAndromeda = ({ const signer = useSigner(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if ( @@ -70,8 +66,8 @@ export const useDepositBaseAndromeda = ({ collateralTypeAddress && availableCollateral && currentCollateral && - usdTokens?.sUSD && - synth + synthToken && + synthToken.token ) ) { return; @@ -79,128 +75,143 @@ export const useDepositBaseAndromeda = ({ if (collateralChange.eq(0)) return; - try { - // Steps: - // 1. Create an account if not exists - // 2. Wrap USDC or stataUSDC to sUSDC or sStataUSDC - // 3. Approve sUSDC or sStataUSDC - // 4. Deposit sUSDC or sStataUSDC - // 5. Delegate collateral - - dispatch({ type: 'prompting' }); - const walletAddress = await signer.getAddress(); - const id = accountId ?? newAccountId; - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const SpotMarketProxyContract = new ethers.Contract( - SpotMarketProxy.address, - SpotMarketProxy.abi, - signer - ); - - // create account only when no account exists - const createAccount = accountId - ? undefined - : CoreProxyContract.populateTransaction['createAccount(uint128)']( - ethers.BigNumber.from(id) - ); - - log('collateralChange', collateralChange); - log('availableCollateral', availableCollateral); - - const synthAmountNeeded = collateralChange - .sub(availableCollateral) - // Reduce precision to avoid rounding issues - .mul(ethers.utils.parseUnits('1', synth.token.decimals)) - .div(D18) - // revert back to 18 - .mul(D18) - .div(ethers.utils.parseUnits('1', synth.token.decimals)); - log('synthAmountNeeded', synthAmountNeeded); - - const tokenAmountToWrap = synthAmountNeeded - .mul(ethers.utils.parseUnits('1', synth.token.decimals)) - .div(D18); - log('tokenAmountToWrap', tokenAmountToWrap); - - // Wrap - const wrap = synthAmountNeeded.gt(0) - ? SpotMarketProxyContract.populateTransaction.wrap( - synth.synthMarketId, - tokenAmountToWrap.toBN(), - synthAmountNeeded.toBN() - ) - : undefined; - - // Synth - const SynthTokenContract = new ethers.Contract(synth.address, approveAbi, signer); - - const synthApproval = synthAmountNeeded.gt(0) - ? SynthTokenContract.populateTransaction.approve( - CoreProxy.address, - synthAmountNeeded.toBN() - ) - : undefined; - - // optionally deposit if available collateral not enough - const deposit = synthAmountNeeded.gt(0) - ? CoreProxyContract.populateTransaction.deposit( - ethers.BigNumber.from(id), - synth.address, - synthAmountNeeded.toBN() // only deposit what's needed - ) - : undefined; - - log('currentCollateral', currentCollateral); - log('collateralChange', collateralChange); - log('newDelegation', currentCollateral.add(collateralChange)); - const delegate = CoreProxyContract.populateTransaction.delegateCollateral( - ethers.BigNumber.from(id), - ethers.BigNumber.from(poolId), - synth.address, - currentCollateral.add(collateralChange).toBN(), - ethers.utils.parseEther('1') - ); - - const callsPromise = Promise.all( - [wrap, synthApproval, createAccount, deposit, delegate].filter(notNil) - ); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useDepositBaseAndromeda', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + // Steps: + // 1. Create an account if not exists + // 2. Wrap USDC or stataUSDC to sUSDC or sStataUSDC + // 3. Approve sUSDC or sStataUSDC + // 4. Deposit sUSDC or sStataUSDC + // 5. Delegate collateral + + dispatch({ type: 'prompting' }); + const walletAddress = await signer.getAddress(); + const id = accountId ?? newAccountId; + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const SpotMarketProxyContract = new ethers.Contract( + SpotMarketProxy.address, + SpotMarketProxy.abi, + signer + ); + + // create account only when no account exists + const createAccount = accountId + ? undefined + : CoreProxyContract.populateTransaction['createAccount(uint128)']( + ethers.BigNumber.from(id) + ); + + log('collateralChange', collateralChange); + log('availableCollateral', availableCollateral); + + const synthAmountNeeded = collateralChange + .sub(availableCollateral) + // Reduce precision to avoid rounding issues + .mul(ethers.utils.parseUnits('1', synthToken.token.decimals)) + .div(D18) + // revert back to 18 + .mul(D18) + .div(ethers.utils.parseUnits('1', synthToken.token.decimals)); + log('synthAmountNeeded', synthAmountNeeded); + + const tokenAmountToWrap = synthAmountNeeded + .mul(ethers.utils.parseUnits('1', synthToken.token.decimals)) + .div(D18); + log('tokenAmountToWrap', tokenAmountToWrap); + + // Wrap + const wrap = synthAmountNeeded.gt(0) + ? SpotMarketProxyContract.populateTransaction.wrap( + synthToken.synthMarketId, + tokenAmountToWrap.toBN(), + synthAmountNeeded.toBN() + ) + : undefined; + + // Synth + const SynthTokenContract = new ethers.Contract(synthToken.address, approveAbi, signer); + + const synthApproval = synthAmountNeeded.gt(0) + ? SynthTokenContract.populateTransaction.approve( + CoreProxy.address, + synthAmountNeeded.toBN() + ) + : undefined; + + // optionally deposit if available collateral not enough + const deposit = synthAmountNeeded.gt(0) + ? CoreProxyContract.populateTransaction.deposit( + ethers.BigNumber.from(id), + synthToken.address, + synthAmountNeeded.toBN() // only deposit what's needed + ) + : undefined; + + log('currentCollateral', currentCollateral); + log('collateralChange', collateralChange); + log('newDelegation', currentCollateral.add(collateralChange)); + const delegate = CoreProxyContract.populateTransaction.delegateCollateral( + ethers.BigNumber.from(id), + ethers.BigNumber.from(poolId), + synthToken.address, + currentCollateral.add(collateralChange).toBN(), + ethers.utils.parseEther('1') + ); + + const callsPromise = Promise.all( + [wrap, synthApproval, createAccount, deposit, delegate].filter(notNil) + ); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useDepositBaseAndromeda', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'Accounts', + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useLiquidityPosition/useLiquidityPosition.ts b/liquidity/lib/useLiquidityPosition/useLiquidityPosition.ts index 8748e9d9f..c0143b1f5 100644 --- a/liquidity/lib/useLiquidityPosition/useLiquidityPosition.ts +++ b/liquidity/lib/useLiquidityPosition/useLiquidityPosition.ts @@ -44,10 +44,7 @@ export const useLiquidityPosition = ({ 'LiquidityPosition', { accountId }, { tokenAddress: collateralType?.tokenAddress }, - { - contractsHash: contractsHash([CoreProxy]), - collateralTypes: contractsHash([systemToken, collateralType]), - }, + { contractsHash: contractsHash([CoreProxy, systemToken]) }, ], enabled: Boolean( network && provider && CoreProxy && systemToken && accountId && collateralType diff --git a/liquidity/lib/useManagePermissions/useManagePermissions.ts b/liquidity/lib/useManagePermissions/useManagePermissions.ts index 2ac343522..1c117176d 100644 --- a/liquidity/lib/useManagePermissions/useManagePermissions.ts +++ b/liquidity/lib/useManagePermissions/useManagePermissions.ts @@ -34,7 +34,7 @@ export const useManagePermissions = ({ existing = [], selected = [], }: { - accountId: string; + accountId: ethers.BigNumber; target: string; existing: Permissions; selected: Permissions; diff --git a/liquidity/lib/usePositionDebt/usePositionDebt.ts b/liquidity/lib/usePositionDebt/usePositionDebt.ts index ccebb4464..550888032 100644 --- a/liquidity/lib/usePositionDebt/usePositionDebt.ts +++ b/liquidity/lib/usePositionDebt/usePositionDebt.ts @@ -23,10 +23,7 @@ export function usePositionDebt({ `${network?.id}-${network?.preset}`, 'PositionDebt', { accountId }, - { - pool: poolId, - token: tokenAddress, - }, + { pool: poolId, token: tokenAddress }, { contractsHash: contractsHash([CoreProxy]) }, ], enabled: Boolean(network && provider && CoreProxy && accountId && poolId && tokenAddress), diff --git a/liquidity/lib/useRepay/useRepay.tsx b/liquidity/lib/useRepay/useRepay.tsx index 191bcef77..1dd627be7 100644 --- a/liquidity/lib/useRepay/useRepay.tsx +++ b/liquidity/lib/useRepay/useRepay.tsx @@ -10,7 +10,7 @@ import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { useSystemToken } from '@snx-v3/useSystemToken'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -33,7 +33,7 @@ export const useRepay = ({ }) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { data: systemToken } = useSystemToken(); const signer = useSigner(); @@ -41,6 +41,7 @@ export const useRepay = ({ const { gasSpeed } = useGasSpeed(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -54,65 +55,79 @@ export const useRepay = ({ const debtChangeAbs = debtChange.abs(); const amountToDeposit = debtChangeAbs.sub(availableUSDCollateral || ZEROWEI); - try { - dispatch({ type: 'prompting' }); - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - - // Only deposit if user doesn't have enough sUSD collateral - const deposit = amountToDeposit.lte(0) - ? undefined - : CoreProxyContract.populateTransaction.deposit( - ethers.BigNumber.from(accountId), - systemToken.address, - amountToDeposit.toBN() // only deposit what's needed - ); - - const burn = CoreProxyContract.populateTransaction.burnUsd( - ethers.BigNumber.from(accountId), - ethers.BigNumber.from(poolId), - collateralTypeAddress, - debtChangeAbs.toBN() - ); - - const callsPromise = Promise.all([deposit, burn].filter(notNil)); - const walletAddress = await signer.getAddress(); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useRepay', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + + // Only deposit if user doesn't have enough sUSD collateral + const deposit = amountToDeposit.lte(0) + ? undefined + : CoreProxyContract.populateTransaction.deposit( + ethers.BigNumber.from(accountId), + systemToken.address, + amountToDeposit.toBN() // only deposit what's needed + ); + + const burn = CoreProxyContract.populateTransaction.burnUsd( + ethers.BigNumber.from(accountId), + ethers.BigNumber.from(poolId), + collateralTypeAddress, + debtChangeAbs.toBN() + ); + + const callsPromise = Promise.all([deposit, burn].filter(notNil)); + const walletAddress = await signer.getAddress(); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useRepay', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useRepayBaseAndromeda/useRepayBaseAndromeda.tsx b/liquidity/lib/useRepayBaseAndromeda/useRepayBaseAndromeda.tsx index 4fcfe239b..329c38ee5 100644 --- a/liquidity/lib/useRepayBaseAndromeda/useRepayBaseAndromeda.tsx +++ b/liquidity/lib/useRepayBaseAndromeda/useRepayBaseAndromeda.tsx @@ -14,7 +14,7 @@ import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { useSystemToken } from '@snx-v3/useSystemToken'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { BigNumber, ethers } from 'ethers'; import { useReducer } from 'react'; @@ -40,7 +40,7 @@ export const useRepayBaseAndromeda = ({ const { data: CoreProxy } = useCoreProxy(); const { data: systemToken } = useSystemToken(); const { data: SpotMarketProxy } = useSpotMarketProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { data: usdTokens } = useGetUSDTokens(); const signer = useSigner(); @@ -48,6 +48,7 @@ export const useRepayBaseAndromeda = ({ const { gasSpeed } = useGasSpeed(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -74,106 +75,116 @@ export const useRepayBaseAndromeda = ({ ? parseUnits(amountToDeposit.toString(), 6) : BigNumber.from(0); - try { - dispatch({ type: 'prompting' }); - - const spotMarketId = getSpotMarketId(collateralSymbol); - - const SpotMarketProxyContract = new ethers.Contract( - SpotMarketProxy.address, - SpotMarketProxy.abi, - signer - ); - - // USDC or stataUSDC to sUSDC or sStataUSDC - const wrap = collateralAmount.gt(0) - ? SpotMarketProxyContract.populateTransaction.wrap(spotMarketId, collateralAmount, 0) - : undefined; - - const Synth_Contract = new ethers.Contract(collateralTypeAddress, approveAbi, signer); - const synth_approval = amountToDeposit.gt(0) - ? Synth_Contract.populateTransaction.approve( - SpotMarketProxy.address, - amountToDeposit.toBN() - ) - : undefined; - - // sUSDC or sStataUSDC => snxUSD - const sell_synth = amountToDeposit.gt(0) - ? SpotMarketProxyContract.populateTransaction.sell( - spotMarketId, - amountToDeposit.toBN(), - 0, - ethers.constants.AddressZero - ) - : undefined; - - // approve sUSD to Core - const SystemTokenContract = new ethers.Contract(systemToken.address, approveAbi, signer); - const sUSD_Approval = amountToDeposit.gt(0) - ? SystemTokenContract.populateTransaction.approve( - CoreProxy.address, - amountToDeposit.toBN() - ) - : undefined; - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - - // Only deposit if user doesn't have enough sUSD collateral - const deposit = amountToDeposit.lte(0) - ? undefined - : CoreProxyContract.populateTransaction.deposit( - BigNumber.from(accountId), - systemToken.address, - amountToDeposit.toBN() // only deposit what's needed - ); - - const burn = CoreProxyContract.populateTransaction.burnUsd( - BigNumber.from(accountId), - BigNumber.from(poolId), - collateralTypeAddress, - debtChangeAbs.toBN() - ); - - const callsPromise = Promise.all( - [wrap, synth_approval, sell_synth, sUSD_Approval, deposit, burn].filter(notNil) - ); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const walletAddress = await signer.getAddress(); - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useRepay', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + + const spotMarketId = getSpotMarketId(collateralSymbol); + + const SpotMarketProxyContract = new ethers.Contract( + SpotMarketProxy.address, + SpotMarketProxy.abi, + signer + ); + + // USDC or stataUSDC to sUSDC or sStataUSDC + const wrap = collateralAmount.gt(0) + ? SpotMarketProxyContract.populateTransaction.wrap(spotMarketId, collateralAmount, 0) + : undefined; + + const Synth_Contract = new ethers.Contract(collateralTypeAddress, approveAbi, signer); + const synth_approval = amountToDeposit.gt(0) + ? Synth_Contract.populateTransaction.approve( + SpotMarketProxy.address, + amountToDeposit.toBN() + ) + : undefined; + + // sUSDC or sStataUSDC => snxUSD + const sell_synth = amountToDeposit.gt(0) + ? SpotMarketProxyContract.populateTransaction.sell( + spotMarketId, + amountToDeposit.toBN(), + 0, + ethers.constants.AddressZero + ) + : undefined; + + // approve sUSD to Core + const SystemTokenContract = new ethers.Contract(systemToken.address, approveAbi, signer); + const sUSD_Approval = amountToDeposit.gt(0) + ? SystemTokenContract.populateTransaction.approve(CoreProxy.address, amountToDeposit.toBN()) + : undefined; + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + + // Only deposit if user doesn't have enough sUSD collateral + const deposit = amountToDeposit.lte(0) + ? undefined + : CoreProxyContract.populateTransaction.deposit( + BigNumber.from(accountId), + systemToken.address, + amountToDeposit.toBN() // only deposit what's needed + ); + + const burn = CoreProxyContract.populateTransaction.burnUsd( + BigNumber.from(accountId), + BigNumber.from(poolId), + collateralTypeAddress, + debtChangeAbs.toBN() + ); + + const callsPromise = Promise.all( + [wrap, synth_approval, sell_synth, sUSD_Approval, deposit, burn].filter(notNil) + ); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const walletAddress = await signer.getAddress(); + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useRepay', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useRewards/useRewards.ts b/liquidity/lib/useRewards/useRewards.ts index 3c5da1473..1e0673342 100644 --- a/liquidity/lib/useRewards/useRewards.ts +++ b/liquidity/lib/useRewards/useRewards.ts @@ -52,7 +52,6 @@ export function useRewards({ contractsHash: contractsHash([ CoreProxy, Multicall3, - AllErrors, ...(rewardsDistributors ?? []), ...(synthTokens ?? []), ]), diff --git a/liquidity/lib/useSynthBalances/useSynthBalances.ts b/liquidity/lib/useSynthBalances/useSynthBalances.ts index ae218664e..07dd67b6b 100644 --- a/liquidity/lib/useSynthBalances/useSynthBalances.ts +++ b/liquidity/lib/useSynthBalances/useSynthBalances.ts @@ -37,35 +37,28 @@ export function useSynthBalances() { const TokenInterface = new ethers.utils.Interface([ 'function balanceOf(address) view returns (uint256)', ]); - const multicall = [ - ...synthTokens.map((synth) => ({ - method: 'balanceOf', - args: [walletAddress], - synth, - isSynth: true, - })), - ...synthTokens.map((synth) => ({ - method: 'balanceOf', - args: [walletAddress], - synth, - isSynth: false, - })), - ]; - log('multicall', multicall); - - const calls = multicall.map(({ method, args, synth, isSynth }) => ({ - target: isSynth ? synth.address : synth.token.address, - callData: TokenInterface.encodeFunctionData(method, args), + const multicall = synthTokens.map((synth) => ({ + synth, + method: 'balanceOf', + args: [walletAddress], + target: synth.address, + callData: TokenInterface.encodeFunctionData('balanceOf', [walletAddress]), allowFailure: true, })); - log('calls', calls); + log('multicall', multicall); const Multicall3Contract = new ethers.Contract(Multicall3.address, Multicall3.abi, provider); - const multicallResponse = await Multicall3Contract.callStatic.aggregate3(calls); + const multicallResponse = await Multicall3Contract.callStatic.aggregate3( + multicall.map(({ target, callData, allowFailure }) => ({ + target, + callData, + allowFailure, + })) + ); log('multicallResponse', multicallResponse); const balances = multicall - .map(({ method, synth, isSynth }, i) => { + .map(({ method, synth }, i) => { const { success, returnData } = multicallResponse[i]; if (!success) { log(`${method} call error for ${synth.symbol}`); @@ -74,31 +67,13 @@ export function useSynthBalances() { const [balance] = TokenInterface.decodeFunctionResult(method, returnData); return { synth, - balance: wei(balance, isSynth ? synth.decimals : synth.token.decimals), - isSynth, + balance: wei(balance, synth.decimals), }; }) .filter((info) => info !== undefined); log('balances', balances); - const map = new Map(); - balances.forEach(({ synth, balance, isSynth }) => { - if (map.has(synth.address)) { - map.set(synth.address, { - synth, - synthBalance: isSynth ? balance : map.get(synth.address).synthBalance, - tokenBalance: isSynth ? map.get(synth.address).tokenBalance : balance, - }); - } else { - map.set(synth.address, { - synth, - synthBalance: isSynth ? balance : wei(0, synth.token.decimals), - tokenBalance: isSynth ? wei(0, synth.decimals) : balance, - }); - } - }); - const combinedBalances = map.values().toArray(); - log('combinedBalances', combinedBalances); - return combinedBalances; + + return balances; }, }); } diff --git a/liquidity/lib/useSynthToken/index.ts b/liquidity/lib/useSynthToken/index.ts new file mode 100644 index 000000000..7334c9110 --- /dev/null +++ b/liquidity/lib/useSynthToken/index.ts @@ -0,0 +1 @@ +export * from './useSynthToken'; diff --git a/liquidity/lib/useSynthToken/package.json b/liquidity/lib/useSynthToken/package.json new file mode 100644 index 000000000..cc26c291c --- /dev/null +++ b/liquidity/lib/useSynthToken/package.json @@ -0,0 +1,15 @@ +{ + "name": "@snx-v3/useSynthToken", + "private": true, + "main": "index.ts", + "version": "0.0.2", + "dependencies": { + "@snx-v3/tsHelpers": "workspace:*", + "@snx-v3/useBlockchain": "workspace:*", + "@snx-v3/useCollateralTypes": "workspace:*", + "@snx-v3/useSynthTokens": "workspace:*", + "@tanstack/react-query": "^5.8.3", + "debug": "^4.3.7", + "react": "^18.2.0" + } +} diff --git a/liquidity/lib/useSynthToken/useSynthToken.ts b/liquidity/lib/useSynthToken/useSynthToken.ts new file mode 100644 index 000000000..530d05ade --- /dev/null +++ b/liquidity/lib/useSynthToken/useSynthToken.ts @@ -0,0 +1,36 @@ +import { contractsHash } from '@snx-v3/tsHelpers'; +import { Network, useNetwork } from '@snx-v3/useBlockchain'; +import { type CollateralType } from '@snx-v3/useCollateralTypes'; +import { useSynthTokens } from '@snx-v3/useSynthTokens'; +import { useQuery } from '@tanstack/react-query'; +import debug from 'debug'; + +const log = debug('snx:useSynthToken'); + +export function useSynthToken(collateralType?: CollateralType, networkOverride?: Network) { + const { data: synthTokens } = useSynthTokens(); + const { network: currentNetwork } = useNetwork(); + const network = networkOverride || currentNetwork; + + return useQuery({ + enabled: Boolean(network && collateralType && synthTokens), + queryKey: [ + `${network?.id}-${network?.preset}`, + 'SynthToken', + { collateralType: collateralType?.address }, + { contractsHash: contractsHash([...(synthTokens ?? [])]) }, + ], + queryFn: async () => { + if (!(network && collateralType && synthTokens)) { + throw new Error('OMG'); + } + log('collateralType', collateralType); + + const tokenAddress = collateralType.address.toLowerCase(); + const synthToken = synthTokens.find(({ address }) => address.toLowerCase() === tokenAddress); + log('synthToken', synthToken); + + return synthToken; + }, + }); +} diff --git a/liquidity/lib/useTransferAccountId/useTransferAccountId.ts b/liquidity/lib/useTransferAccountId/useTransferAccountId.ts index a2eede5d7..f2e7245a3 100644 --- a/liquidity/lib/useTransferAccountId/useTransferAccountId.ts +++ b/liquidity/lib/useTransferAccountId/useTransferAccountId.ts @@ -6,7 +6,7 @@ import { ethers } from 'ethers'; const log = debug('snx:useTransferAccountId'); -export function useTransferAccountId(to: string, accountId: string) { +export function useTransferAccountId(to: string, accountId: ethers.BigNumber) { const { data: AccountProxy } = useAccountProxy(); const { activeWallet } = useWallet(); const signer = useSigner(); diff --git a/liquidity/lib/useUndelegate/useUndelegate.tsx b/liquidity/lib/useUndelegate/useUndelegate.tsx index bd4fbaefd..8da6d3ba0 100644 --- a/liquidity/lib/useUndelegate/useUndelegate.tsx +++ b/liquidity/lib/useUndelegate/useUndelegate.tsx @@ -7,7 +7,7 @@ import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei, { wei } from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -29,67 +29,84 @@ export const useUndelegate = ({ }) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const signer = useSigner(); const { gasSpeed } = useGasSpeed(); const provider = useProvider(); const { network } = useNetwork(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); if (!(CoreProxy && poolId && collateralTypeAddress)) return; if (collateralChange.eq(0)) return; if (currentCollateral.eq(0)) return; - try { - dispatch({ type: 'prompting' }); - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const populatedTxnPromised = CoreProxyContract.populateTransaction.delegateCollateral( - ethers.BigNumber.from(accountId), - ethers.BigNumber.from(poolId), - collateralTypeAddress, - currentCollateral.add(collateralChange).toBN(), - wei(1).toBN() - ); + dispatch({ type: 'prompting' }); - const walletAddress = await signer.getAddress(); + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const populatedTxnPromised = CoreProxyContract.populateTransaction.delegateCollateral( + ethers.BigNumber.from(accountId), + ethers.BigNumber.from(poolId), + collateralTypeAddress, + currentCollateral.add(collateralChange).toBN(), + wei(1).toBN() + ); - const callsPromise = Promise.all([populatedTxnPromised]); - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } + const walletAddress = await signer.getAddress(); - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useUndelegate', - walletAddress - ); + const callsPromise = Promise.all([populatedTxnPromised]); + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); + } - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useUndelegate', + walletAddress + ); - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; - } + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + 'AccountCollateralUnlockDate', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx b/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx index 5a99bf6d6..a7ab1cfac 100644 --- a/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx +++ b/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx @@ -15,7 +15,7 @@ import { type PositionPageSchemaType, useParams } from '@snx-v3/useParams'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { withERC7412 } from '@snx-v3/withERC7412'; import { Wei, wei } from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import React from 'react'; @@ -37,7 +37,7 @@ export function useUndelegateBaseAndromeda({ collateralChange }: { collateralCha const [txnState, dispatch] = React.useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); const { data: SpotMarketProxy } = useSpotMarketProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const signer = useSigner(); const { gasSpeed } = useGasSpeed(); @@ -47,6 +47,7 @@ export function useUndelegateBaseAndromeda({ collateralChange }: { collateralCha const { data: AccountProxy } = useAccountProxy(); const { data: DebtRepayer } = useDebtRepayer(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -65,87 +66,96 @@ export function useUndelegateBaseAndromeda({ collateralChange }: { collateralCha if (collateralChange.eq(0)) return; if (currentCollateral.eq(0)) return; - try { - dispatch({ type: 'prompting' }); - - const AccountProxyContract = new ethers.Contract( - AccountProxy.address, - AccountProxy.abi, - signer - ); - - const DebtRepayerContract = new ethers.Contract( - DebtRepayer.address, - DebtRepayer.abi, - signer - ); - - const approveAccountTx = AccountProxyContract.populateTransaction.approve( - DebtRepayer.address, - params.accountId - ); - - const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( - CoreProxy.address, - SpotMarketProxy.address, - AccountProxy.address, - params.accountId, - params.poolId, - collateralTypeAddress, - USDC_BASE_MARKET - ); - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - - const delegateTx = CoreProxyContract.populateTransaction.delegateCollateral( - ethers.BigNumber.from(params.accountId), - ethers.BigNumber.from(params.poolId), - collateralTypeAddress, - currentCollateral.add(collateralChange).toBN(), - wei(1).toBN() - ); - - const callsPromise: Promise< - (ethers.PopulatedTransaction & { requireSuccess?: boolean })[] - > = Promise.all([approveAccountTx, depositDebtToRepay, delegateTx].filter(notNil)); - - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const walletAddress = await signer.getAddress(); - - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useUndelegateBase', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + + const AccountProxyContract = new ethers.Contract( + AccountProxy.address, + AccountProxy.abi, + signer + ); + + const DebtRepayerContract = new ethers.Contract(DebtRepayer.address, DebtRepayer.abi, signer); + + const approveAccountTx = AccountProxyContract.populateTransaction.approve( + DebtRepayer.address, + params.accountId + ); + + const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( + CoreProxy.address, + SpotMarketProxy.address, + AccountProxy.address, + params.accountId, + params.poolId, + collateralTypeAddress, + USDC_BASE_MARKET + ); + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + + const delegateTx = CoreProxyContract.populateTransaction.delegateCollateral( + ethers.BigNumber.from(params.accountId), + ethers.BigNumber.from(params.poolId), + collateralTypeAddress, + currentCollateral.add(collateralChange).toBN(), + wei(1).toBN() + ); + + const callsPromise: Promise<(ethers.PopulatedTransaction & { requireSuccess?: boolean })[]> = + Promise.all([approveAccountTx, depositDebtToRepay, delegateTx].filter(notNil)); + + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const walletAddress = await signer.getAddress(); + + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useUndelegateBase', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'AccountCollateralUnlockDate', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useUnwrapAllSynths/useUnwrapAllSynths.tsx b/liquidity/lib/useUnwrapAllSynths/useUnwrapAllSynths.tsx index f4d7c4187..0184067c6 100644 --- a/liquidity/lib/useUnwrapAllSynths/useUnwrapAllSynths.tsx +++ b/liquidity/lib/useUnwrapAllSynths/useUnwrapAllSynths.tsx @@ -34,7 +34,7 @@ export function useUnwrapAllSynths() { const errorParser = useContractErrorParser(); - const client = useQueryClient(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async function () { @@ -52,13 +52,13 @@ export function useUnwrapAllSynths() { signer ); synthBalances - .filter(({ synthBalance }) => synthBalance.gt(0)) - .forEach(({ synth, synthBalance }) => { + .filter(({ balance }) => balance.gt(0)) + .forEach(({ synth, balance }) => { transactions.push( SpotMarketProxyContract.populateTransaction.unwrap( synth.synthMarketId, - synthBalance.toBN(), - synthBalance.toBN().sub(synthBalance.toBN().div(100)) + balance.toBN(), + balance.toBN().sub(balance.toBN().div(100)) ) ); }); @@ -93,7 +93,6 @@ export function useUnwrapAllSynths() { dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); const receipt = await provider.waitForTransaction(txn.hash); log('receipt', receipt); - dispatch({ type: 'success' }); return receipt; }, @@ -119,16 +118,20 @@ export function useUnwrapAllSynths() { }); }, - onSuccess() { - client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'PriceUpdates'], - }); - client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'SynthBalances'], - }); - client.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + + dispatch({ type: 'success' }); + toast.closeAll(); toast({ title: 'Success', diff --git a/liquidity/lib/useUnwrapStataUSDC/useUnwrapStataUSDC.ts b/liquidity/lib/useUnwrapStataUSDC/useUnwrapStataUSDC.ts index ed2b9a008..84bb01360 100644 --- a/liquidity/lib/useUnwrapStataUSDC/useUnwrapStataUSDC.ts +++ b/liquidity/lib/useUnwrapStataUSDC/useUnwrapStataUSDC.ts @@ -54,11 +54,16 @@ export function useUnwrapStataUSDC() { log('receipt', receipt); return receipt; }, - mutationKey: ['unwrapStataUSDC'], + onSuccess: async () => { - await queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'TokenBalance', + 'EthBalance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); }, }); } diff --git a/liquidity/lib/useWithdraw/useWithdraw.tsx b/liquidity/lib/useWithdraw/useWithdraw.tsx index 5a4a37a05..81e1e9da4 100644 --- a/liquidity/lib/useWithdraw/useWithdraw.tsx +++ b/liquidity/lib/useWithdraw/useWithdraw.tsx @@ -8,7 +8,7 @@ import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; import { withERC7412 } from '@snx-v3/withERC7412'; import Wei from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { BigNumber, ethers } from 'ethers'; import { useReducer } from 'react'; @@ -26,13 +26,14 @@ export const useWithdraw = ({ }) => { const [txnState, dispatch] = useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { network } = useNetwork(); const { gasSpeed } = useGasSpeed(); const signer = useSigner(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -46,63 +47,78 @@ export const useWithdraw = ({ const walletAddress = await signer.getAddress(); - try { - dispatch({ type: 'prompting' }); - - const contract = new ethers.Contract( - collateralTypeAddress, - ['function decimals() view returns (uint8)'], - provider - ); - - const decimals = await contract.decimals(); - - const collateralAmount = amount.gt(0) - ? parseUnits(amount.toString(), decimals) - : BigNumber.from(0); - - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const populatedTxnPromised = CoreProxyContract.populateTransaction.withdraw( - BigNumber.from(accountId), - collateralTypeAddress, - collateralAmount - ); - - const callsPromise = Promise.all([populatedTxnPromised]); - const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); - if (priceUpdateTx) { - calls.unshift(priceUpdateTx as any); - } - - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - calls, - 'useWithdraw', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; + dispatch({ type: 'prompting' }); + + const contract = new ethers.Contract( + collateralTypeAddress, + ['function decimals() view returns (uint8)'], + provider + ); + + const decimals = await contract.decimals(); + + const collateralAmount = amount.gt(0) + ? parseUnits(amount.toString(), decimals) + : BigNumber.from(0); + + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const populatedTxnPromised = CoreProxyContract.populateTransaction.withdraw( + BigNumber.from(accountId), + collateralTypeAddress, + collateralAmount + ); + + const callsPromise = Promise.all([populatedTxnPromised]); + const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); + if (priceUpdateTx) { + calls.unshift(priceUpdateTx as any); } + + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + calls, + 'useWithdraw', + walletAddress + ); + + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); + + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; + }, + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + 'AccountCollateralUnlockDate', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); return { diff --git a/liquidity/lib/useWithdrawBaseAndromeda/useWithdrawBaseAndromeda.tsx b/liquidity/lib/useWithdrawBaseAndromeda/useWithdrawBaseAndromeda.tsx index 97618a570..771a60bfd 100644 --- a/liquidity/lib/useWithdrawBaseAndromeda/useWithdrawBaseAndromeda.tsx +++ b/liquidity/lib/useWithdrawBaseAndromeda/useWithdrawBaseAndromeda.tsx @@ -16,7 +16,7 @@ import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { useUSDProxy } from '@snx-v3/useUSDProxy'; import { withERC7412 } from '@snx-v3/withERC7412'; import { Wei } from '@synthetixio/wei'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import { useReducer } from 'react'; @@ -37,7 +37,7 @@ export const useWithdrawBaseAndromeda = ({ amountToWithdraw }: { amountToWithdra const { data: CoreProxy } = useCoreProxy(); const { data: SpotMarketProxy } = useSpotMarketProxy(); const { data: USDProxy } = useUSDProxy(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { network } = useNetwork(); const { data: usdTokens } = useGetUSDTokens(); @@ -45,6 +45,7 @@ export const useWithdrawBaseAndromeda = ({ amountToWithdraw }: { amountToWithdra const signer = useSigner(); const provider = useProvider(); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); @@ -87,147 +88,156 @@ export const useWithdrawBaseAndromeda = ({ amountToWithdraw }: { amountToWithdra let sUSDC_amount = ZEROWEI; - try { - const spotMarketId = getSpotMarketId(params.collateralSymbol); - log('spotMarketId', spotMarketId); + const spotMarketId = getSpotMarketId(params.collateralSymbol); + log('spotMarketId', spotMarketId); - if (spotMarketId === USDC_BASE_MARKET) { - sUSDC_amount = sUSDC_amount.add(wrappedCollateralAmount); - } - log('sUSDC_amount', sUSDC_amount); + if (spotMarketId === USDC_BASE_MARKET) { + sUSDC_amount = sUSDC_amount.add(wrappedCollateralAmount); + } + log('sUSDC_amount', sUSDC_amount); - dispatch({ type: 'prompting' }); + dispatch({ type: 'prompting' }); - const gasPricesPromised = getGasPrice({ provider }); + const gasPricesPromised = getGasPrice({ provider }); - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); - const USDProxyContract = new ethers.Contract(USDProxy.address, USDProxy.abi, signer); - const SpotProxyContract = new ethers.Contract( - SpotMarketProxy.address, - SpotMarketProxy.abi, - signer - ); + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const USDProxyContract = new ethers.Contract(USDProxy.address, USDProxy.abi, signer); + const SpotProxyContract = new ethers.Contract( + SpotMarketProxy.address, + SpotMarketProxy.abi, + signer + ); - const withdraw_collateral = wrappedCollateralAmount.gt(0) - ? CoreProxyContract.populateTransaction.withdraw( - ethers.BigNumber.from(accountId), - collateralType.tokenAddress, - wrappedCollateralAmount.toBN() + const withdraw_collateral = wrappedCollateralAmount.gt(0) + ? CoreProxyContract.populateTransaction.withdraw( + ethers.BigNumber.from(accountId), + collateralType.tokenAddress, + wrappedCollateralAmount.toBN() + ) + : undefined; + + //snxUSD + const withdraw_snxUSD = snxUSDAmount.gt(0) + ? CoreProxyContract.populateTransaction.withdraw( + ethers.BigNumber.from(accountId), + usdTokens?.snxUSD, + snxUSDAmount.toBN() + ) + : undefined; + + const snxUSDApproval = snxUSDAmount.gt(0) + ? USDProxyContract.populateTransaction.approve(SpotMarketProxy.address, snxUSDAmount.toBN()) + : undefined; + + //snxUSD => sUSDC + const buy_sUSDC = snxUSDAmount.gt(0) + ? SpotProxyContract.populateTransaction.buy( + USDC_BASE_MARKET, + snxUSDAmount.toBN(), + 0, + ethers.constants.AddressZero + ) + : undefined; + + const synthAmount = snxUSDAmount.gt(0) + ? ( + await SpotProxyContract.callStatic.quoteBuyExactIn( + USDC_BASE_MARKET, + snxUSDAmount.toBN(), + 0 ) - : undefined; - - //snxUSD - const withdraw_snxUSD = snxUSDAmount.gt(0) - ? CoreProxyContract.populateTransaction.withdraw( - ethers.BigNumber.from(accountId), - usdTokens?.snxUSD, - snxUSDAmount.toBN() + ).synthAmount + : ZEROWEI; + const unwrapAmount = sUSDC_amount.add(synthAmount); + + //sUSDC => USDC + const unwrapTxnPromised = unwrapAmount.gt(0) + ? SpotProxyContract.populateTransaction.unwrap(USDC_BASE_MARKET, unwrapAmount.toBN(), 0) + : undefined; + + const unwrapCollateralTxnPromised = + spotMarketId === STATA_BASE_MARKET && wrappedCollateralAmount.gt(0) + ? SpotProxyContract.populateTransaction.unwrap( + STATA_BASE_MARKET, + wrappedCollateralAmount.toBN(), + 0 ) : undefined; - const snxUSDApproval = snxUSDAmount.gt(0) - ? USDProxyContract.populateTransaction.approve( - SpotMarketProxy.address, - snxUSDAmount.toBN() - ) - : undefined; + const [ + gasPrices, + withdraw_collateral_txn, + withdraw_snxUSD_txn, + snxUSDApproval_txn, + buy_sUSDC_txn, + unwrapTxnPromised_txn, + unwrapCollateralTxnPromised_txn, + ] = await Promise.all([ + gasPricesPromised, + withdraw_collateral, + withdraw_snxUSD, + snxUSDApproval, + buy_sUSDC, + unwrapTxnPromised, + unwrapCollateralTxnPromised, + ]); + + const allCalls = [ + withdraw_collateral_txn, + withdraw_snxUSD_txn, + snxUSDApproval_txn, + buy_sUSDC_txn, + unwrapTxnPromised_txn, + unwrapCollateralTxnPromised_txn, + ].filter(notNil); + + if (priceUpdateTx) { + allCalls.unshift(priceUpdateTx as any); + } - //snxUSD => sUSDC - const buy_sUSDC = snxUSDAmount.gt(0) - ? SpotProxyContract.populateTransaction.buy( - USDC_BASE_MARKET, - snxUSDAmount.toBN(), - 0, - ethers.constants.AddressZero - ) - : undefined; + const walletAddress = await signer.getAddress(); + const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( + provider, + network, + allCalls, + 'useWithdrawBase', + walletAddress + ); - const synthAmount = snxUSDAmount.gt(0) - ? ( - await SpotProxyContract.callStatic.quoteBuyExactIn( - USDC_BASE_MARKET, - snxUSDAmount.toBN(), - 0 - ) - ).synthAmount - : ZEROWEI; - const unwrapAmount = sUSDC_amount.add(synthAmount); - - //sUSDC => USDC - const unwrapTxnPromised = unwrapAmount.gt(0) - ? SpotProxyContract.populateTransaction.unwrap(USDC_BASE_MARKET, unwrapAmount.toBN(), 0) - : undefined; + const gasOptionsForTransaction = formatGasPriceForTransaction({ + gasLimit, + gasPrices, + gasSpeed, + }); - const unwrapCollateralTxnPromised = - spotMarketId === STATA_BASE_MARKET && wrappedCollateralAmount.gt(0) - ? SpotProxyContract.populateTransaction.unwrap( - STATA_BASE_MARKET, - wrappedCollateralAmount.toBN(), - 0 - ) - : undefined; - - const [ - gasPrices, - withdraw_collateral_txn, - withdraw_snxUSD_txn, - snxUSDApproval_txn, - buy_sUSDC_txn, - unwrapTxnPromised_txn, - unwrapCollateralTxnPromised_txn, - ] = await Promise.all([ - gasPricesPromised, - withdraw_collateral, - withdraw_snxUSD, - snxUSDApproval, - buy_sUSDC, - unwrapTxnPromised, - unwrapCollateralTxnPromised, - ]); - - const allCalls = [ - withdraw_collateral_txn, - withdraw_snxUSD_txn, - snxUSDApproval_txn, - buy_sUSDC_txn, - unwrapTxnPromised_txn, - unwrapCollateralTxnPromised_txn, - ].filter(notNil); - - if (priceUpdateTx) { - allCalls.unshift(priceUpdateTx as any); - } - - const walletAddress = await signer.getAddress(); - const { multicallTxn: erc7412Tx, gasLimit } = await withERC7412( - provider, - network, - allCalls, - 'useWithdrawBase', - walletAddress - ); - - const gasOptionsForTransaction = formatGasPriceForTransaction({ - gasLimit, - gasPrices, - gasSpeed, - }); - - const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); - log('txn', txn); - dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); - - const receipt = await provider.waitForTransaction(txn.hash); - log('receipt', receipt); - dispatch({ type: 'success' }); - } catch (error: any) { - dispatch({ type: 'error', payload: { error } }); - throw error; - } + const txn = await signer.sendTransaction({ ...erc7412Tx, ...gasOptionsForTransaction }); + log('txn', txn); + dispatch({ type: 'pending', payload: { txnHash: txn.hash } }); + + const receipt = await provider.waitForTransaction(txn.hash); + log('receipt', receipt); + return receipt; }, - onSuccess: () => { - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + + onSuccess: async () => { + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + dispatch({ type: 'success' }); + }, + + onError: (error) => { + dispatch({ type: 'error', payload: { error } }); + throw error; }, }); diff --git a/liquidity/lib/withERC7412/withERC7412.ts b/liquidity/lib/withERC7412/withERC7412.ts index ef89404f1..8f01edfeb 100644 --- a/liquidity/lib/withERC7412/withERC7412.ts +++ b/liquidity/lib/withERC7412/withERC7412.ts @@ -34,7 +34,7 @@ async function fetchOffchainData({ function parseError( errorData: any, - AllErrors: { address: string; abi: string[] } + AllErrors: { abi: string[] } ): { name: string; args: any } | undefined { if (`${errorData}`.startsWith('0x08c379a0')) { const content = `0x${errorData.substring(10)}`; diff --git a/liquidity/ui/package.json b/liquidity/ui/package.json index d153f269e..0fc25e9a8 100644 --- a/liquidity/ui/package.json +++ b/liquidity/ui/package.json @@ -11,7 +11,6 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@pythnetwork/pyth-evm-js": "^1.42.0", "@safe-global/safe-apps-provider": "^0.18.3", "@safe-global/safe-apps-sdk": "^9.1.0", "@snx-v3/Amount": "workspace:*", @@ -51,6 +50,7 @@ "@snx-v3/useCollateralTypes": "workspace:*", "@snx-v3/useContractErrorParser": "workspace:*", "@snx-v3/useCoreProxy": "workspace:*", + "@snx-v3/useCreateAccount": "workspace:*", "@snx-v3/useDebtRepayer": "workspace:*", "@snx-v3/useEthBalance": "workspace:*", "@snx-v3/useGasOptions": "workspace:*", @@ -79,6 +79,7 @@ "@snx-v3/useStaticAaveUSDC": "workspace:*", "@snx-v3/useStaticAaveUSDCRate": "workspace:*", "@snx-v3/useSynthBalances": "workspace:*", + "@snx-v3/useSynthToken": "workspace:*", "@snx-v3/useSynthTokens": "workspace:*", "@snx-v3/useSystemToken": "workspace:*", "@snx-v3/useTokenBalance": "workspace:*", diff --git a/liquidity/ui/src/NetworkController.tsx b/liquidity/ui/src/NetworkController.tsx index ecad62b90..edc6bf11c 100644 --- a/liquidity/ui/src/NetworkController.tsx +++ b/liquidity/ui/src/NetworkController.tsx @@ -15,60 +15,63 @@ import { Text, } from '@chakra-ui/react'; import { LOCAL_STORAGE_KEYS } from '@snx-v3/constants'; -import { prettyString } from '@snx-v3/format'; +import { prettyString, renderAccountId } from '@snx-v3/format'; import { WalletIcon } from '@snx-v3/icons'; import { Tooltip } from '@snx-v3/Tooltip'; -import { useAccounts, useCreateAccount } from '@snx-v3/useAccounts'; +import { useAccounts } from '@snx-v3/useAccounts'; import { NetworkIcon, NETWORKS, useNetwork, useWallet } from '@snx-v3/useBlockchain'; +import { useCreateAccount } from '@snx-v3/useCreateAccount'; import { useLocalStorage } from '@snx-v3/useLocalStorage'; import { makeSearch, useParams } from '@snx-v3/useParams'; import { ethers } from 'ethers'; -import { useEffect, useState } from 'react'; +import React from 'react'; const mainnets = NETWORKS.filter(({ isSupported, isTestnet }) => isSupported && !isTestnet); const testnets = NETWORKS.filter(({ isSupported, isTestnet }) => isSupported && isTestnet); -export function renderAccountId(accountId?: ethers.BigNumber) { - if (!accountId) { - return '---'; - } - const hex = accountId.toHexString(); - // auto-generated 0x80000000000000000000000000000008 value - if (hex.length === 34) { - return `0x...${hex.slice(-4)}`; - } - return `#${accountId}`; -} - export function NetworkController() { const [params, setParams] = useParams(); - const [toolTipLabel, setTooltipLabel] = useState('Copy'); + const [toolTipLabel, setTooltipLabel] = React.useState('Copy'); const { activeWallet, walletsInfo, connect, disconnect } = useWallet(); const { network: activeNetwork, setNetwork } = useNetwork(); const { data: accounts, isPending: isPendingAccounts } = useAccounts(); const createAccount = useCreateAccount(); const [showTestnets, setShowTestnets] = useLocalStorage(LOCAL_STORAGE_KEYS.SHOW_TESTNETS, false); - useEffect(() => { + const paramsAccountId = React.useMemo(() => { + try { + if (params.accountId && params.accountId.length > 0) { + return ethers.BigNumber.from(params.accountId); + } + } catch { + // malformed account id in url + } + }, [params.accountId]); + + React.useEffect(() => { if (!isPendingAccounts && accounts) { - if (accounts.length > 0 && !('accountId' in params)) { - setParams({ ...params, accountId: accounts[0] }); + if (accounts.length > 0 && !params.accountId) { + setParams({ ...params, accountId: accounts[0].toString() }); return; } - if (accounts.length > 0 && !accounts.includes(`${params.accountId}`)) { - setParams({ ...params, accountId: accounts[0] }); + if ( + accounts.length > 0 && + paramsAccountId && + !accounts.some((account) => account.eq(paramsAccountId)) + ) { + setParams({ ...params, accountId: accounts[0].toString() }); return; } if (!accounts.length) { - const { accountId: _x, ...newParams } = params; + const { accountId: _, ...newParams } = params; setParams(newParams); return; } } - }, [accounts, isPendingAccounts, params, setParams]); + }, [accounts, isPendingAccounts, params, paramsAccountId, setParams]); - useEffect(() => { + React.useEffect(() => { if (window.$magicWallet) { connect({ autoSelect: { disableModals: true, label: 'MetaMask' } }); } @@ -241,7 +244,7 @@ export function NetworkController() { {accounts?.map((accountId) => ( { e.stopPropagation(); - setParams({ ...params, accountId }); + setParams({ ...params, accountId: accountId.toString() }); }} > - {renderAccountId(ethers.BigNumber.from(accountId))} - {params.accountId === accountId && ( + {renderAccountId(accountId)} + {paramsAccountId && accountId.eq(paramsAccountId) ? ( Connected - )} + ) : null} ))} diff --git a/liquidity/ui/src/components/AccountBanner/AccountBanner.tsx b/liquidity/ui/src/components/AccountBanner/AccountBanner.tsx deleted file mode 100644 index 5524a35b9..000000000 --- a/liquidity/ui/src/components/AccountBanner/AccountBanner.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Button, Flex, FlexProps, Heading } from '@chakra-ui/react'; -import { useAccounts, useCreateAccount } from '@snx-v3/useAccounts'; -import { FC } from 'react'; - -export const AccountBanner: FC = (props) => { - const { - data: accounts, - isPending: isAccountsLoading, - isFetching: isAccountsFetching, - } = useAccounts(); - const { - mutation: { mutate: createAccount, isPending: isCreateAccountLoading }, - } = useCreateAccount(); - - if (!isAccountsFetching && !isAccountsLoading && accounts && accounts.length === 0) { - return ( - - Create an account to deposit liquidity - - - ); - } - - return null; -}; diff --git a/liquidity/ui/src/components/ChangeStat/ChangeStat.tsx b/liquidity/ui/src/components/ChangeStat/ChangeStat.tsx index 220835ca6..f0576fee7 100644 --- a/liquidity/ui/src/components/ChangeStat/ChangeStat.tsx +++ b/liquidity/ui/src/components/ChangeStat/ChangeStat.tsx @@ -49,9 +49,10 @@ export function ChangeStat({ fontWeight={styles[size].fontWeight} lineHeight={styles[size].lineHeight} flexWrap="wrap" + data-cy={dataCy} > void; onSubmit: () => void }) { const [params] = useParams(); @@ -44,13 +43,6 @@ function ClosePositionUi({ onSubmit, onClose }: { onClose: () => void; onSubmit: const { network } = useNetwork(); const debtSymbol = network?.preset === 'andromeda' ? 'USDC' : systemToken?.symbol; - const provider = useProvider(); - const { data: positionDebt, isPending: isPendingPositionDebt } = usePositionDebt({ - provider, - accountId: params.accountId, - poolId: params.poolId, - collateralTypeTokenAddress: collateralType?.tokenAddress, - }); const { data: systemTokenBalance, isPending: isPendingSystemTokenBalance } = useTokenBalance( systemToken?.address ); @@ -179,9 +171,10 @@ function ClosePositionUi({ onSubmit, onClose }: { onClose: () => void; onSubmit: // Deployments that do not have ClosePosition contract available should skip this check ClosePositionDeployment && systemTokenBalance && - positionDebt && liquidityPosition && - !systemTokenBalance.add(liquidityPosition.availableSystemToken).gte(positionDebt) + !systemTokenBalance + .add(liquidityPosition.availableSystemToken) + .gte(liquidityPosition.debt) } animateOpacity > @@ -212,13 +205,13 @@ function ClosePositionUi({ onSubmit, onClose }: { onClose: () => void; onSubmit: // Deployments that do not have ClosePosition contract available should skip this check ClosePositionDeployment && !( - !isPendingPositionDebt && !isPendingSystemTokenBalance && - !isPendingLiquidityPosition && systemTokenBalance && + !isPendingLiquidityPosition && liquidityPosition && - positionDebt && - systemTokenBalance.add(liquidityPosition.availableSystemToken).gte(positionDebt) + systemTokenBalance + .add(liquidityPosition.availableSystemToken) + .gte(liquidityPosition.debt) ) } > diff --git a/liquidity/ui/src/components/ClosePosition/ClosePositionOneStep.tsx b/liquidity/ui/src/components/ClosePosition/ClosePositionOneStep.tsx index 7097f6a6e..4123d773f 100644 --- a/liquidity/ui/src/components/ClosePosition/ClosePositionOneStep.tsx +++ b/liquidity/ui/src/components/ClosePosition/ClosePositionOneStep.tsx @@ -15,23 +15,18 @@ import { useCoreProxy } from '@snx-v3/useCoreProxy'; import { formatGasPriceForTransaction } from '@snx-v3/useGasOptions'; import { getGasPrice } from '@snx-v3/useGasPrice'; import { useGasSpeed } from '@snx-v3/useGasSpeed'; +import { useLiquidityPosition } from '@snx-v3/useLiquidityPosition'; import { useMulticall3 } from '@snx-v3/useMulticall3'; import { type PositionPageSchemaType, useParams } from '@snx-v3/useParams'; import { usePythFeeds } from '@snx-v3/usePythFeeds'; import { usePythVerifier } from '@snx-v3/usePythVerifier'; import { useSystemToken } from '@snx-v3/useSystemToken'; import { withERC7412 } from '@snx-v3/withERC7412'; -import { wei } from '@synthetixio/wei'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import debug from 'debug'; import { ethers } from 'ethers'; import React from 'react'; import { LiquidityPositionUpdated } from '../Manage/LiquidityPositionUpdated'; -import { fetchPositionDebt } from './fetchPositionDebt'; -import { fetchPositionDebtWithPriceUpdate } from './fetchPositionDebtWithPriceUpdate'; -import { fetchPriceUpdateTxn } from './fetchPriceUpdateTxn'; -import { useAccountCollateral } from './useAccountCollateral'; -import { usePositionDebt } from './usePositionDebt'; const log = debug('snx:ClosePositionOneStep'); @@ -45,6 +40,10 @@ export function ClosePositionOneStep({ const [params] = useParams(); const { data: collateralType } = useCollateralType(params.collateralSymbol); + const { data: liquidityPosition, refetch: refetchLiquidityPosition } = useLiquidityPosition({ + accountId: params.accountId, + collateralType, + }); const { setCollateralChange, setDebtChange } = React.useContext(ManagePositionContext); const toast = useToast({ isClosable: true, duration: 9000 }); @@ -62,32 +61,20 @@ export function ClosePositionOneStep({ const { data: pythFeeds } = usePythFeeds(); const { data: systemToken } = useSystemToken(); - const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); + const { data: priceUpdateTx } = useCollateralPriceUpdates(); const { gasSpeed } = useGasSpeed(); const { network } = useNetwork(); const signer = useSigner(); const provider = useProvider(); - const { data: positionDebt } = usePositionDebt({ - provider, - accountId: params.accountId, - poolId: params.poolId, - collateralTypeTokenAddress: collateralType?.tokenAddress, - }); - - const { data: accountCollateral } = useAccountCollateral({ - provider, - accountId: params.accountId, - collateralTypeTokenAddress: collateralType?.tokenAddress, - }); + // const queryClient = useQueryClient(); + // queryClient.invalidateQueries(); const { mutate: execClosePosition } = useMutation({ mutationFn: async function () { log('params', params); log('collateralType', collateralType); - log('accountCollateral', accountCollateral); - log('positionDebt', positionDebt); setTxState({ step: 1, status: 'pending' }); if ( @@ -104,8 +91,7 @@ export function ClosePositionOneStep({ params.poolId && params.accountId && systemToken?.address && - collateralType?.tokenAddress && - positionDebt + collateralType?.tokenAddress ) ) { throw new Error('Not ready'); @@ -127,37 +113,19 @@ export function ClosePositionOneStep({ signer ); - const freshPriceUpdateTxn = await fetchPriceUpdateTxn({ PythVerfier, pythFeeds }); - log('freshPriceUpdateTxn', freshPriceUpdateTxn); - - const freshPositionDebt = freshPriceUpdateTxn.value - ? await fetchPositionDebtWithPriceUpdate({ - provider, - CoreProxy, - Multicall3, - accountId: params.accountId, - poolId: params.poolId, - collateralTypeTokenAddress: collateralType.tokenAddress, - priceUpdateTxn: freshPriceUpdateTxn, - }) - : await fetchPositionDebt({ - provider, - CoreProxy, - accountId: params.accountId, - poolId: params.poolId, - collateralTypeTokenAddress: collateralType.tokenAddress, - }); - log('freshPositionDebt', freshPositionDebt); - - const adjustedAllowance = freshPositionDebt.lt(1) + const { data: freshLiquidityPosition } = await refetchLiquidityPosition({ + throwOnError: true, + }); + if (!freshLiquidityPosition) { + throw new Error('Could not fetch fresh liquidity position'); + } + const adjustedAllowance = freshLiquidityPosition.debt.lt(1) ? // For the case when debt fluctuates from negative/zero to slightly positive - ethers.utils.parseEther('1.00') + ethers.utils.parseEther('1') : // Add extra buffer for debt fluctuations - freshPositionDebt.mul(120).div(100); + freshLiquidityPosition.debt.mul(120).div(100).toBN(); log('adjustedAllowance', adjustedAllowance); - // "function approve(address to, uint256 tokenId)", - const approveAccountTx = AccountProxyContract.populateTransaction.approve( ClosePosition.address, params.accountId @@ -202,51 +170,30 @@ export function ClosePositionOneStep({ }, onSuccess: async () => { - setTxState({ step: 1, status: 'success' }); + const deployment = `${network?.id}-${network?.preset}`; + await Promise.all( + [ + // + 'PriceUpdates', + 'LiquidityPosition', + 'LiquidityPositions', + 'TokenBalance', + 'SynthBalances', + 'EthBalance', + 'Allowance', + 'TransferableSynthetix', + 'AccountCollateralUnlockDate', + ].map((key) => queryClient.invalidateQueries({ queryKey: [deployment, key] })) + ); + setCollateralChange(ZEROWEI); setDebtChange(ZEROWEI); - - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPosition'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'LiquidityPositions'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'Allowance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'AccountSpecificCollateral'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'TokenBalance'], - }); - queryClient.invalidateQueries({ - queryKey: [`${network?.id}-${network?.preset}`, 'AccountCollateralUnlockDate'], - }); - - queryClient.invalidateQueries({ - queryKey: [network?.id, network?.preset, 'PriceUpdateTxn'], - }); - queryClient.invalidateQueries({ - queryKey: [network?.id, network?.preset, 'PositionDebt'], - }); - queryClient.invalidateQueries({ - queryKey: [network?.id, network?.preset, 'AccountCollateral'], - }); - queryClient.invalidateQueries({ - queryKey: [network?.id, network?.preset, 'AccountAvailableCollateral'], - }); - - // After mutation withERC7412, we guaranteed to have updated all the prices, dont care about await - refetchPriceUpdateTx(); + setTxState({ step: 1, status: 'success' }); }, onError: (error) => { setTxState({ step: 1, status: 'error' }); - const contractError = errorParser(error); - if (contractError) { console.error(new Error(contractError.name), contractError); } @@ -301,29 +248,31 @@ export function ClosePositionOneStep({ subtitle={ <> Approve close position on behalf - {positionDebt && positionDebt.gt(0) ? ( + {liquidityPosition && liquidityPosition.debt && liquidityPosition.debt.gt(0) ? ( ) : null} - {positionDebt && positionDebt.lt(0) ? ( + {liquidityPosition && liquidityPosition.debt && liquidityPosition.debt.lt(0) ? ( ) : null} - + {liquidityPosition ? ( + + ) : null} } status={{ @@ -335,7 +284,7 @@ export function ClosePositionOneStep({ - )} + ) : null} {txnHash && ( (); @@ -46,9 +46,9 @@ export function Rewards() { const map = new Map(); rewards.forEach(({ distributor, claimableAmount }) => { const synthToken = synthTokens?.find( - (synth) => synth.address.toUpperCase() === distributor.payoutToken.address.toUpperCase() + (synth) => synth.address.toLowerCase() === distributor.payoutToken.address.toLowerCase() ); - const token = synthToken ? synthToken.token : distributor.payoutToken; + const token = synthToken && synthToken.token ? synthToken.token : distributor.payoutToken; const displaySymbol = tokenOverrides[token.address] ?? token.symbol; if (map.has(displaySymbol)) { map.set(displaySymbol, map.get(displaySymbol).add(claimableAmount)); @@ -56,9 +56,7 @@ export function Rewards() { map.set(displaySymbol, claimableAmount); } }); - return map - .entries() - .toArray() + return Array.from(map.entries()) .map(([displaySymbol, claimableAmount]) => ({ displaySymbol, claimableAmount, @@ -78,7 +76,7 @@ export function Rewards() {