diff --git a/.circleci/config.yml b/.circleci/config.yml index 78713ed4..0228ba64 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -142,6 +142,7 @@ jobs: docker: - image: cimg/node:<< pipeline.parameters.node-version >> resource_class: xlarge + parallelism: 3 steps: - checkout - install-foundry @@ -171,7 +172,7 @@ jobs: echo "0" > /tmp/cypress_exit_code export NODE_ENV=test export CYPRESS_INFURA_KEY=$INFURA_KEY - yarn cypress run --e2e || echo $? > /tmp/cypress_exit_code + circleci tests glob 'cypress/e2e/**/*.e2e.js' | circleci tests run --verbose --split-by=timings --command="xargs yarn cypress run --e2e --spec" || echo $? > /tmp/cypress_exit_code - save_cache: key: *anvil-fork-cache diff --git a/README.md b/README.md index 745c498a..8b84cbca 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ and all transactions will be automatically signed, without any popups ```sh # Mainnets anvil --auto-impersonate --chain-id 1 --fork-url wss://mainnet.infura.io/ws/v3/$INFURA_KEY --no-rate-limit --steps-tracing --fork-block-number 21233424 - anvil --auto-impersonate --chain-id 8453 --fork-url wss://base-mainnet.infura.io/ws/v3/$INFURA_KEY --no-rate-limit --steps-tracing --fork-block-number 22683522 + anvil --auto-impersonate --chain-id 8453 --fork-url wss://base-mainnet.infura.io/ws/v3/$INFURA_KEY --no-rate-limit --steps-tracing --fork-block-number 22946353 anvil --auto-impersonate --chain-id 42161 --fork-url wss://arbitrum-mainnet.infura.io/ws/v3/$INFURA_KEY --no-rate-limit --steps-tracing --fork-block-number 271813668 # Testnets diff --git a/liquidity/components/UndelegateModal/UndelegateModal.tsx b/liquidity/components/UndelegateModal/UndelegateModal.tsx index 75c86cdd..021c6fd4 100644 --- a/liquidity/components/UndelegateModal/UndelegateModal.tsx +++ b/liquidity/components/UndelegateModal/UndelegateModal.tsx @@ -3,17 +3,20 @@ import { Button, Divider, Link, Text, useToast } from '@chakra-ui/react'; import { Amount } from '@snx-v3/Amount'; import { ZEROWEI } from '@snx-v3/constants'; import { ContractError } from '@snx-v3/ContractError'; -import { currency } from '@snx-v3/format'; +import { currency, parseUnits } from '@snx-v3/format'; import { isBaseAndromeda } from '@snx-v3/isBaseAndromeda'; 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 { CollateralType, useCollateralType } from '@snx-v3/useCollateralTypes'; import { useContractErrorParser } from '@snx-v3/useContractErrorParser'; +import { useDebtRepayer } from '@snx-v3/useDebtRepayer'; import { LiquidityPosition } from '@snx-v3/useLiquidityPosition'; import { useParams } from '@snx-v3/useParams'; 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'; @@ -143,6 +146,19 @@ export const UndelegateModal: UndelegateModalProps = ({ onClose, isOpen, liquidi collateralChange, currentCollateral: currentCollateral, }); + + const debtExists = liquidityPosition?.debt.gt(0); + const currentDebt = debtExists && liquidityPosition ? liquidityPosition.debt : wei(0); + const { data: USDC } = useUSDC(); + const { data: DebtRepayer } = useDebtRepayer(); + + const { approve, requireApproval } = useApprove({ + contractAddress: USDC?.address, + //slippage for approval + amount: debtExists ? parseUnits(currentDebt.toString(), 6).mul(120).div(100) : 0, + spender: DebtRepayer?.address, + }); + const { exec: undelegateBaseAndromeda } = useUndelegateBaseAndromeda({ accountId: params.accountId, poolId: params.poolId, @@ -169,6 +185,9 @@ export const UndelegateModal: UndelegateModalProps = ({ onClose, isOpen, liquidi }); if (isBase) { + if (requireApproval) { + await approve(false); + } await undelegateBaseAndromeda(); } else { await execUndelegate(); diff --git a/liquidity/components/UndelegateModal/package.json b/liquidity/components/UndelegateModal/package.json index d16b8745..3b241ee1 100644 --- a/liquidity/components/UndelegateModal/package.json +++ b/liquidity/components/UndelegateModal/package.json @@ -13,11 +13,14 @@ "@snx-v3/constants": "workspace:*", "@snx-v3/format": "workspace:*", "@snx-v3/isBaseAndromeda": "workspace:*", + "@snx-v3/useApprove": "workspace:*", "@snx-v3/useBlockchain": "workspace:*", "@snx-v3/useCollateralTypes": "workspace:*", "@snx-v3/useContractErrorParser": "workspace:*", + "@snx-v3/useDebtRepayer": "workspace:*", "@snx-v3/useLiquidityPosition": "workspace:*", "@snx-v3/useParams": "workspace:*", + "@snx-v3/useUSDC": "workspace:*", "@snx-v3/useUndelegate": "workspace:*", "@snx-v3/useUndelegateBaseAndromeda": "workspace:*", "@synthetixio/wei": "^2.74.4", diff --git a/liquidity/components/WithdrawModal/WithdrawModal.tsx b/liquidity/components/WithdrawModal/WithdrawModal.tsx index d1042042..eeec7d23 100644 --- a/liquidity/components/WithdrawModal/WithdrawModal.tsx +++ b/liquidity/components/WithdrawModal/WithdrawModal.tsx @@ -71,12 +71,7 @@ export const WithdrawModalUi: FC<{ - -  {symbol} will be withdrawn - - } + subtitle={} status={{ failed: state.step === 1 && state.status === 'error', success: state.step > 1, diff --git a/liquidity/cypress/cypress.d.ts b/liquidity/cypress/cypress.d.ts index df03bece..86e927cc 100644 --- a/liquidity/cypress/cypress.d.ts +++ b/liquidity/cypress/cypress.d.ts @@ -33,6 +33,18 @@ declare global { poolId: number; }) => Promise; + clearDebt: ({ + address, + accountId, + symbol, + poolId, + }: { + address?: string; + accountId?: string; + symbol: string; + poolId: number; + }) => Promise; + delegateCollateral: ({ address, accountId, diff --git a/liquidity/cypress/cypress/e2e/1-main/Create_Account.e2e.js b/liquidity/cypress/cypress/e2e/1-main/Create_Account.e2e.js index fd5e589b..8928fee7 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Create_Account.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/Create_Account.e2e.js @@ -1,4 +1,4 @@ -describe('Create Account', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Create Account', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/1-main/Dashboard_Connected.e2e.js b/liquidity/cypress/cypress/e2e/1-main/Dashboard_Connected.e2e.js index bb3994f5..93db7a03 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Dashboard_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/Dashboard_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Dashboard - Connected', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/1-main/Dashboard_Not_Connected.e2e.js b/liquidity/cypress/cypress/e2e/1-main/Dashboard_Not_Connected.e2e.js index 1dd55b3e..1fabc543 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Dashboard_Not_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/Dashboard_Not_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Not Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -13,7 +13,7 @@ describe('Dashboard - Not Connected', () => { }); }); - it('works', () => { + it(__filename, () => { cy.visit('/#/dashboard'); cy.contains('h2', 'Dashboard').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Borrow.e2e.js b/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js similarity index 94% rename from liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Borrow.e2e.js rename to liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js index f9ba6393..63db33e6 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Borrow.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/SNX_Borrow.e2e.js @@ -1,4 +1,4 @@ -describe('Manage SNX Position - Borrow', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Manage SNX Position - Borrow', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.approveCollateral({ symbol: 'SNX', spender: 'CoreProxy' }); cy.getSNX({ amount: 2000 }); @@ -30,7 +30,7 @@ describe('Manage SNX Position - Borrow', () => { cy.visit(`/#/positions/SNX/1?manageAction=claim&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="claim form"]').should('exist'); + 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'); diff --git a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Deposit.e2e.js b/liquidity/cypress/cypress/e2e/1-main/SNX_Deposit.e2e.js similarity index 92% rename from liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Deposit.e2e.js rename to liquidity/cypress/cypress/e2e/1-main/SNX_Deposit.e2e.js index bba4a607..2945608f 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Deposit.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/SNX_Deposit.e2e.js @@ -1,4 +1,4 @@ -describe('Manage SNX Position - Deposit', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,13 +21,13 @@ describe('Manage SNX Position - Deposit', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getSNX({ amount: 500 }); cy.visit(`/#/positions/SNX/1?manageAction=deposit&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="deposit and lock collateral form"]').should('exist'); + cy.get('[data-cy="deposit and lock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="balance amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="deposit amount input"]').type('101'); diff --git a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Unlock.e2e.js b/liquidity/cypress/cypress/e2e/1-main/SNX_Unlock.e2e.js similarity index 93% rename from liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Unlock.e2e.js rename to liquidity/cypress/cypress/e2e/1-main/SNX_Unlock.e2e.js index 8ecc46be..d6923035 100644 --- a/liquidity/cypress/cypress/e2e/1-main/Manage_SNX_Position_Unlock.e2e.js +++ b/liquidity/cypress/cypress/e2e/1-main/SNX_Unlock.e2e.js @@ -1,4 +1,4 @@ -describe('Manage SNX Position - Unlock', () => { +describe(__filename, () => { Cypress.env('chainId', '1'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Manage SNX Position - Unlock', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getSNX({ amount: 2000 }); cy.approveCollateral({ symbol: 'SNX', spender: 'CoreProxy' }); @@ -30,7 +30,7 @@ describe('Manage SNX Position - Unlock', () => { cy.visit(`/#/positions/SNX/1?manageAction=undelegate&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="unlock collateral form"]').should('exist'); + cy.get('[data-cy="unlock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="locked amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="undelegate amount input"]').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Create_Account.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/Create_Account.e2e.js index 05b53e70..5e33d002 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Create_Account.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/Create_Account.e2e.js @@ -1,4 +1,4 @@ -describe('Create Account', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Create Account', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Connected.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Connected.e2e.js index 72718e32..ce684b61 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Dashboard - Connected', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Not_Connected.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Not_Connected.e2e.js index 5e3078de..fface664 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Not_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/Dashboard_Not_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Not Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -13,7 +13,7 @@ describe('Dashboard - Not Connected', () => { }); }); - it('works', () => { + it(__filename, () => { cy.visit('/#/dashboard'); cy.contains('h2', 'Dashboard').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Borrow.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js similarity index 94% rename from liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Borrow.e2e.js rename to liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js index 50a8a6c1..87b406dd 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Borrow.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/WETH_Borrow.e2e.js @@ -1,4 +1,4 @@ -describe('Manage WETH Position - Borrow', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -23,7 +23,7 @@ describe('Manage WETH Position - Borrow', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.approveCollateral({ symbol: 'WETH', spender: 'CoreProxy' }); cy.wrapEth({ amount: 20 }); @@ -32,7 +32,7 @@ describe('Manage WETH Position - Borrow', () => { cy.visit(`/#/positions/WETH/1?manageAction=claim&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="claim form"]').should('exist'); + 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"]').type('100'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Deposit.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/WETH_Deposit.e2e.js similarity index 93% rename from liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Deposit.e2e.js rename to liquidity/cypress/cypress/e2e/42161-main/WETH_Deposit.e2e.js index d69cb351..99c01149 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Deposit.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/WETH_Deposit.e2e.js @@ -1,4 +1,4 @@ -describe('Manage WETH Position - Deposit', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Manage WETH Position - Deposit', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); // Make initial delegation @@ -32,7 +32,7 @@ describe('Manage WETH Position - Deposit', () => { cy.visit(`/#/positions/WETH/1?manageAction=deposit&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="deposit and lock collateral form"]').should('exist'); + cy.get('[data-cy="deposit and lock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="balance amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="deposit amount input"]').type('1'); diff --git a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Repay.e2e.js b/liquidity/cypress/cypress/e2e/42161-main/WETH_Repay.e2e.js similarity index 93% rename from liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Repay.e2e.js rename to liquidity/cypress/cypress/e2e/42161-main/WETH_Repay.e2e.js index 50738592..7562508b 100644 --- a/liquidity/cypress/cypress/e2e/42161-main/Manage_WETH_Position_Repay.e2e.js +++ b/liquidity/cypress/cypress/e2e/42161-main/WETH_Repay.e2e.js @@ -1,4 +1,4 @@ -describe('Manage WETH Position - Repay', () => { +describe(__filename, () => { Cypress.env('chainId', '42161'); Cypress.env('preset', 'main'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -21,7 +21,7 @@ describe('Manage WETH Position - Repay', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.approveCollateral({ symbol: 'WETH', spender: 'CoreProxy' }); cy.wrapEth({ amount: 20 }); @@ -31,7 +31,7 @@ describe('Manage WETH Position - Repay', () => { cy.visit(`/#/positions/WETH/1?manageAction=repay&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="repay debt form"]').should('exist'); + cy.get('[data-cy="repay debt form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="current debt amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="repay amount input"]').type('5'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Create_Account.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/Create_Account.e2e.js index 5fcea2c3..efc02f21 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Create_Account.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/Create_Account.e2e.js @@ -1,4 +1,4 @@ -describe('Create Account', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Create Account', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,7 +21,7 @@ describe('Create Account', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Connected.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Connected.e2e.js index a52ea6bb..2126c47c 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Dashboard - Connected', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,7 +21,7 @@ describe('Dashboard - Connected', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.visit('/#/dashboard'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Not_Connected.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Not_Connected.e2e.js index 4817d812..7d38102d 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Not_Connected.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/Dashboard_Not_Connected.e2e.js @@ -1,4 +1,4 @@ -describe('Dashboard - Not Connected', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -13,7 +13,7 @@ describe('Dashboard - Not Connected', () => { }); }); - it('works', () => { + it(__filename, () => { cy.visit('/#/dashboard'); cy.contains('h2', 'Dashboard').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Deposit.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Deposit.e2e.js similarity index 91% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Deposit.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Deposit.e2e.js index d5baf42f..15d1b595 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Deposit.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Deposit.e2e.js @@ -1,4 +1,4 @@ -describe('Manage USDC Position - Deposit', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Manage USDC Position - Deposit', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,13 +21,13 @@ describe('Manage USDC Position - Deposit', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 500 }); cy.visit(`/#/positions/USDC/1?manageAction=deposit&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="deposit and lock collateral form"]').should('exist'); + cy.get('[data-cy="deposit and lock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="balance amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="deposit amount input"]').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Unlock.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_negative_debt.e2e.js similarity index 91% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Unlock.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_negative_debt.e2e.js index 3c5492a8..111cefbb 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_USDC_Position_Unlock.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_negative_debt.e2e.js @@ -1,4 +1,4 @@ -describe('should be able to unlock USDC collateral after depositing', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('should be able to unlock USDC collateral after depositing', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22683522', // negative debt }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,7 +21,7 @@ describe('should be able to unlock USDC collateral after depositing', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); cy.approveCollateral({ symbol: 'USDC', spender: 'SpotMarketProxy' }); @@ -32,7 +32,7 @@ describe('should be able to unlock USDC collateral after depositing', () => { cy.visit(`/#/positions/USDC/1?manageAction=undelegate&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="unlock collateral form"]').should('exist'); + cy.get('[data-cy="unlock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="locked amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="undelegate amount input"]').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_positive_debt.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_positive_debt.e2e.js new file mode 100644 index 00000000..c77102aa --- /dev/null +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/USDC_Unlock_positive_debt.e2e.js @@ -0,0 +1,49 @@ +describe(__filename, () => { + Cypress.env('chainId', '8453'); + Cypress.env('preset', 'andromeda'); + Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); + Cypress.env('accountId', '522433293696'); + + beforeEach(() => { + cy.task('startAnvil', { + chainId: Cypress.env('chainId'), + forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, + block: '22946353', // positive debt + }).then(() => cy.log('Anvil started')); + + cy.on('window:before:load', (win) => { + win.localStorage.setItem('MAGIC_WALLET', Cypress.env('walletAddress')); + win.localStorage.setItem( + 'DEFAULT_NETWORK', + `${Cypress.env('chainId')}-${Cypress.env('preset')}` + ); + }); + }); + afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); + + it(__filename, () => { + cy.setEthBalance({ balance: 100 }); + cy.getUSDC({ amount: 1000 }); + + cy.visit(`/#/positions/USDC/1?manageAction=undelegate&accountId=${Cypress.env('accountId')}`); + + cy.get('[data-cy="unlock collateral form"]', { timeout: 180_000 }).should('exist'); + cy.get('[data-cy="locked amount"]').should('exist').and('include.text', 'Max'); + + cy.get('[data-cy="undelegate amount input"]').should('exist'); + cy.get('[data-cy="undelegate amount input"]').type('30'); + cy.get('[data-cy="undelegate submit"]').should('be.enabled'); + cy.get('[data-cy="undelegate submit"]').click(); + + cy.get('[data-cy="undelegate multistep"]') + .should('exist') + .and('include.text', '30 USDC will be unlocked from the pool.'); + + cy.get('[data-cy="undelegate confirm button"]').should('include.text', 'Execute Transaction'); + cy.get('[data-cy="undelegate confirm button"]').click(); + + cy.contains('[data-status="success"]', 'Your locked collateral amount has been updated.', { + timeout: 180_000, + }).should('exist'); + }); +}); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Close.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Close.e2e.js similarity index 88% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Close.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Close.e2e.js index 42b14647..7b816fa2 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Close.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Close.e2e.js @@ -1,4 +1,4 @@ -describe('Manage Static AaveUSDC Position - Close Position', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Manage Static AaveUSDC Position - Close Position', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,13 +21,13 @@ describe('Manage Static AaveUSDC Position - Close Position', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); cy.visit(`/#/positions/stataUSDC/1?manageAction=deposit&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="close position"]').should('exist').click(); + cy.get('[data-cy="close position"]', { timeout: 180_000 }).should('exist').click(); cy.get('[data-cy="close position multistep"]') .should('exist') diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Deposit.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Deposit.e2e.js similarity index 91% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Deposit.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Deposit.e2e.js index ce5188a8..d1ba6127 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Deposit.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Deposit.e2e.js @@ -1,4 +1,4 @@ -describe('Manage Static AaveUSDC Position - Deposit', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Manage Static AaveUSDC Position - Deposit', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,13 +21,13 @@ describe('Manage Static AaveUSDC Position - Deposit', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); cy.visit(`/#/positions/stataUSDC/1?manageAction=deposit&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="deposit and lock collateral form"]').should('exist'); + cy.get('[data-cy="deposit and lock collateral form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="balance amount"]').should('exist').and('include.text', 'Max'); cy.get('[data-cy="deposit amount input"]').should('exist'); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Repay.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Repay.e2e.js similarity index 87% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Repay.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Repay.e2e.js index 2423003d..12ec8dca 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Repay.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Repay.e2e.js @@ -1,4 +1,4 @@ -describe('Manage Static AaveUSDC Position - Repay', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Manage Static AaveUSDC Position - Repay', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,13 +21,13 @@ describe('Manage Static AaveUSDC Position - Repay', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); cy.visit(`/#/positions/stataUSDC/1?manageAction=repay&accountId=${Cypress.env('accountId')}`); - cy.get('[data-cy="repay debt form"]').should('exist'); + cy.get('[data-cy="repay debt form"]', { timeout: 180_000 }).should('exist'); cy.get('[data-cy="repay debt submit"]').should('exist'); cy.get('[data-cy="repay debt submit"]').click(); diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Unlock.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Unlock.e2e.js similarity index 86% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Unlock.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Unlock.e2e.js index 9357c6b0..8d6b6c30 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Unlock.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Unlock.e2e.js @@ -1,4 +1,4 @@ -describe('Manage Static AaveUSDC Position - Unlock', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'); @@ -8,7 +8,7 @@ describe('Manage Static AaveUSDC Position - Unlock', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22683522', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,7 +21,7 @@ describe('Manage Static AaveUSDC Position - Unlock', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); @@ -44,8 +44,8 @@ describe('Manage Static AaveUSDC Position - Unlock', () => { cy.get('[data-cy="undelegate confirm button"]').should('include.text', 'Execute Transaction'); cy.get('[data-cy="undelegate confirm button"]').click(); - cy.contains('[data-status="error"]', 'Unlock collateral failed').should('exist'); - cy.contains('[data-status="error"]', 'MinDelegationTimeoutPending').should('exist'); + // cy.contains('[data-status="error"]', 'Unlock collateral failed').should('exist'); + // cy.contains('[data-status="error"]', 'MinDelegationTimeoutPending').should('exist'); cy.contains('[data-status="success"]', 'Your locked collateral amount has been updated.', { timeout: 180_000, diff --git a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Withdraw.e2e.js b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Withdraw.e2e.js similarity index 94% rename from liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Withdraw.e2e.js rename to liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Withdraw.e2e.js index 60d971c0..abef5ab0 100644 --- a/liquidity/cypress/cypress/e2e/8453-andromeda/Manage_Static_AaveUSDC_Position_Withdraw.e2e.js +++ b/liquidity/cypress/cypress/e2e/8453-andromeda/stataUSDC_Withdraw.e2e.js @@ -1,4 +1,4 @@ -describe('Manage Static AaveUSDC Position - Withdraw', () => { +describe(__filename, () => { Cypress.env('chainId', '8453'); Cypress.env('preset', 'andromeda'); Cypress.env('walletAddress', '0xc77b0cd1B1E73F6De8f606685Fb09Ace95f614c3'); @@ -8,7 +8,7 @@ describe('Manage Static AaveUSDC Position - Withdraw', () => { cy.task('startAnvil', { chainId: Cypress.env('chainId'), forkUrl: `wss://base-mainnet.infura.io/ws/v3/${Cypress.env('INFURA_KEY')}`, - block: '22890156', + block: '22946353', }).then(() => cy.log('Anvil started')); cy.on('window:before:load', (win) => { @@ -21,7 +21,7 @@ describe('Manage Static AaveUSDC Position - Withdraw', () => { }); afterEach(() => cy.task('stopAnvil').then(() => cy.log('Anvil stopped'))); - it('works', () => { + it(__filename, () => { cy.setEthBalance({ balance: 100 }); cy.getUSDC({ amount: 1000 }); diff --git a/liquidity/cypress/cypress/support/commands/clearDebt.js b/liquidity/cypress/cypress/support/commands/clearDebt.js new file mode 100644 index 00000000..20f87ade --- /dev/null +++ b/liquidity/cypress/cypress/support/commands/clearDebt.js @@ -0,0 +1,67 @@ +import { + importDebtRepayer, + importCoreProxy, + importSpotMarketProxy, + importAccountProxy, + importAllErrors, +} from '@snx-v3/contracts'; +import { parseContractError } from '@snx-v3/parseContractError'; +import { ethers } from 'ethers'; +import { getCollateralConfig } from './getCollateralConfig'; + +export async function clearDebt({ + address = Cypress.env('walletAddress'), + accountId = Cypress.env('accountId'), + symbol, + poolId, +}) { + console.log('clearDebt', { address, accountId, symbol, poolId }); + + const config = await getCollateralConfig({ symbol }); + const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545'); + const signer = provider.getSigner(address); + + const DebtRepayer = await importDebtRepayer(Cypress.env('chainId'), Cypress.env('preset')); + const DebtRepayerContract = new ethers.Contract(DebtRepayer.address, DebtRepayer.abi, signer); + + const AccountProxy = await importAccountProxy(Cypress.env('chainId'), Cypress.env('preset')); + const AccountProxyContract = new ethers.Contract(AccountProxy.address, AccountProxy.abi, signer); + + const CoreProxy = await importCoreProxy(Cypress.env('chainId'), Cypress.env('preset')); + const SpotMarketProxy = await importSpotMarketProxy( + Cypress.env('chainId'), + Cypress.env('preset') + ); + + const USDC = await getCollateralConfig({ symbol: 'USDC' }); + const TokenContract = new ethers.Contract( + USDC.tokenAddress, + ['function approve(address spender, uint256 amount) returns (bool)'], + signer + ); + + await (await TokenContract.approve(DebtRepayer.address, ethers.constants.MaxUint256)).wait(); + await (await AccountProxyContract.approve(DebtRepayer.address, accountId)).wait(); + + const args = [ + // + CoreProxy.address, + SpotMarketProxy.address, + AccountProxy.address, + ethers.BigNumber.from(accountId), + ethers.BigNumber.from(poolId), + config.tokenAddress, + '1', // USDC_BASE_MARKET + ]; + + const gasLimit = await DebtRepayerContract.estimateGas + .depositDebtToRepay(...args) + .catch(async (error) => { + const AllErrors = await importAllErrors(Cypress.env('chainId'), Cypress.env('preset')); + 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)) }); +} diff --git a/liquidity/cypress/cypress/support/e2e.js b/liquidity/cypress/cypress/support/e2e.js index 8bbbb6c9..4dfd5683 100644 --- a/liquidity/cypress/cypress/support/e2e.js +++ b/liquidity/cypress/cypress/support/e2e.js @@ -3,6 +3,7 @@ import { default as installLogsCollector } from 'cypress-terminal-report/src/ins import { approveCollateral } from './commands/approveCollateral'; import { borrowUsd } from './commands/borrowUsd'; +import { clearDebt } from './commands/clearDebt'; import { delegateCollateral } from './commands/delegateCollateral'; import { depositCollateral } from './commands/depositCollateral'; import { getSNX } from './commands/getSNX'; @@ -13,6 +14,7 @@ import { wrapEth } from './commands/wrapEth'; Cypress.Commands.add('approveCollateral', approveCollateral); Cypress.Commands.add('borrowUsd', borrowUsd); +Cypress.Commands.add('clearDebt', clearDebt); Cypress.Commands.add('delegateCollateral', delegateCollateral); Cypress.Commands.add('depositCollateral', depositCollateral); Cypress.Commands.add('getSNX', getSNX); diff --git a/liquidity/cypress/cypress/tasks/anvil.js b/liquidity/cypress/cypress/tasks/anvil.js index 5c7abf10..ec478cea 100644 --- a/liquidity/cypress/cypress/tasks/anvil.js +++ b/liquidity/cypress/cypress/tasks/anvil.js @@ -15,10 +15,12 @@ export async function startAnvil({ chainId, forkUrl, block }) { chainId, '--fork-url', forkUrl, - '--no-rate-limit', - '--steps-tracing', + // '--no-rate-limit', + // '--steps-tracing', '--fork-block-number', block, + '--memory-limit', + '12884901888', // 12G ]; console.log(`Starting anvil:`, cmd, args.join(' ')); return new Promise(async (resolve, reject) => { diff --git a/liquidity/lib/contracts/importers/importDebtRepayer.js b/liquidity/lib/contracts/importers/importDebtRepayer.js index e7b890f2..586f8741 100644 --- a/liquidity/lib/contracts/importers/importDebtRepayer.js +++ b/liquidity/lib/contracts/importers/importDebtRepayer.js @@ -1,17 +1,17 @@ const abi = [ - 'function depositDebtToRepay(address synthetixCore, address spotMarket, uint128 accountId, uint128 poolId, address collateralType, uint128 spotMarketId)', + 'function depositDebtToRepay(address synthetixCore, address spotMarket, address accountProxy, uint128 accountId, uint128 poolId, address collateralType, uint128 spotMarketId)', ]; export async function importDebtRepayer(chainId, preset) { const deployment = `${Number(chainId).toFixed(0)}-${preset}`; switch (deployment) { case '8453-andromeda': { - // https://basescan.org/address/0xBD8004ea5c73E33d405d35d594221Efc733F7E37#code - return { address: '0xBD8004ea5c73E33d405d35d594221Efc733F7E37', abi }; + // https://basescan.org/address/0x220bd4ba855a954a57d5eac74ca686e73c58f388#code + return { address: '0x220bd4ba855a954a57d5eac74ca686e73c58f388', abi }; } case '84532-andromeda': { - // https://sepolia.basescan.org/address/0x0d08ff9e0ceddf81a85bc160d5d378eea7a1e200#code - return { address: '0x0d08ff9e0ceddf81a85bc160d5d378eea7a1e200', abi }; + // https://sepolia.basescan.org/address/0x4e41a49dc192b3c31acea9db38be74ac224e7212#code + return { address: '0x4e41a49dc192b3c31acea9db38be74ac224e7212', abi }; } // Arbitrum contracts cannot be the same as Base as the workflow is different // case '42161-main': { diff --git a/liquidity/lib/useClearDebt/package.json b/liquidity/lib/useClearDebt/package.json index 306f8c6f..e2aff785 100644 --- a/liquidity/lib/useClearDebt/package.json +++ b/liquidity/lib/useClearDebt/package.json @@ -6,6 +6,7 @@ "dependencies": { "@snx-v3/isBaseAndromeda": "workspace:*", "@snx-v3/txnReducer": "workspace:*", + "@snx-v3/useAccountProxy": "workspace:*", "@snx-v3/useBlockchain": "workspace:*", "@snx-v3/useCollateralPriceUpdates": "workspace:*", "@snx-v3/useCoreProxy": "workspace:*", @@ -15,7 +16,6 @@ "@snx-v3/useGasSpeed": "workspace:*", "@snx-v3/useSpotMarketProxy": "workspace:*", "@snx-v3/withERC7412": "workspace:*", - "@synthetixio/wei": "^2.74.4", "@tanstack/react-query": "^5.8.3", "ethers": "^5.7.2", "react": "^18.2.0" diff --git a/liquidity/lib/useClearDebt/useClearDebt.tsx b/liquidity/lib/useClearDebt/useClearDebt.tsx index f04bb24e..6a38ed98 100644 --- a/liquidity/lib/useClearDebt/useClearDebt.tsx +++ b/liquidity/lib/useClearDebt/useClearDebt.tsx @@ -1,5 +1,6 @@ import { USDC_BASE_MARKET } from '@snx-v3/isBaseAndromeda'; import { initialState, reducer } from '@snx-v3/txnReducer'; +import { useAccountProxy } from '@snx-v3/useAccountProxy'; import { useNetwork, useProvider, useSigner } from '@snx-v3/useBlockchain'; import { useCollateralPriceUpdates } from '@snx-v3/useCollateralPriceUpdates'; import { useCoreProxy } from '@snx-v3/useCoreProxy'; @@ -9,7 +10,6 @@ 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 Wei from '@synthetixio/wei'; import { useMutation } from '@tanstack/react-query'; import { ethers } from 'ethers'; import React from 'react'; @@ -18,18 +18,15 @@ export const useClearDebt = ({ accountId, poolId, collateralTypeAddress, - availableUSDCollateral, - debt, }: { accountId?: string; poolId?: string; collateralTypeAddress?: string; - availableUSDCollateral?: Wei; - debt?: Wei; }) => { const [txnState, dispatch] = React.useReducer(reducer, initialState); const { data: CoreProxy } = useCoreProxy(); const { data: SpotMarketProxy } = useSpotMarketProxy(); + const { data: AccountProxy } = useAccountProxy(); const { data: priceUpdateTx, refetch: refetchPriceUpdateTx } = useCollateralPriceUpdates(); const signer = useSigner(); @@ -42,51 +39,57 @@ export const useClearDebt = ({ const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); - if (!(CoreProxy && poolId && accountId && collateralTypeAddress && SpotMarketProxy)) { + if ( + !( + CoreProxy && + poolId && + accountId && + collateralTypeAddress && + SpotMarketProxy && + DebtRepayer && + AccountProxy + ) + ) { return; } - if (!availableUSDCollateral) return; - try { dispatch({ type: 'prompting' }); - const transactions = []; - - if (DebtRepayer) { - const DebtRepayerContract = new ethers.Contract( - DebtRepayer.address, - DebtRepayer.abi, - signer - ); - const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( - CoreProxy.address, - SpotMarketProxy.address, - accountId, - poolId, - collateralTypeAddress, - USDC_BASE_MARKET - ); - transactions.push(depositDebtToRepay); - } + const AccountProxyContract = new ethers.Contract( + AccountProxy.address, + AccountProxy.abi, + signer + ); + const DebtRepayerContract = new ethers.Contract( + DebtRepayer.address, + DebtRepayer.abi, + signer + ); - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const approveAccountTx = AccountProxyContract.populateTransaction.approve( + DebtRepayer.address, + accountId + ); - const burn = CoreProxyContract.populateTransaction.burnUsd( - ethers.BigNumber.from(accountId), - ethers.BigNumber.from(poolId), + const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( + CoreProxy.address, + SpotMarketProxy.address, + AccountProxy.address, + accountId, + poolId, collateralTypeAddress, - debt?.mul(110).div(100).toBN().toString() || '0' + USDC_BASE_MARKET ); - transactions.push(burn); - const callsPromise = Promise.all(transactions); + 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( network, diff --git a/liquidity/lib/useUndelegateBaseAndromeda/package.json b/liquidity/lib/useUndelegateBaseAndromeda/package.json index 7369c2d5..ce27b7e9 100644 --- a/liquidity/lib/useUndelegateBaseAndromeda/package.json +++ b/liquidity/lib/useUndelegateBaseAndromeda/package.json @@ -4,11 +4,10 @@ "main": "index.ts", "version": "0.0.1", "dependencies": { - "@snx-v3/format": "workspace:*", "@snx-v3/isBaseAndromeda": "workspace:*", "@snx-v3/tsHelpers": "workspace:*", "@snx-v3/txnReducer": "workspace:*", - "@snx-v3/useApprove": "workspace:*", + "@snx-v3/useAccountProxy": "workspace:*", "@snx-v3/useBlockchain": "workspace:*", "@snx-v3/useCollateralPriceUpdates": "workspace:*", "@snx-v3/useCoreProxy": "workspace:*", @@ -16,7 +15,6 @@ "@snx-v3/useGasOptions": "workspace:*", "@snx-v3/useGasPrice": "workspace:*", "@snx-v3/useGasSpeed": "workspace:*", - "@snx-v3/useGetUSDTokens": "workspace:*", "@snx-v3/useLiquidityPosition": "workspace:*", "@snx-v3/useSpotMarketProxy": "workspace:*", "@snx-v3/withERC7412": "workspace:*", diff --git a/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx b/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx index 02a4b6a0..95909ba2 100644 --- a/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx +++ b/liquidity/lib/useUndelegateBaseAndromeda/useUndelegateBaseAndromeda.tsx @@ -1,8 +1,7 @@ -import { parseUnits } from '@snx-v3/format'; import { USDC_BASE_MARKET } from '@snx-v3/isBaseAndromeda'; import { notNil } from '@snx-v3/tsHelpers'; import { initialState, reducer } from '@snx-v3/txnReducer'; -import { useApprove } from '@snx-v3/useApprove'; +import { useAccountProxy } from '@snx-v3/useAccountProxy'; import { useNetwork, useProvider, useSigner } from '@snx-v3/useBlockchain'; import { useCollateralPriceUpdates } from '@snx-v3/useCollateralPriceUpdates'; import { useCoreProxy } from '@snx-v3/useCoreProxy'; @@ -10,7 +9,6 @@ import { useDebtRepayer } from '@snx-v3/useDebtRepayer'; 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 { LiquidityPosition } from '@snx-v3/useLiquidityPosition'; import { useSpotMarketProxy } from '@snx-v3/useSpotMarketProxy'; import { withERC7412 } from '@snx-v3/withERC7412'; @@ -25,7 +23,6 @@ export const useUndelegateBaseAndromeda = ({ collateralTypeAddress, collateralChange, currentCollateral, - liquidityPosition, }: { accountId?: string; poolId?: string; @@ -43,73 +40,70 @@ export const useUndelegateBaseAndromeda = ({ const { gasSpeed } = useGasSpeed(); const provider = useProvider(); const { network } = useNetwork(); - const { data: usdTokens } = useGetUSDTokens(); - - const debtExists = liquidityPosition?.debt.gt(0); - const currentDebt = debtExists && liquidityPosition ? liquidityPosition.debt : wei(0); + const { data: AccountProxy } = useAccountProxy(); const { data: DebtRepayer } = useDebtRepayer(); - const { approve, requireApproval } = useApprove({ - contractAddress: usdTokens?.USDC, - //slippage for approval - amount: parseUnits(currentDebt.toString(), 6).mul(110).div(100), - spender: DebtRepayer?.address, - }); const mutation = useMutation({ mutationFn: async () => { if (!signer || !network || !provider) throw new Error('No signer or network'); - if (!(CoreProxy && poolId && collateralTypeAddress && SpotMarketProxy)) return; + if ( + !( + CoreProxy && + AccountProxy && + DebtRepayer && + poolId && + collateralTypeAddress && + SpotMarketProxy + ) + ) + return; if (collateralChange.eq(0)) return; if (currentCollateral.eq(0)) return; + try { dispatch({ type: 'prompting' }); - if (debtExists && requireApproval) { - await approve(false); - } + const AccountProxyContract = new ethers.Contract( + AccountProxy.address, + AccountProxy.abi, + signer + ); - const transactions: Promise[] = []; - - if (DebtRepayer) { - const DebtRepayerContract = new ethers.Contract( - DebtRepayer.address, - DebtRepayer.abi, - signer - ); - const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( - CoreProxy.address, - SpotMarketProxy.address, - accountId, - poolId, - collateralTypeAddress, - USDC_BASE_MARKET - ); - transactions.push(depositDebtToRepay); - } + const DebtRepayerContract = new ethers.Contract( + DebtRepayer.address, + DebtRepayer.abi, + signer + ); - const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + const approveAccountTx = AccountProxyContract.populateTransaction.approve( + DebtRepayer.address, + accountId + ); - const burn = CoreProxyContract.populateTransaction.burnUsd( - ethers.BigNumber.from(accountId), - ethers.BigNumber.from(poolId), + const depositDebtToRepay = DebtRepayerContract.populateTransaction.depositDebtToRepay( + CoreProxy.address, + SpotMarketProxy.address, + AccountProxy.address, + accountId, + poolId, collateralTypeAddress, - currentDebt.abs().mul(10).toBN() + USDC_BASE_MARKET ); - transactions.push(burn); - const populatedTxnPromised = CoreProxyContract.populateTransaction.delegateCollateral( + const CoreProxyContract = new ethers.Contract(CoreProxy.address, CoreProxy.abi, signer); + + const delegateTx = CoreProxyContract.populateTransaction.delegateCollateral( ethers.BigNumber.from(accountId), ethers.BigNumber.from(poolId), collateralTypeAddress, currentCollateral.add(collateralChange).toBN(), wei(1).toBN() ); - transactions.push(populatedTxnPromised); const callsPromise: Promise< (ethers.PopulatedTransaction & { requireSuccess?: boolean })[] - > = Promise.all([...transactions].filter(notNil)); + > = Promise.all([approveAccountTx, depositDebtToRepay, delegateTx].filter(notNil)); const [calls, gasPrices] = await Promise.all([callsPromise, getGasPrice({ provider })]); diff --git a/liquidity/ui/package.json b/liquidity/ui/package.json index cdc153b9..6b9321ca 100644 --- a/liquidity/ui/package.json +++ b/liquidity/ui/package.json @@ -86,6 +86,7 @@ "@snx-v3/useTokenPrice": "workspace:*", "@snx-v3/useTransferAccountId": "workspace:*", "@snx-v3/useTransferableSynthetix": "workspace:*", + "@snx-v3/useUSDC": "workspace:*", "@snx-v3/useUSDProxy": "workspace:*", "@snx-v3/useUndelegate": "workspace:*", "@snx-v3/useUndelegateBaseAndromeda": "workspace:*", diff --git a/liquidity/ui/src/components/ClosePosition/ClosePosition.tsx b/liquidity/ui/src/components/ClosePosition/ClosePosition.tsx index e7b57b65..c42cace9 100644 --- a/liquidity/ui/src/components/ClosePosition/ClosePosition.tsx +++ b/liquidity/ui/src/components/ClosePosition/ClosePosition.tsx @@ -232,7 +232,7 @@ export const ClosePosition = ({ debt={liquidityPosition?.debt || ZEROWEI} collateralAmount={liquidityPosition?.collateralAmount || ZEROWEI} onClose={onClose} - debtSymbol={isBase ? params.collateralSymbol : systemToken?.symbol} + debtSymbol={isBase ? 'USDC' : systemToken?.symbol} collateralSymbol={collateralType.displaySymbol} onSubmit={() => setTransactions(true)} /> diff --git a/liquidity/ui/src/components/ClosePosition/ClosePositionTransactions.tsx b/liquidity/ui/src/components/ClosePosition/ClosePositionTransactions.tsx index a8917bfb..fc0f8b7c 100644 --- a/liquidity/ui/src/components/ClosePosition/ClosePositionTransactions.tsx +++ b/liquidity/ui/src/components/ClosePosition/ClosePositionTransactions.tsx @@ -26,6 +26,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { FC, ReactNode, useCallback, useContext, useEffect, useState } from 'react'; import { LiquidityPositionUpdated } from '../Manage/LiquidityPositionUpdated'; import { useParams } from '@snx-v3/useParams'; +import { useUSDC } from '@snx-v3/useUSDC'; export const ClosePositionTransactions: FC<{ onClose: () => void; @@ -93,6 +94,7 @@ export const ClosePositionTransactions: FC<{ const { data: DebtRepayer } = useDebtRepayer(); const { data: ClosePositionContract } = useClosePosition(); + const { data: USDC } = useUSDC(); // repay approval for base const { @@ -100,10 +102,10 @@ export const ClosePositionTransactions: FC<{ requireApproval: requireApprovalUSDC, isLoading, } = useApprove({ - contractAddress: wrapperToken, + contractAddress: USDC?.address, // slippage for approval amount: parseUnits(liquidityPosition?.debt.abs().toString(), 6) - .mul(110) + .mul(120) .div(100), spender: DebtRepayer?.address, }); @@ -181,13 +183,11 @@ export const ClosePositionTransactions: FC<{ cb: () => undelegate(), }); } else { - if (liquidityPosition?.debt.gt(-0.00001)) { - if (requireApprovalUSDC) { - transactions.push({ - title: `Approve ${debtSymbol} transfer`, - cb: () => approveUSDC(false), - }); - } + if (liquidityPosition?.debt.gt(-0.00001) && requireApprovalUSDC) { + transactions.push({ + title: `Approve ${debtSymbol} transfer`, + cb: () => approveUSDC(false), + }); } transactions.push({ @@ -200,20 +200,6 @@ export const ClosePositionTransactions: FC<{ ), cb: () => undelegateBaseAndromeda(), }); - - if (liquidityPosition?.debt.lt(0)) { - transactions.push({ - title: 'Claim', - subtitle: ( - - ), - cb: () => execBorrow(), - }); - } } return transactions; diff --git a/liquidity/ui/src/components/Deposit/Deposit.tsx b/liquidity/ui/src/components/Deposit/Deposit.tsx index 6a15eefe..ca9e7d78 100644 --- a/liquidity/ui/src/components/Deposit/Deposit.tsx +++ b/liquidity/ui/src/components/Deposit/Deposit.tsx @@ -42,7 +42,7 @@ import { TokenIcon } from '../TokenIcon/TokenIcon'; import { TransactionSummary } from '../TransactionSummary/TransactionSummary'; export const DepositUi: FC<{ - accountCollateral: AccountCollateralType; + accountCollateral?: AccountCollateralType; collateralChange: Wei; ethBalance?: Wei; snxBalance?: { @@ -53,7 +53,7 @@ export const DepositUi: FC<{ displaySymbol: string; symbol: string; setCollateralChange: (val: Wei) => void; - minDelegation: Wei; + minDelegation?: Wei; currentCollateral: Wei; currentDebt: Wei; collateralPrice: Wei; @@ -104,9 +104,10 @@ export const DepositUi: FC<{ return tokenBalance.add(ethBalance); }, [symbol, isStataUSDC, tokenBalance, ethBalance, snxBalance?.transferable, stataUSDCBalance]); - const maxAmount = useMemo(() => { - return combinedTokenBalance?.add(accountCollateral.availableCollateral); - }, [accountCollateral.availableCollateral, combinedTokenBalance]); + const maxAmount = + combinedTokenBalance && accountCollateral?.availableCollateral + ? combinedTokenBalance.add(accountCollateral.availableCollateral) + : ZEROWEI; const txSummaryItems = useMemo(() => { const items = [ @@ -248,18 +249,22 @@ export const DepositUi: FC<{ - - - - - Your deposit must be {formatNumber(parseFloat(minDelegation.toString()))} {symbol} or - higher - - - + + {minDelegation ? ( + + + + + Your deposit must be {formatNumber(parseFloat(minDelegation.toString()))} {symbol} or + higher + + + + ) : null} + @@ -269,20 +274,24 @@ export const DepositUi: FC<{ - - - + {minDelegation ? ( + + + + ) : null} +