From 331d415bb3fab66f5e81057c69c951f347493df8 Mon Sep 17 00:00:00 2001 From: Mark Grothe Date: Mon, 3 Oct 2022 11:49:34 -0500 Subject: [PATCH] chore: merge main (#1194) * chore: automated cache update [skip cypress] * feat: add maticx icons (#1106) * e2e - refactor (#1131) * init commands * test version with commands without timeouts * move all methods to CY commands * fix matic e-mode * add documentation * add cy.wait for max amount Co-authored-by: Nikita Boakrev * chore: automated cache update [skip cypress] * chore: add prettier commands (#1142) * chore: add prettierignore * chore: add prettier commands * chore: add combined linting commands * chore: run yarn lint:fix * chore: update lint-staged * chore: update contributors * chore: typo * chore: typos * chore: add license field * chore: automated cache update [skip cypress] * fix: lint-staged (#1145) * chore: automated cache update [skip cypress] * feat: refactor frozen warnings (#1141) * feat: improve frozen warning mappings * fix: clean up logic and make warnings responsive to frozen status * fix: trans tag wrapping * fix: simplify MarketWarning component * chore: run i18n * remove fantom market from CI while it frozen Co-authored-by: Nikita Boakrev * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore(i18n): synchronize translations from crowdin [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * feat: removed warnings related to eth borrow being disabled (#1113) * feat: removed warnings related to eth borrow being disabled * chore: prettier * chore: fix missing param * turn on eth tests Co-authored-by: Mark Hinschberger Co-authored-by: Nikita Boakrev * feat: add custom error page templates (#1094) * chore: stub out the minimal error pages * feat: add custom 404 page * chore: i18n * chore: add 500 page * chore: cleanup * chore: remove img from 500 * chore: use native img tag * chore: add light/dark mode graphics * chore: more UI updates * chore: 500 page ideas * chore: 500 error updates * chore: test error boundary component in prod * chore: ignore for build (temp) * chore: remove error boundary component * chore: style updates * chore: remove throw * chore: merge branch 'main' into feat/error-pages * chore: use lint-staged pre-commit * fix: build fix * chore: post-merge cleanup * chore: post-merge cleanup * chore(i18n): synchronize translations from crowdin [skip cypress] * feat: warn about governance in borrow modal (#999) * chore(i18n): synchronize translations from crowdin [skip cypress] * chore(i18n): synchronize translations from crowdin [skip cypress] * fix: btc.b symbol (#1152) * fix-tests (#1159) Co-authored-by: Nikita Boakrev * fix: use correct blocktime (#1147) * fix: change item name (#1148) * chore: automated cache update [skip cypress] * chore: sort imports (#1127) Co-authored-by: Drew Cook * chore: revise contributing doc and PR template (#1158) * chore: update contributing doc * chore: lint * chore: add default PR template + labels * chore: move to be used as default * chore: lowercase labels * fix: typo * chore: add project, remove frontmatter from pr template * chore: projects fix * chore: remove projects from frontmatter * chore: update fork link * chore: use js code block * feat: add in wallet fork config section * feat: graph improvements (#1102) * chore: refactor graph component hierarchy * feat: initial component for graph time range selector * refactor: moved global style for ToggleButton to a styled component * fix: enforce a selection * fix: styles * fix: bad merge * refactor: move data fetching to graph containers * feat: handle selected time range in parent component * feat: initial hookup of time range selector * feat: removed 'Max' time selection from graphs * feat: add in loading/error states * feat: clean up graph styles * feat: add supply rate * chore: remove log * chore: add circle over supply rate * chore: supply APR logic * chore: show time/legend when loading * chore: test error state * chore: shorter graphs * fix: build * chore: remove unused component * chore: remove time selector from IRM graph * chore: add style changes to graphs * chore: i18n * chore: bump material and add react-transition-group * chore: more style updates * chore: more styling * feat: add interest rate strategy link (#1124) * feat: add link to interest rate strategy * fix: remove unnecessary typography tag * chore: i18n * chore: more styling * chore: cleanup * chore: removed TODO comment * fix: handle formatting dates behind UTC time, and don't include time for 6m and 1y time range * fix: remove console logs * fix: remove supply APY from interest rate model graph (#1160) * chore: log cleanup * chore: show % * fix: remove horizontal divider for util rate * chore: update eslintignore * fix: remove unused file * fix: re-render issue with loading spinner * chore: remove unused package * chore: hide charts for v3 and comment about bug * chore: remove log * feat: temporarily disable charts for polygon v2 Co-authored-by: Mark Grothe Co-authored-by: Andrew Schmidt * chore: add QA verification checklist item for reviews (#1175) * fix: remove duplicate warning (#1173) * chore(i18n): synchronize translations from crowdin [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * fix(swap-modal): open swap modal on mobile (#1181) * chore: automated cache update [skip cypress] * fix: correct bridge message (#1168) * fix: you don't bridge ETH assets * fix: update po file * fix: split messages in two * chore: add @lidofinance/web3-ledgerhq-frame-connector (#1157) * fix: debt ceiling wording (#1171) * fix: tooltip wording * fix: use correct warning component and sort imports * chore: run i18n Co-authored-by: Drew Cook * feat: market search (#1174) * feat: wip * feat: wip * feat: wip * feat(search): styled to match designs * chore: i18n * feat(search): filter list based on search term * chore: lint * feat(search): auto focus on mobile, hide clear button when inactive * feat(search): search on underlying address * feat(search): show no results message * chore: i18n * feat(search): give input focus on clearing * refactor: asset list titles * feat(search): renamed file, cleanup * feat(search): cleanup * refactor: moved files * feat(search): set font size to 16px on mobile to prevent auto zooming * chore: small cleanup * feat(search): focus input on clear on mobile, make code consistent * chore: i18n * feat(search): adjust no results message for mobile * feat: graph improvements (#1102) * chore: refactor graph component hierarchy * feat: initial component for graph time range selector * refactor: moved global style for ToggleButton to a styled component * fix: enforce a selection * fix: styles * fix: bad merge * refactor: move data fetching to graph containers * feat: handle selected time range in parent component * feat: initial hookup of time range selector * feat: removed 'Max' time selection from graphs * feat: add in loading/error states * feat: clean up graph styles * feat: add supply rate * chore: remove log * chore: add circle over supply rate * chore: supply APR logic * chore: show time/legend when loading * chore: test error state * chore: shorter graphs * fix: build * chore: remove unused component * chore: remove time selector from IRM graph * chore: add style changes to graphs * chore: i18n * chore: bump material and add react-transition-group * chore: more style updates * chore: more styling * feat: add interest rate strategy link (#1124) * feat: add link to interest rate strategy * fix: remove unnecessary typography tag * chore: i18n * chore: more styling * chore: cleanup * chore: removed TODO comment * fix: handle formatting dates behind UTC time, and don't include time for 6m and 1y time range * fix: remove console logs * fix: remove supply APY from interest rate model graph (#1160) * chore: log cleanup * chore: show % * fix: remove horizontal divider for util rate * chore: update eslintignore * fix: remove unused file * fix: re-render issue with loading spinner * chore: remove unused package * chore: hide charts for v3 and comment about bug * chore: remove log * feat: temporarily disable charts for polygon v2 Co-authored-by: Mark Grothe Co-authored-by: Andrew Schmidt * chore: add QA verification checklist item for reviews (#1175) * fix: remove duplicate warning (#1173) * chore: i18n * chore: i18n * refactor: renamed prop * feat(search): added debounce to the input change handler * chore(i18n): synchronize translations from crowdin [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * fix(swap-modal): open swap modal on mobile (#1181) * chore: automated cache update [skip cypress] * fix: correct bridge message (#1168) * fix: you don't bridge ETH assets * fix: update po file * fix: split messages in two * chore: i18n * fix: duplicate imports * chore: i18n * chore: i18n Co-authored-by: Drew Cook Co-authored-by: Andrew Schmidt Co-authored-by: Crowdin bot Co-authored-by: Cache bot Co-authored-by: Lukas * chore: automated cache update [skip cypress] * chore: lint and pre-commit updates (#1169) * chore: order imports * chore: add files from i18n in pre-commit Co-authored-by: Drew Cook * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore(i18n): synchronize translations from crowdin [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: use a fallback IPFS gateway for dynamic proposals (#1182) * chore: use a fallback IPFS gateway for dynamic proposals * fix: typo * fix: lint (#1185) * chore: update 404 image (#1184) * chore: update 404 image * chore: remove-brackets * fix tests Co-authored-by: Nikita Boakrev * feat: add watch mode only wallet address (#1178) * feat(WalletWidget): added Alert component * feat(mockWalletAddressContext): added mockWalletAddressContext * feat(WalletSelector): added input to wallet selector for mock address * feat(WatchOnlyModeTooltip): added WatchOnlyMode tooltip * feat(WalletSelector): use web3 context for set mock adddress * chore(WalletWidget): removed console log * feat(WalletWidget): added warning icon * chore: updated message.js * chore: updated message.js * feat(Web3Provider): updated condition to render Wallet Modal * feat(TxActionsWrapper): render warning text when using mockAddress * chore: updated messages * feat(WalletSelector): use utils to check if mock address is valid * feat(TxActionsWrapper): disabled button if mockAddress is present * chore: updated message.js * feat: do not render ChangeNetworkWarning when mockAddress is present * feat(WalletWidget): added buttons to switch network and disconnect * feat(WalletWidget): adjusted margin * chore(WalletWidget): removed Disconnect Wallet button * chore: removed temp value for max amount to borrow * chore: updated messages.po * chore: updated markdown to trigger git delta * chore: reverted changes to Code of Conduct md * chore: updated message.js * chore: unimport WatchOnlyModeToolTip to ensure message.js is updated * chore(cypress.config): removed eslint-disable * chore(WalletSelector): re-import WatchOnlyModeToolTip * feat(WalletWidget): set wallet widget to close when clicking switch wallet * feat(Web3Provider): when connect wallet, check and remove mock address * feat: if mockAddress is present, do not render approval help text and button * feat: on mobile, if mockAddress is present, render Close menu icon * feat(WalletWidget): update styling for warning text in mobile * feat(WalletWidget): on mobile, render CTAs at list buttom * feat(WalletWidget): add color styling to CTAs * feat(WalletWidget): styling for avatar icon on mobile * chore: updated message.js * feat(WalletWidget): update styling for warning icon background * feat(WalletWidget): use hideWalletAccountText variable to determine mockAddress styling * chore(WalletWidget): removed un-used improt * feat(WalletWidget): fine-tune warning icon color * feat(WalletWidget): updated styling to background and CTA buttons * feat(WalletWidget): added Divider * chore(WalletWidget): updated copy * feat(WalletSelector): use fullWifth props for button * feat(WalletSelector): use fullWidfth props for input * feat(WatchOnlyModeTooltip): re-named component with correct casing * feat(WalletSelector): added boolean state to check for valid address error * feat(WalletWidget): added handle switch wallet method * chore: updated messages.js * chore: code style changes * update tests * fix: use proper app header color * fix: prevent auto zoom on mobile on watch address input * test: don't set a watch wallet in cypress tests * feat: wrap in form to allow input by pressing enter * feat: add support for watching ens names * feat: add ENS to placeholder text * feat: enable watch button for ens inputs * chore: use proper submit button for form * chore: sx styles * fix tests Co-authored-by: matthew.lau Co-authored-by: Mark Grothe Co-authored-by: Nikita Boakrev Co-authored-by: Andrew Schmidt * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore(i18n): synchronize translations from crowdin [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * chore: automated cache update [skip cypress] * fix: use gateway retry logic anywhere we fetch ipfs data (#1186) * fix: swap ipfs gateway * fix: replace pinata gateway with ipfs one * fix: use retry logic anywhere we try to get proposal from ipfs * fix: remove unused param * chore: add JSDoc comments Co-authored-by: Drew Cook * fix: lint Co-authored-by: Cache bot Co-authored-by: Lukas Co-authored-by: Nikita Co-authored-by: Nikita Boakrev Co-authored-by: Drew Cook Co-authored-by: Andrew Schmidt Co-authored-by: Crowdin bot Co-authored-by: Mark Hinschberger Co-authored-by: Jonathan Reem Co-authored-by: derasd Co-authored-by: slundy-ledger <96065469+slundy-ledger@users.noreply.github.com> Co-authored-by: 0xGraham Co-authored-by: matthew.lau --- .eslintignore | 23 +- .eslintrc.js | 6 +- .github/ISSUE_TEMPLATE/bug-report---.md | 5 +- .github/ISSUE_TEMPLATE/feature-request---.md | 5 +- .github/actions/analyze-comment/action.yml | 4 +- .github/actions/cypress/action.yml | 2 +- .../actions/fork-result-comment/action.yml | 4 +- .github/actions/private-ipfs-pin/action.yml | 6 +- .github/actions/upload-artifacts/action.yml | 10 +- .github/pull_request_template.md | 29 ++ .github/workflows/build-fork.yml | 4 +- .github/workflows/build-test-deploy.yml | 3 - .github/workflows/i18n-check.yml | 4 +- .github/workflows/test-deploy-fork.yml | 32 +- .github/workflows/update-prod-staging.yml | 2 +- .husky/pre-commit | 1 + .prettierignore | 22 ++ CONTRIBUTING.md | 155 ++++++--- cypress/configs/settings.config.ts | 1 + .../configs/v2-markets/amm-v2-full.config.ts | 1 + .../configs/v2-markets/amm-v2-smoke.config.ts | 1 + .../avalanche-v2-additional.config.ts | 1 + .../v2-markets/avalanche-v2-full.config.ts | 1 + .../v2-markets/avalanche-v2-smoke.config.ts | 1 + .../ethereum-v2-additional.config.ts | 1 + .../v2-markets/ethereum-v2-full.config.ts | 1 + .../v2-markets/ethereum-v2-smoke.config.ts | 1 + .../polygon-v2-additional.config.ts | 3 +- .../v2-markets/polygon-v2-full.config.ts | 3 +- .../v2-markets/polygon-v2-smoke.config.ts | 3 +- .../arbitrum-v3-additional.config.ts | 1 + .../v3-markets/arbitrum-v3-full.config.ts | 1 + .../v3-markets/arbitrum-v3-smoke.config.ts | 1 + .../avalanche-v3-additional.config.ts | 1 + .../v3-markets/avalanche-v3-full.config.ts | 1 + .../v3-markets/avalanche-v3-smoke.config.ts | 1 + .../v3-markets/fantom-v3-additional.config.ts | 1 + .../v3-markets/fantom-v3-full.config.ts | 1 + .../v3-markets/fantom-v3-smoke.config.ts | 1 + .../optimism-v3-additional.config.ts | 1 + .../v3-markets/optimism-v3-full.config.ts | 1 + .../v3-markets/optimism-v3-smoke.config.ts | 1 + .../polygon-v3-additional.config.ts | 1 + .../v3-markets/polygon-v3-full.config.ts | 1 + .../v3-markets/polygon-v3-smoke.config.ts | 1 + .../0-assets/bat.aave-v2.cy.ts | 10 +- .../0-assets/dai.aave-v2.cy.ts | 10 +- .../0-assets/eth.aave-v2.cy.ts | 37 ++- .../0-assets/mana.aave-v2.cy.ts | 10 +- .../0-assets/mkr.aave-v2.cy.ts | 10 +- .../0-assets/ren.aave-v2.cy.ts | 10 +- .../0-assets/tusd.aave-v2.cy.ts | 10 +- .../0-assets/usdc.aave-v2.cy.ts | 10 +- .../0-assets/usdt.aave-v2.cy.ts | 10 +- .../critical-conditions.aave-v2.cy.ts | 6 +- .../0-main-v2-market/reward.aave-v2.cy.ts | 2 +- .../0-main-v2-market/stake.aave-v2.cy.ts | 24 +- .../0-main-v2-market/swap.aave-v2.cy.ts | 4 +- .../1-amm-v2-market/0-assets/dai.amm-v2.cy.ts | 8 +- .../1-amm-v2-market/0-assets/eth.amm-v2.cy.ts | 37 ++- .../0-assets/usdc.amm-v2.cy.ts | 8 +- .../0-assets/usdt.amm-v2.cy.ts | 8 +- .../0-assets/wbtc.amm-v2.cy.ts | 8 +- .../critical-conditions.amm-v2.cy.ts | 6 +- .../0-assets/bal.polygon-v2.cy.ts | 8 +- .../0-assets/crv.polygon-v2.cy.ts | 8 +- .../0-assets/dai.polygon-v2.cy.ts | 8 +- .../0-assets/dpi.polygon-v2.cy.ts | 8 +- .../0-assets/ghst.polygon-v2.cy.ts | 8 +- .../0-assets/link.polygon-v2.cy.ts | 8 +- .../0-assets/matic.polygon-v2.cy.ts | 12 +- .../0-assets/sushi.polygon-v2.cy.ts | 8 +- .../0-assets/usdc.polygon-v2.cy.ts | 8 +- .../0-assets/usdt.polygon-v2.cy.ts | 8 +- .../0-assets/wbtc.polygon-v2.cy.ts | 8 +- .../0-assets/weth.polygon-v2.cy.ts | 8 +- .../critical-conditions.polygon-v2.cy.ts | 6 +- .../2-polygon-v2-market/swap.polygon-v2.cy.ts | 4 +- .../0-assets/avax.avalanche-v2.cy.ts | 12 +- .../0-assets/dai.avalanche-v2.cy.ts | 8 +- .../0-assets/usdc.avalanche-v2.cy.ts | 8 +- .../0-assets/usdt.avalanche-v2.cy.ts | 8 +- .../0-assets/wbtc.avalanche-v2.cy.ts | 8 +- .../0-assets/weth.avalanche-v2.cy.ts | 8 +- .../critical-conditions.avalanche-v2.cy.ts | 6 +- .../reward.avalanche-v2.cy.ts | 2 +- .../swap.avalanche-v2.cy.ts | 4 +- .../0-assets/dai.arbitrum-v3.cy.ts | 10 +- .../0-assets/eth.arbitrum-v3.cy.ts | 12 +- .../0-assets/link.arbitrum-v3.cy.ts | 8 +- .../0-assets/usdc.arbitrum-v3.cy.ts | 10 +- .../0-assets/usdt.arbitrum-v3.cy.ts | 10 +- .../0-assets/wbtc.arbitrum-v3.cy.ts | 8 +- .../critical-conditions.arbitrum-v3.cy.ts | 6 +- .../e-mode.arbitrum-v3.cy.ts | 8 +- .../0-assets/avax.avalanche-v3.cy.ts | 12 +- .../0-assets/dai.avalanche-v3.cy.ts | 10 +- .../0-assets/link.avalanche-v3.cy.ts | 8 +- .../0-assets/usdc.avalanche-v3.cy.ts | 10 +- .../0-assets/usdt.avalacnhe-v3.cy.ts | 10 +- .../0-assets/wbtc.avalanche-v3.cy.ts | 8 +- .../0-assets/weth.avalanche-v3.cy.ts | 8 +- .../critical-conditions.avalanche-v3.cy.ts | 6 +- .../e-mode.avalanche-v3.cy.ts | 13 +- .../isolated-mode.avalanche-v3.cy.ts | 10 +- .../reward.avalanche-v3.cy.ts | 2 +- .../swap.avalanche-v3.cy.ts | 4 +- .../0-assets/dai.polygon-v3.cy.ts | 10 +- .../0-assets/eurs.polygon-v3.cy.ts | 10 +- .../0-assets/link.polygon-v3.cy.ts | 8 +- .../0-assets/matic.polygon-v3.cy.ts | 12 +- .../0-assets/usdc.polygon-v3.cy.ts | 10 +- .../0-assets/usdt.polygon-v3.cy.ts | 10 +- .../0-assets/wbtc.polygon-v3.cy.ts | 8 +- .../0-assets/weth.polygon-v3.cy.ts | 8 +- .../critical-conditions.polygon-v3.cy.ts | 6 +- .../e-mode.polygon-v3.cy.ts | 24 +- .../isolated-and-emode.polygon-v3.cy.ts | 18 +- .../isolated-mode.polygon-v3.cy.ts | 10 +- .../3-polygon-v3-market/swap.polygon-v3.cy.ts | 4 +- .../0-assets/dai.optimism-v3.cy.ts | 10 +- .../0-assets/eth.optimism-v3.cy.ts | 12 +- .../0-assets/link.optimism-v3.cy.ts | 8 +- .../0-assets/susd.optimism-v3.cy.ts | 8 +- .../0-assets/usdc.optimism-v3.cy.ts | 10 +- .../0-assets/usdt.optimism-v3.cy.ts | 10 +- .../0-assets/wbtc.optimism-v3.cy.ts | 8 +- .../critical-conditions.optimism-v3.cy.ts | 6 +- .../e-mode.optimism-v3.cy.ts | 10 +- .../0-assets/dai.fantom-v3.cy.ts | 10 +- .../0-assets/ftm.fantom-v3.cy.ts | 12 +- .../0-assets/link.fantom-v3.cy.ts | 8 +- .../0-assets/usdc.fantom-v3.cy.ts | 10 +- .../0-assets/usdt.fantom-v3.cy.ts | 10 +- .../0-assets/wbtc.fantom-v3.cy.ts | 8 +- .../0-assets/weth.fantom-v3.cy.ts | 8 +- .../critical-conditions.fantom-v3.cy.ts | 6 +- .../5-fantom-v3-market/e-mode.fantom-v3.cy.ts | 8 +- .../isolated-mode.fantom-v3.cy.ts | 10 +- .../5-fantom-v3-market/swap.fantom-v3.cy.ts | 4 +- .../e2e/2-settings/details.page.aave-v2.cy.ts | 8 +- cypress/e2e/2-settings/switch-market.cy.ts | 2 +- cypress/e2e/2-settings/wallet-connect.cy.ts | 3 +- cypress/fixtures/assets.json | 12 + cypress/support/commands.ts | 134 ++++++-- cypress/support/cypress.d.ts | 10 - cypress/support/e2e.ts | 32 -- cypress/support/steps/actions.steps.ts | 119 ------- cypress/support/steps/configuration.steps.ts | 6 +- cypress/support/steps/main.steps.ts | 301 +++++++++++------ cypress/support/steps/verification.steps.ts | 124 ++++--- cypress/support/tools/tenderly.ts | 3 +- package.json | 38 ++- pages/404.page.tsx | 51 +++ pages/500.page.tsx | 90 +++++ pages/_app.page.tsx | 19 +- pages/_error.page.tsx | 20 ++ pages/faucet.page.tsx | 1 + pages/governance/ipfs-preview.governance.tsx | 4 +- .../proposal/[proposalId].governance.tsx | 195 ++++++----- .../governance/proposal/index.governance.tsx | 21 +- pages/index.page.tsx | 24 +- pages/reserve-overview.page.tsx | 24 +- pages/staking.staking.tsx | 26 +- public/404/404.svg | 1 + public/fork-config-example.png | Bin 0 -> 105014 bytes public/icons/tokens/maticx.svg | 1 + scripts/populate-cache.js | 15 +- src/components/AddressBlocked.tsx | 1 + src/components/AddressBlockedModal.tsx | 1 + src/components/ConnectWalletPaper.tsx | 3 +- src/components/ContentWithTooltip.tsx | 2 +- src/components/FaucetButton.tsx | 1 + src/components/HALLink.tsx | 8 +- src/components/HealthFactorNumber.tsx | 2 +- src/components/MarketSwitcher.tsx | 17 +- src/components/Meta.tsx | 2 +- src/components/ReserveOverviewBox.tsx | 2 +- src/components/ReserveSubheader.tsx | 3 +- src/components/StyledToggleButton.tsx | 36 ++ src/components/StyledToggleButtonGroup.tsx | 11 + .../WalletConnection/ConnectWalletButton.tsx | 1 + .../WalletConnection/WalletModal.tsx | 1 + .../WalletConnection/WalletSelector.tsx | 106 +++++- src/components/Warnings/CooldownWarning.tsx | 2 +- src/components/caps/CapsCircularStatus.tsx | 6 +- src/components/caps/DebtCeilingStatus.tsx | 15 +- .../infoTooltips/BorrowCapMaxedTooltip.tsx | 3 +- .../infoTooltips/DebtCeilingMaxedTooltip.tsx | 3 +- src/components/infoTooltips/EModeTooltip.tsx | 2 +- src/components/infoTooltips/FrozenTooltip.tsx | 51 +++ src/components/infoTooltips/FrozenWarning.tsx | 39 --- .../infoTooltips/ReserveFactorTooltip.tsx | 2 +- .../infoTooltips/SupplyCapMaxedTooltip.tsx | 3 +- .../infoTooltips/WatchOnlyModeTooltip.tsx | 14 + src/components/lists/ListMobileItem.tsx | 18 +- src/components/lists/ListWrapper.tsx | 11 +- src/components/primitives/CheckBadge.tsx | 2 +- src/components/primitives/Link.tsx | 2 +- src/components/transactions/AssetInput.tsx | 4 +- .../transactions/Borrow/BorrowActions.tsx | 1 + .../Borrow/BorrowModalContent.tsx | 27 +- .../ClaimRewards/ClaimRewardsModalContent.tsx | 4 +- .../CollateralChangeActions.tsx | 1 + .../CollateralChangeModalContent.tsx | 1 + .../transactions/Emode/EmodeActions.tsx | 1 + .../transactions/Emode/EmodeModalContent.tsx | 7 +- .../transactions/Emode/EmodeSelect.tsx | 1 + .../transactions/Faucet/FaucetActions.tsx | 1 + .../Faucet/FaucetModalContent.tsx | 1 + .../transactions/FlowCommons/ModalWrapper.tsx | 5 +- .../transactions/FlowCommons/Success.tsx | 2 +- .../GovDelegation/DelegationTokenSelector.tsx | 1 + .../GovDelegation/GovDelegationActions.tsx | 5 +- .../GovDelegationModalContent.tsx | 8 +- .../transactions/GovVote/GovVoteActions.tsx | 1 + .../GovVote/GovVoteModalContent.tsx | 5 +- .../RateSwitch/RateSwitchActions.tsx | 1 + .../RateSwitch/RateSwitchModalContent.tsx | 2 +- .../Repay/CollateralRepayModalContent.tsx | 21 +- .../transactions/Repay/RepayActions.tsx | 1 + .../transactions/Repay/RepayModal.tsx | 1 + .../transactions/Repay/RepayModalContent.tsx | 1 + .../transactions/Repay/RepayTypeSelector.tsx | 19 +- .../transactions/Stake/StakeModalContent.tsx | 4 +- .../StakeCooldownModalContent.tsx | 4 +- .../StakeRewardClaimActions.tsx | 3 +- .../StakeRewardClaimModal.tsx | 1 + .../StakeRewardClaimModalContent.tsx | 27 +- .../Supply/SupplyModalContent.tsx | 5 +- .../transactions/Swap/SwapActions.tsx | 2 +- .../transactions/Swap/SwapModal.tsx | 2 +- .../transactions/Swap/SwapModalContent.tsx | 37 ++- .../transactions/Swap/SwapModalDetails.tsx | 18 +- .../transactions/TxActionsWrapper.tsx | 20 +- .../transactions/UnStake/UnStakeActions.tsx | 3 +- .../transactions/UnStake/UnStakeModal.tsx | 1 + .../UnStake/UnStakeModalContent.tsx | 31 +- .../Warnings/BorrowCapWarning.tsx | 1 + .../Warnings/DebtCeilingWarning.tsx | 1 + .../Warnings/ETHBorrowWarning.tsx | 31 -- .../transactions/Warnings/HarmonyWarning.tsx | 27 -- .../transactions/Warnings/MarketWarning.tsx | 55 ++++ .../Warnings/SupplyCapWarning.tsx | 1 + .../transactions/Withdraw/WithdrawActions.tsx | 1 + .../AaveTokensDataProvider.tsx | 7 +- .../stake-data-provider/_useStakeDataRPC.tsx | 7 +- src/hooks/useAddressAllowed.tsx | 1 + src/hooks/useAssetCaps.tsx | 11 +- src/hooks/usePermissions.tsx | 6 +- src/hooks/useReservesHistory.tsx | 84 ++++- src/hooks/useSwap.ts | 13 +- src/hooks/useWalletModal.tsx | 6 +- src/layouts/AppHeader.tsx | 2 +- src/layouts/WalletWidget.tsx | 234 +++++++++---- src/layouts/components/DrawerWrapper.tsx | 2 +- src/libs/LanguageProvider.tsx | 2 +- src/libs/hooks/useWeb3Context.tsx | 1 + src/libs/web3-data-provider/WalletOptions.ts | 12 +- src/libs/web3-data-provider/Web3Provider.tsx | 96 ++++-- src/locales/de/messages.po | 163 +++++++-- src/locales/el/messages.js | 2 +- src/locales/el/messages.po | 163 +++++++-- src/locales/en/messages.js | 2 +- src/locales/en/messages.po | 167 +++++++--- src/locales/es/messages.js | 2 +- src/locales/es/messages.po | 183 ++++++++--- src/locales/fr/messages.js | 2 +- src/locales/fr/messages.po | 163 +++++++-- .../dashboard/DashboardEModeButton.tsx | 8 +- src/modules/dashboard/DashboardTopPanel.tsx | 11 +- .../BorrowAssetsList/BorrowAssetsList.tsx | 55 ++-- .../BorrowAssetsList/BorrowAssetsListItem.tsx | 1 + .../BorrowAssetsListMobileItem.tsx | 1 + .../BorrowedPositionsList.tsx | 13 +- .../BorrowedPositionsListItem.tsx | 5 +- .../BorrowedPositionsListMobileItem.tsx | 3 +- .../lists/ListItemCanBeCollateral.tsx | 1 + .../dashboard/lists/ListItemWrapper.tsx | 24 +- src/modules/dashboard/lists/ListLoader.tsx | 11 +- .../dashboard/lists/ListMobileItemWrapper.tsx | 12 +- .../SuppliedPositionsList.tsx | 15 +- .../SuppliedPositionsListItem.tsx | 1 + .../SuppliedPositionsListMobileItem.tsx | 5 +- .../SupplyAssetsList/SupplyAssetsList.tsx | 44 +-- .../SupplyAssetsList/SupplyAssetsListItem.tsx | 1 + .../SupplyAssetsListMobileItem.tsx | 1 + .../SupplyAssetsList/WalletEmptyInfo.tsx | 26 ++ src/modules/faucet/FaucetAssetsList.tsx | 21 +- src/modules/faucet/FaucetMobileItemLoader.tsx | 2 +- src/modules/faucet/FaucetTopPanel.tsx | 2 +- src/modules/governance/GovernanceTopPanel.tsx | 2 +- src/modules/governance/ProposalsList.tsx | 7 +- src/modules/governance/proposal/VoteInfo.tsx | 2 +- .../governance/utils/formatProposal.ts | 2 +- .../governance/utils/getProposalMetadata.ts | 73 ++++- src/modules/governance/utils/getVotes.ts | 1 + .../governance/utils/governanceProvider.tsx | 1 + src/modules/markets/AssetListTitle.tsx | 81 +++++ src/modules/markets/AssetSearchInput.tsx | 99 ++++++ src/modules/markets/AssetsList.tsx | 87 ++++- src/modules/markets/AssetsListItem.tsx | 7 +- src/modules/markets/AssetsListMobileItem.tsx | 7 +- src/modules/markets/MarketsTopPanel.tsx | 7 +- .../reserve-overview/ReserveActions.tsx | 39 ++- .../reserve-overview/ReserveConfiguration.tsx | 308 +++++------------- .../reserve-overview/ReservePanels.tsx | 73 +++++ .../reserve-overview/ReserveTopDetails.tsx | 5 +- .../reserve-overview/TokenLinkDropdown.tsx | 2 +- .../{ApyChart.tsx => graphs/ApyGraph.tsx} | 187 +++++++---- .../graphs/ApyGraphContainer.tsx | 144 ++++++++ .../GraphLegend.tsx} | 26 +- .../graphs/GraphTimeRangeSelector.tsx | 65 ++++ .../InterestRateModelGraph.tsx} | 243 +++++++++----- .../InterestRateModelGraphContainer.tsx | 62 ++++ src/modules/staking/GetABPToken.tsx | 5 +- src/modules/staking/GetABPTokenModal.tsx | 2 +- src/modules/staking/StakingHeader.tsx | 4 +- src/static-build/ipfs.ts | 10 +- src/static-build/ipfsFiles.json | 29 ++ src/static-build/proposal.ts | 1 + src/static-build/proposals.json | 108 +++++- src/static-build/vote.ts | 7 +- src/ui-config/governanceConfig.ts | 4 +- src/ui-config/menu-items/index.tsx | 2 +- src/ui-config/reservePatches.ts | 16 +- src/utils/hfUtils.ts | 6 +- src/utils/marketsAndNetworksConfig.ts | 12 +- src/utils/theme.tsx | 49 +-- tsconfig.json | 1 - yarn.lock | 218 ++++++++----- 331 files changed, 4483 insertions(+), 2405 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .prettierignore delete mode 100644 cypress/support/cypress.d.ts delete mode 100644 cypress/support/steps/actions.steps.ts create mode 100644 pages/404.page.tsx create mode 100644 pages/500.page.tsx create mode 100644 pages/_error.page.tsx create mode 100644 public/404/404.svg create mode 100644 public/fork-config-example.png create mode 100644 public/icons/tokens/maticx.svg create mode 100644 src/components/StyledToggleButton.tsx create mode 100644 src/components/StyledToggleButtonGroup.tsx create mode 100644 src/components/infoTooltips/FrozenTooltip.tsx delete mode 100644 src/components/infoTooltips/FrozenWarning.tsx create mode 100644 src/components/infoTooltips/WatchOnlyModeTooltip.tsx delete mode 100644 src/components/transactions/Warnings/ETHBorrowWarning.tsx delete mode 100644 src/components/transactions/Warnings/HarmonyWarning.tsx create mode 100644 src/components/transactions/Warnings/MarketWarning.tsx create mode 100644 src/modules/dashboard/lists/SupplyAssetsList/WalletEmptyInfo.tsx create mode 100644 src/modules/markets/AssetListTitle.tsx create mode 100644 src/modules/markets/AssetSearchInput.tsx create mode 100644 src/modules/reserve-overview/ReservePanels.tsx rename src/modules/reserve-overview/{ApyChart.tsx => graphs/ApyGraph.tsx} (52%) create mode 100644 src/modules/reserve-overview/graphs/ApyGraphContainer.tsx rename src/modules/reserve-overview/{ChartLegend.tsx => graphs/GraphLegend.tsx} (54%) create mode 100644 src/modules/reserve-overview/graphs/GraphTimeRangeSelector.tsx rename src/modules/reserve-overview/{InterestRateModelChart.tsx => graphs/InterestRateModelGraph.tsx} (60%) create mode 100644 src/modules/reserve-overview/graphs/InterestRateModelGraphContainer.tsx diff --git a/.eslintignore b/.eslintignore index 5093a29b85..5ba9f5090f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,22 @@ -scripts/ +# General Ignores +.gitignore +.prettierignore +.eslintcache +node_modules/ +public/ + +# Artifacts +.next/ +out/ +src/locales/ + +# IDE +.vscode + +# Specific Ignores +*.svg +*.ico +*.json +*.md +*.log +*.lock diff --git a/.eslintrc.js b/.eslintrc.js index 9e5283b35a..8327a84f6a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,15 +6,15 @@ module.exports = { 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', ], - plugins: ['prettier', 'import', /* 'simple-import-sort', */ '@typescript-eslint'], + plugins: ['prettier', 'import', 'simple-import-sort', '@typescript-eslint'], rules: { '@typescript-eslint/no-empty-interface': 'off', '@typescript-eslint/no-explicit-any': 'error', 'prettier/prettier': 'warn', 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', - // 'simple-import-sort/imports': 'error', - // 'simple-import-sort/exports': 'warn', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'warn', 'import/first': 'error', 'import/newline-after-import': 'error', 'import/no-duplicates': 'error', diff --git a/.github/ISSUE_TEMPLATE/bug-report---.md b/.github/ISSUE_TEMPLATE/bug-report---.md index adf13608e2..06253939a2 100644 --- a/.github/ISSUE_TEMPLATE/bug-report---.md +++ b/.github/ISSUE_TEMPLATE/bug-report---.md @@ -1,10 +1,7 @@ --- name: "Bug report \U0001F41B" about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - +labels: 'new issue, bug' --- **Describe the bug** diff --git a/.github/ISSUE_TEMPLATE/feature-request---.md b/.github/ISSUE_TEMPLATE/feature-request---.md index f5f168c58a..2d2cf9bae6 100644 --- a/.github/ISSUE_TEMPLATE/feature-request---.md +++ b/.github/ISSUE_TEMPLATE/feature-request---.md @@ -1,10 +1,7 @@ --- name: "Feature request \U0001F47B" about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - +labels: 'new issue, feature' --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/actions/analyze-comment/action.yml b/.github/actions/analyze-comment/action.yml index 29c162660c..3f30c3f60c 100644 --- a/.github/actions/analyze-comment/action.yml +++ b/.github/actions/analyze-comment/action.yml @@ -4,9 +4,9 @@ inputs: PULL_REQUEST_NUMBER: description: Pull request id in case it does not exist in the context by default required: false - default: "0" + default: '0' runs: - using: "composite" + using: 'composite' steps: - name: Download PR bundle stats if: github.event.workflow_run.id diff --git a/.github/actions/cypress/action.yml b/.github/actions/cypress/action.yml index 8786e2d25b..fd296972d9 100644 --- a/.github/actions/cypress/action.yml +++ b/.github/actions/cypress/action.yml @@ -14,7 +14,7 @@ inputs: description: yarn command to run test required: true runs: - using: "composite" + using: 'composite' steps: - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # v3.4.1 with: diff --git a/.github/actions/fork-result-comment/action.yml b/.github/actions/fork-result-comment/action.yml index ff473c882d..5f46abeacd 100644 --- a/.github/actions/fork-result-comment/action.yml +++ b/.github/actions/fork-result-comment/action.yml @@ -4,12 +4,12 @@ inputs: PULL_REQUEST_NUMBER: description: Pull request id required: false - default: "0" + default: '0' COMMENT_BODY: description: Text of the comment required: true runs: - using: "composite" + using: 'composite' steps: - name: Find Comment uses: peter-evans/find-comment@1769778a0c5bd330272d749d12c036d65e70d39d # v2.0.0 diff --git a/.github/actions/private-ipfs-pin/action.yml b/.github/actions/private-ipfs-pin/action.yml index 931cba11e2..4033262074 100644 --- a/.github/actions/private-ipfs-pin/action.yml +++ b/.github/actions/private-ipfs-pin/action.yml @@ -1,8 +1,8 @@ name: Pin to private cluster -description: Upload build to ipfs nodes runnning in GKE +description: Upload build to ipfs nodes running in GKE inputs: DOMAIN: - description: Domain to unpin + description: Domain to unpin required: true GCP_SA_KEY: description: '' @@ -16,7 +16,7 @@ inputs: BUILD_PATH: description: path to the build directory required: true - PINATA_HASH: + PINATA_HASH: description: '' required: true runs: diff --git a/.github/actions/upload-artifacts/action.yml b/.github/actions/upload-artifacts/action.yml index 966cb7bbd9..57547d6ec9 100644 --- a/.github/actions/upload-artifacts/action.yml +++ b/.github/actions/upload-artifacts/action.yml @@ -6,15 +6,15 @@ inputs: required: false default: out PR_INFO: - description: shoud this artifact be uploaded + description: should this artifact be uploaded required: false - default: "true" + default: 'true' NEXTJS_BUILD: - description: shoud this artifact be uploaded + description: should this artifact be uploaded required: false - default: "true" + default: 'true' runs: - using: "composite" + using: 'composite' steps: - name: Save PR number if: github.event.number && inputs.PR_INFO == 'true' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..d4425f733d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +## General Changes + +- Fixes XYZ bug +- Adds XYZ feature +- … + +## Developer Notes + +Add any notes here that may be helpful for reviewers. + +## Author Checklist + +- [ ] The base branch is set to `main` +- [ ] The title is using [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) formatting +- [ ] The Github issue has been linked to the PR in the Development section +- [ ] The General Changes section has been filled out +- [ ] Developer Notes have been added (optional) + +## Reviewer Checklist + +- [ ] End-to-end tests are passing without any errors +- [ ] Code style generally follows existing patterns +- [ ] Code changes do not significantly increase the application bundle size +- [ ] New third-party packages, if any, do not introduce potential security threats +- [ ] There are no CI changes, or they have been OK’d by the devops team +- [ ] Code changes have been quality checked in the ephemeral URL +- [ ] QA verification has been completed +- [ ] There are two or more approvals from the core team +- [ ] Squash and merge has been checked diff --git a/.github/workflows/build-fork.yml b/.github/workflows/build-fork.yml index 4a8e2132a1..0f8ee0b92b 100644 --- a/.github/workflows/build-fork.yml +++ b/.github/workflows/build-fork.yml @@ -6,7 +6,7 @@ concurrency: on: pull_request: - branches: ["main"] + branches: ['main'] jobs: build: @@ -21,7 +21,7 @@ jobs: - name: Build App uses: ./.github/actions/build with: - NEXT_PUBLIC_ENV: "prod" + NEXT_PUBLIC_ENV: 'prod' - name: Upload artifacts uses: ./.github/actions/upload-artifacts diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 5cf45f3920..bb4a1fb548 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -84,7 +84,6 @@ jobs: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism @@ -181,7 +180,6 @@ jobs: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism @@ -232,7 +230,6 @@ jobs: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism diff --git a/.github/workflows/i18n-check.yml b/.github/workflows/i18n-check.yml index 406c2e9b9f..01e55df016 100644 --- a/.github/workflows/i18n-check.yml +++ b/.github/workflows/i18n-check.yml @@ -6,9 +6,9 @@ concurrency: on: pull_request: - branches: ["main"] + branches: ['main'] push: - branches: ["main"] + branches: ['main'] jobs: i18n-check: diff --git a/.github/workflows/test-deploy-fork.yml b/.github/workflows/test-deploy-fork.yml index 25fd9cc87b..b6365b24de 100644 --- a/.github/workflows/test-deploy-fork.yml +++ b/.github/workflows/test-deploy-fork.yml @@ -6,8 +6,8 @@ concurrency: on: workflow_run: - workflows: ["Build PR From Fork"] - types: ["completed"] + workflows: ['Build PR From Fork'] + types: ['completed'] jobs: prepare_jobs: @@ -22,7 +22,7 @@ jobs: uses: dawidd6/action-download-artifact@ea71f332a90fd52416b1629b933dcf7e0ccd421d # v2.22.0 with: workflow: build-fork.yml - run_id: "${{ github.event.workflow_run.id }}" + run_id: '${{ github.event.workflow_run.id }}' workflow_conclusion: success name: pr path: ./ @@ -46,7 +46,7 @@ jobs: next_js_analyze: runs-on: ubuntu-latest - needs: ["prepare_jobs"] + needs: ['prepare_jobs'] env: PR_NUMBER: ${{ needs.prepare_jobs.outputs.pr_number }} steps: @@ -56,11 +56,11 @@ jobs: - uses: ./.github/actions/analyze-comment with: - PULL_REQUEST_NUMBER: "${{ env.PR_NUMBER }}" + PULL_REQUEST_NUMBER: '${{ env.PR_NUMBER }}' cypress_smoke_v2: runs-on: ubuntu-latest - needs: ["prepare_jobs"] + needs: ['prepare_jobs'] strategy: fail-fast: false matrix: @@ -82,12 +82,11 @@ jobs: cypress_smoke_v3: runs-on: ubuntu-latest - needs: ["prepare_jobs"] + needs: ['prepare_jobs'] strategy: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism @@ -147,7 +146,7 @@ jobs: cypress_full_v2: runs-on: ubuntu-latest - needs: ["deploy_fork"] + needs: ['deploy_fork'] strategy: fail-fast: false matrix: @@ -169,12 +168,11 @@ jobs: cypress_full_v3: runs-on: ubuntu-latest - needs: ["deploy_fork"] + needs: ['deploy_fork'] strategy: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism @@ -191,7 +189,7 @@ jobs: cypress_additional_v2: runs-on: ubuntu-latest - needs: ["deploy_fork"] + needs: ['deploy_fork'] strategy: fail-fast: false matrix: @@ -212,12 +210,11 @@ jobs: cypress_additional_v3: runs-on: ubuntu-latest - needs: ["deploy_fork"] + needs: ['deploy_fork'] strategy: fail-fast: false matrix: market: - - fantom - polygon - avalanche - optimism @@ -234,7 +231,7 @@ jobs: cypress_app_functionality: runs-on: ubuntu-latest - needs: ["deploy_fork"] + needs: ['deploy_fork'] strategy: fail-fast: false matrix: @@ -251,7 +248,6 @@ jobs: CYPRESS_TENDERLY_PROJECT: ${{ secrets.TENDERLY_PROJECT }} YARN_TEST_COMMAND: npx cypress-repeat run -n 2 --rerun-failed-only --config-file ./cypress/configs/${{ matrix.scoupe }}.config.ts - notify_failure: runs-on: ubuntu-latest if: failure() @@ -293,5 +289,5 @@ jobs: with: PULL_REQUEST_NUMBER: '${{ env.PR_NUMBER }}' COMMENT_BODY: | - ✅ CI run has succeded! - You can find tests and deployment logs at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' + ✅ CI run has succeded! + You can find tests and deployment logs at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' diff --git a/.github/workflows/update-prod-staging.yml b/.github/workflows/update-prod-staging.yml index 11371f166c..36d26bd277 100644 --- a/.github/workflows/update-prod-staging.yml +++ b/.github/workflows/update-prod-staging.yml @@ -84,7 +84,7 @@ jobs: with: PINATA_API_KEY: '${{ secrets.PINATA_API_KEY }}' PINATA_SECRET_KEY: '${{ secrets.PINATA_SECRET_KEY }}' - PIN_ALIAS: "app-aave-staging" + PIN_ALIAS: 'app-aave-staging' BUILD_LOCATION: './out_staging' CID_VERSION: 1 diff --git a/.husky/pre-commit b/.husky/pre-commit index 3c8d0633f5..995462acff 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -3,3 +3,4 @@ npx lint-staged yarn i18n +git add ./src/locales diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..06fd49d9bb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,22 @@ +# General Ignores +.gitignore +.eslintcache +.eslintignore +node_modules/ +public/ + +# Artifacts +.next/ +out/ +src/locales/ + +# IDE +.vscode + +# Specific Ignores +*.svg +*.ico +*.json +*.md +*.log +*.lock diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4533890ca5..37a4cd1110 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,109 +1,156 @@ # Contributing -## Initial setup +We use Github issues for tracking new features, bug fixes, and enhancements related to the Aave Interface. We are currently working on a UI/UX Integrations framework for integrating third-party services and entities within the Aave Interface. Please stay tuned for new information around this. -```sh -# potentially you can user other node versions, but it's only tested on what's currently listed in nvmrc +## Pre-Requisites + +### Install Node + +We are using a Next.js application, which relies on Node. You must have Node installed and set to use the specified version in `.nvmrc`. You can potentially use other versions, but we don’t recommend differing here. + +You can download from the [NodeJS website](https://nodejs.org/en/download/), but we recommend installing nvm and running the following command at root, after cloning or downloading the repo. + +```bash nvm use +``` + +### Install Dependencies + +Download [Yarn Package Manager](https://yarnpkg.com/) and install the dependencies. + +```bash yarn install +``` + +### Setup Environment Variables + +There are a few environment variables that we store for the interface. Before starting development, copy over the environment variables from the provided dummy file to a local copy. + +```bash cp .env.example .env.local ``` -## Running the interface +## Get Up & Running Locally + +Once you’ve completed the pre-requisites above, you should be able to start running the interface locally. There are several variations of running locally. -```sh +### Development Mode + +Development builds are run with a watcher and an open server listening on `localhost:3000`. + +```bash yarn dev ``` -## Build +### Production Mode -```sh -# builds the app for usage on serverless & containered hosting platforms like vercel +Production-based builds are great for usage on serverless & container-based hosting platforms like [Vercel](https://vercel.com) or [Heroku](https://heroku.com). To compile the source files into minified, production-ready asset bundles, run the build command. + +```bash yarn build -# builds the app as a static bundle to be hosted on ipfs -yarn build:static ``` -### Environment +There is also the option to build the application for usages as a static site on platforms like IPFS, AWS, Vercel, etc. This essentially builds the application as above and additionally uses the `next export` command. More information around Next’s export command can be found [here](https://nextjs.org/docs/advanced-features/static-html-export). -```sh -# setting the environment to 'staging' will enable testnet markets, disabling governance, staking, and production markets -NEXT_PUBLIC_ENV=prod -# you can also disable staking & governance by altering -NEXT_PUBLIC_ENABLE_GOVERNANCE=true -NEXT_PUBLIC_ENABLE_STAKING=true +```bash +yarn build:static ``` -## Test +To start the server in production mode and use these bundled, minified assets, run the following command. This will start the web server listening on `localhost:3000`. -The integration test suite runs against tenderly forks of various networks. To setup the local environment you need to add environment variables accordingly: - -```sh -TENDERLY_KEY= -TENDERLY_ACCOUNT= -TENDERLY_PROJECT= +```bash +yarn start ``` -For setup env with staking page (to get positive result for staking test coverage), need to execute: +You can also serve up the static site assets with the following command, which can be viewable on `localhost:3000`. + +```bash +yarn serve:static ``` -cp .env.example .env.local + +### Test Mode + +The integration test suite runs against [Tenderly](https://tenderly.co/) forks of various networks. To setup the local environment, you’ll first need to create an account and obtain an access key. Then fill in the three environment variables accordingly: + +```bash +TENDERLY_KEY= +TENDERLY_ACCOUNT= +TENDERLY_PROJECT= ``` -For running the integration test suite you need to run the app. You can either run again `yarn dev` which will be a bit more resource intensive as the app will be build on the fly or run against a static build via `yarn build:static` & `yarn serve:static`. +For running the integration test suite, you’ll need to have the application running locally in a separate terminal. You may choose to either run it in development mode via `yarn dev` or against a static production build via `yarn build:static` & `yarn serve:static`. The caveat to running in development mode is that it will be more resource-intensive, since the application will be built on the fly. -```sh +```bash # open interactive cypress test suite yarn test:open + # run all tests in headless mode yarn test:headless ``` -## Run on a fork +## Environment Variables + +Some environment variables can be adjusted to suite your needs during development, depending on the scenarios you want to test or build in. + +```bash +# setting the environment to 'staging' will enable testnet markets, disabling governance, staking, and production markets +NEXT_PUBLIC_ENV=prod + +# you can also disable staking & governance by altering +NEXT_PUBLIC_ENABLE_GOVERNANCE=true +NEXT_PUBLIC_ENABLE_STAKING=true +``` -You can run the ui against a forked network similar to what the tests do which allows you to play around on the ui without spending actual funds. -To enable forks in the ui, you have to run the following commands in console. +## Running Against a Chain Fork + +You can run the UI locally against a forked chain network, similar to what the tests do with Tenderly. This will allow you to build and interact with the UI without spending actual funds. This is very useful for testing many protocol scenarios. + +First, you’ll need to create a fork of a network, which can be done with a few different tools. We suggest using the one created by Bored Ghost Labs, where you can follow the steps and get set up [here](https://github.com/bgd-labs/aave-tenderly-cli). + +Second, you’ll need to tell the local application which fork to run against. The easiest way to do this is to copy/paste the following statements. With the application running locally, open up the console in the browser and copy/paste the following with the appropriate values. ```js localStorage.setItem('forkEnabled', 'true'); -localStorage.setItem('forkBaseChainId', 1); // the networkId you are forking -localStorage.setItem('forkNetworkId', '3030'); // the networkId on the fork -localStorage.setItem('forkRPCUrl', ); +localStorage.setItem('forkBaseChainId', ); // the ID for the chain you are forking, in numeric format +localStorage.setItem('forkNetworkId', '3030'); // the ID of the new forked network +localStorage.setItem('forkRPCUrl', ); ``` -As localeStorage is not observed you need to **reload** after setting the parameters. -Now the market selection should show forked markets for all the markets that run on `forkBaseChainId`. -To do actual transactions on the fork you need to setup your wallet to use the same `rpcurl` you provided as `forkRPCUrl`. +Since `localStorage` changes are not not observed, _you’ll need to reload after setting the parameters_. After reloading, the market selection should show forked markets for all the markets that run on `forkBaseChainId`. To make actual transactions on the fork, you’ll need to setup your wallet to use the same `rpcUrl` you provided as `forkRPCUrl`. This will require you to setup your wallet by adding in the new fork network and connecting to the app with it. -## Token addition +If you are using MetaMask, make sure to configure the Tenderly fork RPC URL into a new network configuration. Give it a network name, and you'll want to use the same values that you copied into `localStorage` for the other fields. See below as an example: -To add a new token to the app, all you have to do is adding a svg token icon inside `public/icons/tokens` please make sure the svg name equals the `lowercase` onchain `symbol`. -If you want a custom name to appear alongside the icon you can alter `src/ui-config/reservePatches.ts` accordingly. +![MetaMask Forked Network Setup](/public/fork-config-example.png) -## Translations +Once you have both copied over the values into `localStorage` and have saved the new network configuration in MetaMask (or any other browser wallet), switch to the network in your wallet. -Aave uses [Crowdin](https://crowdin.com/project/aave-interface) for translation management. +Next, reload the page. The new forked network should appear in the dropdown list for markets in the application. -Feel free to reach out to us on discord to become a translator! +Finally, switch to the market pertaining to the fork in the dropdown list. Now you are able to interact with the Aave Protocol via the UI without spending any real funds! -We only update strings within the app. Everything else is downloaded from crowdin. +__NOTE:__ _Always double check the selected network in your wallet provider to make sure transactions are executed only on the fork network_. -[Crowdin Docs](https://support.crowdin.com/enterprise/cli/) for installation of the cli -To upload strings +## Token Additions -```sh +To add a new token to the app, the process is pretty simple. All you’ll need to do is add a new token icon SVG to the `public/icons/tokens` directory. The only requirement is that the name of the SVG file is equal to the asset’s `symbol` in all lowercase. If you want a custom name to appear alongside the icon, you can alter `src/ui-config/reservePatches.ts` accordingly. -crowdin upload sources +## Translations -``` +Aave uses [Crowdin](https://crowdin.com/) for translation management and internationalization. We only update strings within the app. Everything else is downloaded from Crowdin. For more information, such as installing their CLI tool, read their [documentation](https://developer.crowdin.com/api/v2/). + +To upload strings: -To download strings +```bash +crowdin upload sources +``` -```sh +To download strings: +```bash crowdin download - ``` -To add a new language first start an issue to check for public interest. -If the community decides to go forward with your preferred language follow [this pr](https://github.com/aave/interface/pull/447#issue-1165545965) to add support for a new language. +If you would like to add support for a new language across the app, please [open up an issue](https://github.com/aave/interface/issues/new/choose) and add the `i18n` label to it. This helps us categorize, and it will also help to gauge public interest for the new language. If the community decides to go forward with your preferred language, you may open up a pull request. Please follow the template of [this PR](https://github.com/aave/interface/pull/447#issue-1165545965). + +**Feel free to reach out to us on [Discord](https://discord.gg/aave) to become a translator!** diff --git a/cypress/configs/settings.config.ts b/cypress/configs/settings.config.ts index 733f2ebc1b..724d5e2f1f 100644 --- a/cypress/configs/settings.config.ts +++ b/cypress/configs/settings.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from './base.cypress'; const folder = `./cypress/e2e/2-settings/`; diff --git a/cypress/configs/v2-markets/amm-v2-full.config.ts b/cypress/configs/v2-markets/amm-v2-full.config.ts index 5b14d4e8df..8ff0031332 100644 --- a/cypress/configs/v2-markets/amm-v2-full.config.ts +++ b/cypress/configs/v2-markets/amm-v2-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/1-amm-v2-market/`; diff --git a/cypress/configs/v2-markets/amm-v2-smoke.config.ts b/cypress/configs/v2-markets/amm-v2-smoke.config.ts index a1910e5d70..e090262645 100644 --- a/cypress/configs/v2-markets/amm-v2-smoke.config.ts +++ b/cypress/configs/v2-markets/amm-v2-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/1-amm-v2-market/`; diff --git a/cypress/configs/v2-markets/avalanche-v2-additional.config.ts b/cypress/configs/v2-markets/avalanche-v2-additional.config.ts index bbf8e26480..f7e4bdd735 100644 --- a/cypress/configs/v2-markets/avalanche-v2-additional.config.ts +++ b/cypress/configs/v2-markets/avalanche-v2-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/3-avalanche-v2-market/`; diff --git a/cypress/configs/v2-markets/avalanche-v2-full.config.ts b/cypress/configs/v2-markets/avalanche-v2-full.config.ts index d19d2055cf..d8fd9061b9 100644 --- a/cypress/configs/v2-markets/avalanche-v2-full.config.ts +++ b/cypress/configs/v2-markets/avalanche-v2-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/3-avalanche-v2-market/`; diff --git a/cypress/configs/v2-markets/avalanche-v2-smoke.config.ts b/cypress/configs/v2-markets/avalanche-v2-smoke.config.ts index 6bf8be9f0f..fa4fb00a31 100644 --- a/cypress/configs/v2-markets/avalanche-v2-smoke.config.ts +++ b/cypress/configs/v2-markets/avalanche-v2-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/3-avalanche-v2-market/`; diff --git a/cypress/configs/v2-markets/ethereum-v2-additional.config.ts b/cypress/configs/v2-markets/ethereum-v2-additional.config.ts index da2a805f9e..01d0514dff 100644 --- a/cypress/configs/v2-markets/ethereum-v2-additional.config.ts +++ b/cypress/configs/v2-markets/ethereum-v2-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/0-main-v2-market/`; diff --git a/cypress/configs/v2-markets/ethereum-v2-full.config.ts b/cypress/configs/v2-markets/ethereum-v2-full.config.ts index 256344c507..cfb35497ca 100644 --- a/cypress/configs/v2-markets/ethereum-v2-full.config.ts +++ b/cypress/configs/v2-markets/ethereum-v2-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/0-main-v2-market/`; diff --git a/cypress/configs/v2-markets/ethereum-v2-smoke.config.ts b/cypress/configs/v2-markets/ethereum-v2-smoke.config.ts index eb9351bd22..644df84ec2 100644 --- a/cypress/configs/v2-markets/ethereum-v2-smoke.config.ts +++ b/cypress/configs/v2-markets/ethereum-v2-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/0-v2-markets/0-main-v2-market/`; diff --git a/cypress/configs/v2-markets/polygon-v2-additional.config.ts b/cypress/configs/v2-markets/polygon-v2-additional.config.ts index 5ea10bdcbe..a596a74e7e 100644 --- a/cypress/configs/v2-markets/polygon-v2-additional.config.ts +++ b/cypress/configs/v2-markets/polygon-v2-additional.config.ts @@ -1,6 +1,7 @@ -import { defaultConfig } from '../base.cypress'; import { defineConfig } from 'cypress'; +import { defaultConfig } from '../base.cypress'; + const folder = `./cypress/e2e/0-v2-markets/2-polygon-v2-market/`; export default defineConfig({ diff --git a/cypress/configs/v2-markets/polygon-v2-full.config.ts b/cypress/configs/v2-markets/polygon-v2-full.config.ts index 137120891c..1dcc71e517 100644 --- a/cypress/configs/v2-markets/polygon-v2-full.config.ts +++ b/cypress/configs/v2-markets/polygon-v2-full.config.ts @@ -1,6 +1,7 @@ -import { defaultConfig } from '../base.cypress'; import { defineConfig } from 'cypress'; +import { defaultConfig } from '../base.cypress'; + const folder = `./cypress/e2e/0-v2-markets/2-polygon-v2-market/`; export default defineConfig({ diff --git a/cypress/configs/v2-markets/polygon-v2-smoke.config.ts b/cypress/configs/v2-markets/polygon-v2-smoke.config.ts index 891ace02e7..d525204308 100644 --- a/cypress/configs/v2-markets/polygon-v2-smoke.config.ts +++ b/cypress/configs/v2-markets/polygon-v2-smoke.config.ts @@ -1,6 +1,7 @@ -import { defaultConfig } from '../base.cypress'; import { defineConfig } from 'cypress'; +import { defaultConfig } from '../base.cypress'; + const folder = `./cypress/e2e/0-v2-markets/2-polygon-v2-market/`; export default defineConfig({ diff --git a/cypress/configs/v3-markets/arbitrum-v3-additional.config.ts b/cypress/configs/v3-markets/arbitrum-v3-additional.config.ts index bee5dc391d..fcfb8fdb6a 100644 --- a/cypress/configs/v3-markets/arbitrum-v3-additional.config.ts +++ b/cypress/configs/v3-markets/arbitrum-v3-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/1-arbitrum-v3-market/`; diff --git a/cypress/configs/v3-markets/arbitrum-v3-full.config.ts b/cypress/configs/v3-markets/arbitrum-v3-full.config.ts index 90d6b171a9..e915f418a8 100644 --- a/cypress/configs/v3-markets/arbitrum-v3-full.config.ts +++ b/cypress/configs/v3-markets/arbitrum-v3-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/1-arbitrum-v3-market/`; diff --git a/cypress/configs/v3-markets/arbitrum-v3-smoke.config.ts b/cypress/configs/v3-markets/arbitrum-v3-smoke.config.ts index 20b02c8469..cb51b05512 100644 --- a/cypress/configs/v3-markets/arbitrum-v3-smoke.config.ts +++ b/cypress/configs/v3-markets/arbitrum-v3-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/1-arbitrum-v3-market/`; diff --git a/cypress/configs/v3-markets/avalanche-v3-additional.config.ts b/cypress/configs/v3-markets/avalanche-v3-additional.config.ts index f0ec57c11a..acfc5e0f89 100644 --- a/cypress/configs/v3-markets/avalanche-v3-additional.config.ts +++ b/cypress/configs/v3-markets/avalanche-v3-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/2-avalanche-v3-market/`; diff --git a/cypress/configs/v3-markets/avalanche-v3-full.config.ts b/cypress/configs/v3-markets/avalanche-v3-full.config.ts index a8721905a7..f2d2dfaa74 100644 --- a/cypress/configs/v3-markets/avalanche-v3-full.config.ts +++ b/cypress/configs/v3-markets/avalanche-v3-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/2-avalanche-v3-market/`; diff --git a/cypress/configs/v3-markets/avalanche-v3-smoke.config.ts b/cypress/configs/v3-markets/avalanche-v3-smoke.config.ts index 8ec99c62cc..ec6a53ae59 100644 --- a/cypress/configs/v3-markets/avalanche-v3-smoke.config.ts +++ b/cypress/configs/v3-markets/avalanche-v3-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/2-avalanche-v3-market/`; diff --git a/cypress/configs/v3-markets/fantom-v3-additional.config.ts b/cypress/configs/v3-markets/fantom-v3-additional.config.ts index 925aca37c7..4480e0215d 100644 --- a/cypress/configs/v3-markets/fantom-v3-additional.config.ts +++ b/cypress/configs/v3-markets/fantom-v3-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/5-fantom-v3-market/`; diff --git a/cypress/configs/v3-markets/fantom-v3-full.config.ts b/cypress/configs/v3-markets/fantom-v3-full.config.ts index d83db5c0c9..fc9e8cbb5c 100644 --- a/cypress/configs/v3-markets/fantom-v3-full.config.ts +++ b/cypress/configs/v3-markets/fantom-v3-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/5-fantom-v3-market/`; diff --git a/cypress/configs/v3-markets/fantom-v3-smoke.config.ts b/cypress/configs/v3-markets/fantom-v3-smoke.config.ts index 7aadd7210c..ef1d66ea01 100644 --- a/cypress/configs/v3-markets/fantom-v3-smoke.config.ts +++ b/cypress/configs/v3-markets/fantom-v3-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/5-fantom-v3-market/`; diff --git a/cypress/configs/v3-markets/optimism-v3-additional.config.ts b/cypress/configs/v3-markets/optimism-v3-additional.config.ts index cbc5f8f2e5..b675e73668 100644 --- a/cypress/configs/v3-markets/optimism-v3-additional.config.ts +++ b/cypress/configs/v3-markets/optimism-v3-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/4-optimism-v3-market/`; diff --git a/cypress/configs/v3-markets/optimism-v3-full.config.ts b/cypress/configs/v3-markets/optimism-v3-full.config.ts index 2b82f1ae32..9c300c7257 100644 --- a/cypress/configs/v3-markets/optimism-v3-full.config.ts +++ b/cypress/configs/v3-markets/optimism-v3-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/4-optimism-v3-market/`; diff --git a/cypress/configs/v3-markets/optimism-v3-smoke.config.ts b/cypress/configs/v3-markets/optimism-v3-smoke.config.ts index b80fa0b03d..373e643573 100644 --- a/cypress/configs/v3-markets/optimism-v3-smoke.config.ts +++ b/cypress/configs/v3-markets/optimism-v3-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/4-optimism-v3-market/`; diff --git a/cypress/configs/v3-markets/polygon-v3-additional.config.ts b/cypress/configs/v3-markets/polygon-v3-additional.config.ts index 148db0240c..0d15c6809d 100644 --- a/cypress/configs/v3-markets/polygon-v3-additional.config.ts +++ b/cypress/configs/v3-markets/polygon-v3-additional.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/3-polygon-v3-market/`; diff --git a/cypress/configs/v3-markets/polygon-v3-full.config.ts b/cypress/configs/v3-markets/polygon-v3-full.config.ts index 38e4ca3ff8..87a3a9d099 100644 --- a/cypress/configs/v3-markets/polygon-v3-full.config.ts +++ b/cypress/configs/v3-markets/polygon-v3-full.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/3-polygon-v3-market/`; diff --git a/cypress/configs/v3-markets/polygon-v3-smoke.config.ts b/cypress/configs/v3-markets/polygon-v3-smoke.config.ts index 492ae8828e..04fc0ba96c 100644 --- a/cypress/configs/v3-markets/polygon-v3-smoke.config.ts +++ b/cypress/configs/v3-markets/polygon-v3-smoke.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress'; + import { defaultConfig } from '../base.cypress'; const folder = `./cypress/e2e/1-v3-markets/3-polygon-v3-market/`; diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/bat.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/bat.aave-v2.cy.ts index 30e9737edb..13e1160c25 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/bat.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/bat.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/dai.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/dai.aave-v2.cy.ts index dd5e61cf3c..1eb495a42c 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/dai.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/dai.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/eth.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/eth.aave-v2.cy.ts index aeda27d9c6..c352cd3e36 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/eth.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/eth.aave-v2.cy.ts @@ -1,12 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, withdraw, changeCollateral } from '../../../../support/steps/main.steps'; +import { + borrow, + changeCollateral, + changeCollateralNegative, + repay, + supply, + withdraw, +} from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { @@ -70,14 +77,13 @@ const testData = { collateralType: constants.collateralType.isCollateral, isCollateral: true, }, - // SKIP while eth borrow blocked - // { - // type: constants.dashboardTypes.borrow, - // assetName: assets.aaveMarket.ETH.shortName, - // wrapped: assets.aaveMarket.ETH.wrapped, - // amount: 0.03, - // apyType: constants.borrowAPYType.variable, - // }, + { + type: constants.dashboardTypes.borrow, + assetName: assets.aaveMarket.ETH.shortName, + wrapped: assets.aaveMarket.ETH.wrapped, + amount: 0.03, + apyType: constants.borrowAPYType.variable, + }, ], }, }; @@ -92,10 +98,9 @@ describe('ETH INTEGRATION SPEC, AAVE V2 MARKET', () => { borrowsUnavailable(skipTestState); changeCollateral(testData.testCases.collateral.switchOn, skipTestState, false); }); - // SKIP, while eth borrow blocked - // borrow(testData.testCases.borrow, skipTestState, true); - // changeCollateralNegative(testData.testCases.collateral.switchNegative, skipTestState, false); - // repay(testData.testCases.repay, skipTestState, false); + borrow(testData.testCases.borrow, skipTestState, true); + changeCollateralNegative(testData.testCases.collateral.switchNegative, skipTestState, false); + repay(testData.testCases.repay, skipTestState, false); testData.testCases.withdraw.forEach((withdrawCase) => { withdraw(withdrawCase, skipTestState, false); }); diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mana.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mana.aave-v2.cy.ts index 8f41f23e05..625a70f12d 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mana.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mana.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mkr.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mkr.aave-v2.cy.ts index 80df395376..bb0e5356b2 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mkr.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/mkr.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/ren.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/ren.aave-v2.cy.ts index 3e405ab336..df91b694ee 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/ren.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/ren.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/tusd.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/tusd.aave-v2.cy.ts index 833670afaa..a932392319 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/tusd.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/tusd.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdc.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdc.aave-v2.cy.ts index 4228ba36fd..c5d22718f7 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdc.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdc.aave-v2.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdt.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdt.aave-v2.cy.ts index 75fe5ffda4..90779a4569 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdt.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/0-assets/usdt.aave-v2.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; import { borrow, - repay, - withdraw, changeBorrowType, + repay, supply, + withdraw, } from '../../../../support/steps/main.steps'; import { changeBorrowTypeBlocked, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositETH: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/critical-conditions.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/critical-conditions.aave-v2.cy.ts index 22a94dd0da..2305b018e6 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/critical-conditions.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/critical-conditions.aave-v2.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/reward.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/reward.aave-v2.cy.ts index bf9d2d25c1..6285dcf674 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/reward.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/reward.aave-v2.cy.ts @@ -1,8 +1,8 @@ +import assets from '../../../fixtures/assets.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; import { claimReward, supply, withdraw } from '../../../support/steps/main.steps'; import { rewardIsNotAvailable } from '../../../support/steps/verification.steps'; -import assets from '../../../fixtures/assets.json'; const testData = { deposit: { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/stake.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/stake.aave-v2.cy.ts index 7925e1ae8c..d34e3c3ecc 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/stake.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/stake.aave-v2.cy.ts @@ -1,6 +1,6 @@ -import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; -import { doCloseModal, doConfirm, setAmount } from '../../../support/steps/actions.steps'; import assets from '../../../fixtures/assets.json'; +import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; +import { doCloseModal } from '../../../support/steps/main.steps'; const testCases = [ { @@ -46,14 +46,8 @@ testCases.forEach( .click(); }); it(`Set amount`, () => { - setAmount({ - amount: testCase.amount, - max: false, - }); - doConfirm({ - hasApproval: false, - actionName: 'Stake', - }); + cy.setAmount(testCase.amount, false); + cy.doConfirm(false, 'Stake'); }); doCloseModal(); it(`Check staked amount`, () => { @@ -77,10 +71,7 @@ testCases.forEach( cy.get(`[data-cy="claimBtn_${testCase.assetName.shortName}"]`).click(); }); it(`Confirm`, () => { - doConfirm({ - hasApproval: true, - actionName: `STAKE ${testCase.assetName.shortName}`, - }); + cy.doConfirm(true, `STAKE ${testCase.assetName.shortName}`); }); doCloseModal(); }); @@ -90,10 +81,7 @@ testCases.forEach( }); it(`Confirm`, () => { cy.get(`[data-cy="cooldownAcceptCheckbox"]`).click(); - doConfirm({ - hasApproval: true, - actionName: 'Cooldown to unstake', - }); + cy.doConfirm(true, 'Cooldown to unstake'); }); doCloseModal(); it(`Check cooldown activation`, () => { diff --git a/cypress/e2e/0-v2-markets/0-main-v2-market/swap.aave-v2.cy.ts b/cypress/e2e/0-v2-markets/0-main-v2-market/swap.aave-v2.cy.ts index 1bd4a72aae..374e65c6ee 100644 --- a/cypress/e2e/0-v2-markets/0-main-v2-market/swap.aave-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/0-main-v2-market/swap.aave-v2.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/dai.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/dai.amm-v2.cy.ts index cadefc6294..ce98307020 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/dai.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/dai.amm-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/eth.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/eth.amm-v2.cy.ts index e1c77ad03e..215ade2abd 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/eth.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/eth.amm-v2.cy.ts @@ -1,12 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, withdraw, changeCollateral } from '../../../../support/steps/main.steps'; +import { + borrow, + changeCollateral, + changeCollateralNegative, + repay, + supply, + withdraw, +} from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { @@ -70,14 +77,13 @@ const testData = { collateralType: constants.collateralType.isCollateral, isCollateral: true, }, - // SKIP while eth borrow blocked - // { - // type: constants.dashboardTypes.borrow, - // assetName: assets.ammMarket.ETH.shortName, - // wrapped: assets.ammMarket.ETH.wrapped, - // amount: 0.03, - // apyType: constants.borrowAPYType.variable, - // }, + { + type: constants.dashboardTypes.borrow, + assetName: assets.ammMarket.ETH.shortName, + wrapped: assets.ammMarket.ETH.wrapped, + amount: 0.03, + apyType: constants.borrowAPYType.variable, + }, ], }, }; @@ -92,10 +98,9 @@ describe('ETH INTEGRATION SPEC, AMM V2 MARKET', () => { borrowsUnavailable(skipTestState); changeCollateral(testData.testCases.collateral.switchOn, skipTestState, false); }); - // SKIP while eth borrow blocked - // borrow(testData.testCases.borrow, skipTestState, true); - // changeCollateralNegative(testData.testCases.collateral.switchNegative, skipTestState, false); - // repay(testData.testCases.repay, skipTestState, false); + borrow(testData.testCases.borrow, skipTestState, true); + changeCollateralNegative(testData.testCases.collateral.switchNegative, skipTestState, false); + repay(testData.testCases.repay, skipTestState, false); testData.testCases.withdraw.forEach((withdrawCase) => { withdraw(withdrawCase, skipTestState, false); }); diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdc.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdc.amm-v2.cy.ts index e80734cc22..206cf3c5a1 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdc.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdc.amm-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdt.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdt.amm-v2.cy.ts index c7a66f3e71..62bf728334 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdt.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/usdt.amm-v2.cy.ts @@ -1,13 +1,13 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { changeBorrowTypeBlocked, dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/wbtc.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/wbtc.amm-v2.cy.ts index 33a9e96bae..f9bcb85245 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/wbtc.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/0-assets/wbtc.amm-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyMainnetFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/1-amm-v2-market/critical-conditions.amm-v2.cy.ts b/cypress/e2e/0-v2-markets/1-amm-v2-market/critical-conditions.amm-v2.cy.ts index 566e603050..aaf65b697d 100644 --- a/cypress/e2e/0-v2-markets/1-amm-v2-market/critical-conditions.amm-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/1-amm-v2-market/critical-conditions.amm-v2.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyMainnetFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/bal.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/bal.polygon-v2.cy.ts index 172d84b60d..96f625165b 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/bal.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/bal.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/crv.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/crv.polygon-v2.cy.ts index 8cf417d85c..8cdee08db0 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/crv.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/crv.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dai.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dai.polygon-v2.cy.ts index 5de87fe022..bec093abdf 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dai.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dai.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dpi.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dpi.polygon-v2.cy.ts index 070025ed14..743693b6f9 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dpi.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/dpi.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/ghst.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/ghst.polygon-v2.cy.ts index 399b14dfc0..af3c3c9de1 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/ghst.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/ghst.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/link.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/link.polygon-v2.cy.ts index 37d1cb137e..4fe0472db4 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/link.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/link.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/matic.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/matic.polygon-v2.cy.ts index 358b28406a..bec3370721 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/matic.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/matic.polygon-v2.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/sushi.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/sushi.polygon-v2.cy.ts index 5a8db0a0e6..7a7483ef60 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/sushi.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/sushi.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdc.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdc.polygon-v2.cy.ts index 7bb26e43b8..edbcbfc6e1 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdc.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdc.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdt.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdt.polygon-v2.cy.ts index d619712941..bda9fd6c8e 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdt.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/usdt.polygon-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { changeBorrowTypeBlocked, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/wbtc.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/wbtc.polygon-v2.cy.ts index 361a45a4b1..2df8ad9dd5 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/wbtc.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/wbtc.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/weth.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/weth.polygon-v2.cy.ts index 9c38037d42..23f7ea7e64 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/weth.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/0-assets/weth.polygon-v2.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/critical-conditions.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/critical-conditions.polygon-v2.cy.ts index 041ce6b187..253e8a69be 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/critical-conditions.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/critical-conditions.polygon-v2.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/0-v2-markets/2-polygon-v2-market/swap.polygon-v2.cy.ts b/cypress/e2e/0-v2-markets/2-polygon-v2-market/swap.polygon-v2.cy.ts index 639148d712..1d29711f3b 100644 --- a/cypress/e2e/0-v2-markets/2-polygon-v2-market/swap.polygon-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/2-polygon-v2-market/swap.polygon-v2.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/avax.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/avax.avalanche-v2.cy.ts index a0586bf1cf..bf39a8f908 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/avax.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/avax.avalanche-v2.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/dai.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/dai.avalanche-v2.cy.ts index 4dd48e719b..bffd07c5f9 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/dai.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/dai.avalanche-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdc.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdc.avalanche-v2.cy.ts index 57bf51e0fa..44ef49fec1 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdc.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdc.avalanche-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdt.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdt.avalanche-v2.cy.ts index 82d1e6ea9c..792537669f 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdt.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/usdt.avalanche-v2.cy.ts @@ -1,13 +1,13 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { changeBorrowTypeBlocked, dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/wbtc.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/wbtc.avalanche-v2.cy.ts index 383aced091..10a2089fff 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/wbtc.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/wbtc.avalanche-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/weth.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/weth.avalanche-v2.cy.ts index c4dbb29ff1..dad5fe7b6e 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/weth.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/0-assets/weth.avalanche-v2.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/critical-conditions.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/critical-conditions.avalanche-v2.cy.ts index 03e4a13f87..f1d75f22cf 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/critical-conditions.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/critical-conditions.avalanche-v2.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/reward.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/reward.avalanche-v2.cy.ts index 0d15e4b790..b500af555e 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/reward.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/reward.avalanche-v2.cy.ts @@ -1,8 +1,8 @@ +import assets from '../../../fixtures/assets.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; import { claimReward, supply, withdraw } from '../../../support/steps/main.steps'; import { rewardIsNotAvailable } from '../../../support/steps/verification.steps'; -import assets from '../../../fixtures/assets.json'; const testData = { deposit: { diff --git a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/swap.avalanche-v2.cy.ts b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/swap.avalanche-v2.cy.ts index 26b180d64b..052f3ae5ba 100644 --- a/cypress/e2e/0-v2-markets/3-avalanche-v2-market/swap.avalanche-v2.cy.ts +++ b/cypress/e2e/0-v2-markets/3-avalanche-v2-market/swap.avalanche-v2.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/dai.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/dai.arbitrum-v3.cy.ts index 0270434b54..c7087a3aed 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/dai.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/dai.arbitrum-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/eth.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/eth.arbitrum-v3.cy.ts index e781d7927a..3d6c4d3066 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/eth.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/eth.arbitrum-v3.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/link.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/link.arbitrum-v3.cy.ts index 908eb4299f..88f0216b88 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/link.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/link.arbitrum-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdc.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdc.arbitrum-v3.cy.ts index 4cacc4c72c..01592b4272 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdc.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdc.arbitrum-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdt.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdt.arbitrum-v3.cy.ts index 291340dd11..6d8528bff4 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdt.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/usdt.arbitrum-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/wbtc.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/wbtc.arbitrum-v3.cy.ts index 38cb5a8361..3ca5852ec4 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/wbtc.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/0-assets/wbtc.arbitrum-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyArbitrumFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/critical-conditions.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/critical-conditions.arbitrum-v3.cy.ts index ca8a377c60..13b80a1451 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/critical-conditions.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/critical-conditions.arbitrum-v3.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyArbitrumFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyArbitrumFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/e-mode.arbitrum-v3.cy.ts b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/e-mode.arbitrum-v3.cy.ts index 3c817cce64..09abd95b88 100644 --- a/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/e-mode.arbitrum-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/1-arbitrum-v3-market/e-mode.arbitrum-v3.cy.ts @@ -1,11 +1,11 @@ -import { configEnvWithTenderlyArbitrumFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyArbitrumFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply } from '../../../support/steps/main.steps'; import { - checkDashboardHealthFactor, borrowsAvailable, + checkDashboardHealthFactor, } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/avax.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/avax.avalanche-v3.cy.ts index 210366ebe9..19d8ca02c0 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/avax.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/avax.avalanche-v3.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/dai.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/dai.avalanche-v3.cy.ts index db3845561d..a474a50bb8 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/dai.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/dai.avalanche-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/link.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/link.avalanche-v3.cy.ts index f3f6c42a6c..99211f3209 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/link.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/link.avalanche-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdc.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdc.avalanche-v3.cy.ts index a6504cb500..eb69809d30 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdc.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdc.avalanche-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdt.avalacnhe-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdt.avalacnhe-v3.cy.ts index 38c12a4abc..e9cc302a5a 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdt.avalacnhe-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/usdt.avalacnhe-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/wbtc.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/wbtc.avalanche-v3.cy.ts index 040f4f0e35..910ebbc8d4 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/wbtc.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/wbtc.avalanche-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/weth.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/weth.avalanche-v3.cy.ts index 2a002ba4ef..2876f0619f 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/weth.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/0-assets/weth.avalanche-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/critical-conditions.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/critical-conditions.avalanche-v3.cy.ts index d338b40836..81c77fae03 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/critical-conditions.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/critical-conditions.avalanche-v3.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/e-mode.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/e-mode.avalanche-v3.cy.ts index 53f4fe6e07..9a05aadb7f 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/e-mode.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/e-mode.avalanche-v3.cy.ts @@ -1,13 +1,12 @@ -import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply } from '../../../support/steps/main.steps'; import { - checkDashboardHealthFactor, borrowsAvailable, + checkDashboardHealthFactor, checkEmodeActivatingDisabled, - verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { @@ -42,6 +41,8 @@ const testData = { assets.avalancheV3Market.DAI, // assets.avalancheV3Market.USDT, assets.avalancheV3Market.USDC, + assets.avalancheV3Market.FRAX, + assets.avalancheV3Market.MAI, ], }, }; @@ -64,7 +65,7 @@ describe('E-MODE SPEC, AVALANCHE V3 MARKET', () => { ); checkDashboardHealthFactor({ valueFrom: 1.07, valueTo: 1000 }, skipTestState); borrowsAvailable(skipTestState); - verifyCountOfBorrowAssets({ assets: testData.testCases.eModeAssets }, skipTestState); + // verifyCountOfBorrowAssets({ assets: testData.testCases.eModeAssets }, skipTestState); temporary skip this step }); describe('Turn off E-mode and verify decrease of health factor', () => { emodeActivating({ turnOn: false, multipleEmodes: true }, skipTestState, true); diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/isolated-mode.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/isolated-mode.avalanche-v3.cy.ts index 25a0e73732..fb5b0efecf 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/isolated-mode.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/isolated-mode.avalanche-v3.cy.ts @@ -1,14 +1,14 @@ -import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, swap, repay, changeCollateral } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; +import { borrow, changeCollateral, repay, supply, swap } from '../../../support/steps/main.steps'; import { - verifyCountOfBorrowAssets, + borrowsUnavailable, dashboardAssetValuesVerification, switchCollateralBlocked, - borrowsUnavailable, switchCollateralBlockedInModal, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/reward.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/reward.avalanche-v3.cy.ts index 85fcafbda7..0e0a756ee4 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/reward.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/reward.avalanche-v3.cy.ts @@ -1,8 +1,8 @@ +import assets from '../../../fixtures/assets.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; import { claimReward, supply, withdraw } from '../../../support/steps/main.steps'; import { rewardIsNotAvailable } from '../../../support/steps/verification.steps'; -import assets from '../../../fixtures/assets.json'; const testData = { deposit: { diff --git a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/swap.avalanche-v3.cy.ts b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/swap.avalanche-v3.cy.ts index b6ac4ca276..43a6be5236 100644 --- a/cypress/e2e/1-v3-markets/2-avalanche-v3-market/swap.avalanche-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/2-avalanche-v3-market/swap.avalanche-v3.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyAvalancheFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/dai.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/dai.polygon-v3.cy.ts index 65ed41005e..82cebbcce3 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/dai.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/dai.polygon-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/eurs.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/eurs.polygon-v3.cy.ts index 0a160bf853..0101a2b831 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/eurs.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/eurs.polygon-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/link.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/link.polygon-v3.cy.ts index 33f21068f8..48e3176798 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/link.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/link.polygon-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/matic.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/matic.polygon-v3.cy.ts index b220133aa8..445da53fef 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/matic.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/matic.polygon-v3.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdc.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdc.polygon-v3.cy.ts index 59ad0b35fa..279af0965a 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdc.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdc.polygon-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdt.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdt.polygon-v3.cy.ts index dff2cfaf28..fdce66bf7c 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdt.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/usdt.polygon-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/wbtc.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/wbtc.polygon-v3.cy.ts index 2e53b9fc43..e8819aa4e1 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/wbtc.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/wbtc.polygon-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/weth.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/weth.polygon-v3.cy.ts index f702a55a7b..44337a1ee6 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/weth.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/0-assets/weth.polygon-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/critical-conditions.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/critical-conditions.polygon-v3.cy.ts index 01eff894e1..7efe15add0 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/critical-conditions.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/critical-conditions.polygon-v3.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/e-mode.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/e-mode.polygon-v3.cy.ts index 2119913b2e..7edac00438 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/e-mode.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/e-mode.polygon-v3.cy.ts @@ -1,13 +1,13 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply } from '../../../support/steps/main.steps'; import { - checkDashboardHealthFactor, borrowsAvailable, - verifyCountOfBorrowAssets, + checkDashboardHealthFactor, checkEmodeActivatingDisabled, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { @@ -61,17 +61,25 @@ describe('E-MODE SPEC, POLYGON V3 MARKET', () => { checkDashboardHealthFactor({ valueFrom: 1.0, valueTo: 1.07 }, skipTestState); }); describe('Turn on E-Mode and verify increase of health factor', () => { - emodeActivating({ turnOn: true }, skipTestState, true); + emodeActivating( + { turnOn: true, multipleEmodes: true, emodeOption: 'Stablecoin' }, + skipTestState, + true + ); checkDashboardHealthFactor({ valueFrom: 1.07, valueTo: 1000 }, skipTestState); borrowsAvailable(skipTestState); verifyCountOfBorrowAssets({ assets: testData.testCases.eModeAssets }, skipTestState); }); describe('Turn off E-mode and verify decrease of health factor', () => { - emodeActivating({ turnOn: false }, skipTestState, true); + emodeActivating({ turnOn: false, multipleEmodes: true }, skipTestState, true); checkDashboardHealthFactor({ valueFrom: 1.0, valueTo: 1.07 }, skipTestState); }); describe('Turn off E-mode blocked with low health factor', () => { - emodeActivating({ turnOn: true }, skipTestState, true); + emodeActivating( + { turnOn: true, multipleEmodes: true, emodeOption: 'Stablecoin' }, + skipTestState, + true + ); borrow(testData.testCases.borrow, skipTestState, true); checkEmodeActivatingDisabled({ turnOn: false }, skipTestState); }); diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-and-emode.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-and-emode.polygon-v3.cy.ts index dc19b692d6..6e14bdc331 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-and-emode.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-and-emode.polygon-v3.cy.ts @@ -1,14 +1,14 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, swap, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply, swap } from '../../../support/steps/main.steps'; import { - verifyCountOfBorrowAssets, + borrowsAvailable, + checkDashboardHealthFactor, dashboardAssetValuesVerification, switchCollateralBlockedInModal, - checkDashboardHealthFactor, - borrowsAvailable, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { @@ -72,7 +72,11 @@ describe('ISOLATED MODE with EMODE SPEC, POLYGON V3 MARKET', () => { switchCollateralBlockedInModal(testData.testCases.checkBorrowTypeBlocked1, skipTestState); }); describe('Turn on E-Mode and verify increase of health factor', () => { - emodeActivating({ turnOn: true }, skipTestState, true); + emodeActivating( + { turnOn: true, multipleEmodes: true, emodeOption: 'Stablecoin' }, + skipTestState, + true + ); checkDashboardHealthFactor({ valueFrom: 1.07, valueTo: 1000 }, skipTestState); borrowsAvailable(skipTestState); verifyCountOfBorrowAssets({ assets: testData.IsolatedModeAssets }, skipTestState); diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-mode.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-mode.polygon-v3.cy.ts index 9600abced0..1b75b38d98 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-mode.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/isolated-mode.polygon-v3.cy.ts @@ -1,14 +1,14 @@ -import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, swap, repay, changeCollateral } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; +import { borrow, changeCollateral, repay, supply, swap } from '../../../support/steps/main.steps'; import { - verifyCountOfBorrowAssets, + borrowsUnavailable, dashboardAssetValuesVerification, switchCollateralBlocked, - borrowsUnavailable, switchCollateralBlockedInModal, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/3-polygon-v3-market/swap.polygon-v3.cy.ts b/cypress/e2e/1-v3-markets/3-polygon-v3-market/swap.polygon-v3.cy.ts index 976dcea20f..af821cedbb 100644 --- a/cypress/e2e/1-v3-markets/3-polygon-v3-market/swap.polygon-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/3-polygon-v3-market/swap.polygon-v3.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyPolygonFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/dai.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/dai.optimism-v3.cy.ts index e5518b33b0..01081e32bc 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/dai.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/dai.optimism-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/eth.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/eth.optimism-v3.cy.ts index 401d265c96..ae616c802b 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/eth.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/eth.optimism-v3.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/link.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/link.optimism-v3.cy.ts index 7cff7003a4..50acb3769d 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/link.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/link.optimism-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/susd.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/susd.optimism-v3.cy.ts index a03e7820ff..1bd3e863d2 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/susd.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/susd.optimism-v3.cy.ts @@ -1,9 +1,9 @@ -import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; -import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; import assets from '../../../../fixtures/assets.json'; import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; +import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; +import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdc.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdc.optimism-v3.cy.ts index 1baba45683..bb4d57065b 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdc.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdc.optimism-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdt.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdt.optimism-v3.cy.ts index 258a1acd10..6c350ca133 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdt.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/usdt.optimism-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/wbtc.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/wbtc.optimism-v3.cy.ts index 64e16092b8..27a77acb2c 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/wbtc.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/0-assets/wbtc.optimism-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyOptimismFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/critical-conditions.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/critical-conditions.optimism-v3.cy.ts index 7032dca1c1..9531aabc73 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/critical-conditions.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/critical-conditions.optimism-v3.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyOptimismFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyOptimismFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/4-optimism-v3-market/e-mode.optimism-v3.cy.ts b/cypress/e2e/1-v3-markets/4-optimism-v3-market/e-mode.optimism-v3.cy.ts index b52eedaf2f..8d4560b8c9 100644 --- a/cypress/e2e/1-v3-markets/4-optimism-v3-market/e-mode.optimism-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/4-optimism-v3-market/e-mode.optimism-v3.cy.ts @@ -1,13 +1,13 @@ -import { configEnvWithTenderlyOptimismFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyOptimismFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply } from '../../../support/steps/main.steps'; import { - checkDashboardHealthFactor, borrowsAvailable, - verifyCountOfBorrowAssets, + checkDashboardHealthFactor, checkEmodeActivatingDisabled, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/dai.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/dai.fantom-v3.cy.ts index 7d5e228328..fe22618e02 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/dai.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/dai.fantom-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/ftm.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/ftm.fantom-v3.cy.ts index 7e90a293b9..5afdf164b9 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/ftm.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/ftm.fantom-v3.cy.ts @@ -1,19 +1,19 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, - repay, - withdraw, changeCollateral, changeCollateralNegative, + repay, + supply, + withdraw, } from '../../../../support/steps/main.steps'; import { borrowsUnavailable, dashboardAssetValuesVerification, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { testCases: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/link.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/link.fantom-v3.cy.ts index 8a9e9da231..64c2bfd4c8 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/link.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/link.fantom-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdc.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdc.fantom-v3.cy.ts index 826eb85b4f..216acb955e 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdc.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdc.fantom-v3.cy.ts @@ -1,15 +1,15 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdt.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdt.fantom-v3.cy.ts index a2f0c7a950..468d7f7621 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdt.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/usdt.fantom-v3.cy.ts @@ -1,18 +1,18 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; import { - supply, borrow, + changeBorrowType, repay, + supply, withdraw, - changeBorrowType, } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchCollateralBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/wbtc.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/wbtc.fantom-v3.cy.ts index a6a4179333..9b770b7d4d 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/wbtc.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/wbtc.fantom-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/weth.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/weth.fantom-v3.cy.ts index 2255d9db00..7277e266ce 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/weth.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/0-assets/weth.fantom-v3.cy.ts @@ -1,12 +1,12 @@ +import assets from '../../../../fixtures/assets.json'; +import constants from '../../../../fixtures/constans.json'; +import { skipState } from '../../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../../support/steps/configuration.steps'; -import { supply, borrow, repay, withdraw } from '../../../../support/steps/main.steps'; +import { borrow, repay, supply, withdraw } from '../../../../support/steps/main.steps'; import { dashboardAssetValuesVerification, switchApyBlocked, } from '../../../../support/steps/verification.steps'; -import { skipState } from '../../../../support/steps/common'; -import assets from '../../../../fixtures/assets.json'; -import constants from '../../../../fixtures/constans.json'; const testData = { depositBaseAmount: { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/critical-conditions.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/critical-conditions.fantom-v3.cy.ts index 6556f1d13c..78d2a74c18 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/critical-conditions.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/critical-conditions.fantom-v3.cy.ts @@ -1,8 +1,8 @@ -import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, withdraw } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; +import { borrow, supply, withdraw } from '../../../support/steps/main.steps'; import { checkDashboardHealthFactor } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/e-mode.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/e-mode.fantom-v3.cy.ts index 4755eb6ccc..9f9784344a 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/e-mode.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/e-mode.fantom-v3.cy.ts @@ -1,11 +1,11 @@ -import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, emodeActivating } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; +import { borrow, emodeActivating, supply } from '../../../support/steps/main.steps'; import { - checkDashboardHealthFactor, borrowsAvailable, + checkDashboardHealthFactor, verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/isolated-mode.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/isolated-mode.fantom-v3.cy.ts index e643ccf0d3..9ec0537ebe 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/isolated-mode.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/isolated-mode.fantom-v3.cy.ts @@ -1,14 +1,14 @@ -import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; -import { supply, borrow, swap, repay, changeCollateral } from '../../../support/steps/main.steps'; -import { skipState } from '../../../support/steps/common'; import assets from '../../../fixtures/assets.json'; import constants from '../../../fixtures/constans.json'; +import { skipState } from '../../../support/steps/common'; +import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; +import { borrow, changeCollateral, repay, supply, swap } from '../../../support/steps/main.steps'; import { - verifyCountOfBorrowAssets, + borrowsUnavailable, dashboardAssetValuesVerification, switchCollateralBlocked, - borrowsUnavailable, switchCollateralBlockedInModal, + verifyCountOfBorrowAssets, } from '../../../support/steps/verification.steps'; const testData = { diff --git a/cypress/e2e/1-v3-markets/5-fantom-v3-market/swap.fantom-v3.cy.ts b/cypress/e2e/1-v3-markets/5-fantom-v3-market/swap.fantom-v3.cy.ts index b910423a50..78431542fa 100644 --- a/cypress/e2e/1-v3-markets/5-fantom-v3-market/swap.fantom-v3.cy.ts +++ b/cypress/e2e/1-v3-markets/5-fantom-v3-market/swap.fantom-v3.cy.ts @@ -1,9 +1,9 @@ +import assets from '../../../fixtures/assets.json'; +import constants from '../../../fixtures/constans.json'; import { skipState } from '../../../support/steps/common'; import { configEnvWithTenderlyFantomFork } from '../../../support/steps/configuration.steps'; import { supply, swap } from '../../../support/steps/main.steps'; -import assets from '../../../fixtures/assets.json'; import { dashboardAssetValuesVerification } from '../../../support/steps/verification.steps'; -import constants from '../../../fixtures/constans.json'; const testData = { deposit: { diff --git a/cypress/e2e/2-settings/details.page.aave-v2.cy.ts b/cypress/e2e/2-settings/details.page.aave-v2.cy.ts index fcf8d98429..2e5c31edb9 100644 --- a/cypress/e2e/2-settings/details.page.aave-v2.cy.ts +++ b/cypress/e2e/2-settings/details.page.aave-v2.cy.ts @@ -1,11 +1,11 @@ +import assets from '../../fixtures/assets.json'; +import constants from '../../fixtures/constans.json'; +import { skipState } from '../../support/steps/common'; import { configEnvWithTenderlyMainnetFork, configEnvWithTenderlyOptimismFork, } from '../../support/steps/configuration.steps'; -import { skipState } from '../../support/steps/common'; -import { supply, borrow } from '../../support/steps/main.steps'; -import constants from '../../fixtures/constans.json'; -import assets from '../../fixtures/assets.json'; +import { borrow, supply } from '../../support/steps/main.steps'; const testData = { depositETH: { diff --git a/cypress/e2e/2-settings/switch-market.cy.ts b/cypress/e2e/2-settings/switch-market.cy.ts index 3416a9c9aa..89fc1f9b7e 100644 --- a/cypress/e2e/2-settings/switch-market.cy.ts +++ b/cypress/e2e/2-settings/switch-market.cy.ts @@ -1,5 +1,5 @@ -import { configEnvWithTenderlyMainnetFork } from '../../support/steps/configuration.steps'; import markets from '../../fixtures/markets.json'; +import { configEnvWithTenderlyMainnetFork } from '../../support/steps/configuration.steps'; const switchToTestNet = () => { cy.get('#settings-button').click(); diff --git a/cypress/e2e/2-settings/wallet-connect.cy.ts b/cypress/e2e/2-settings/wallet-connect.cy.ts index b2dd40b1a8..7666f324b7 100644 --- a/cypress/e2e/2-settings/wallet-connect.cy.ts +++ b/cypress/e2e/2-settings/wallet-connect.cy.ts @@ -17,8 +17,9 @@ describe('Manipulation on the wallet connect', () => { const walletButton = '#wallet-button'; it('step1:Disconnect wallet', () => { + cy.wait(1000); cy.get(walletButton).click(); - cy.contains('Disconnect Wallet').click(); + cy.contains('Disconnect').click(); cy.contains('Please, connect your wallet').should('be.visible'); }); diff --git a/cypress/fixtures/assets.json b/cypress/fixtures/assets.json index b0e373cec4..74e2239e1a 100644 --- a/cypress/fixtures/assets.json +++ b/cypress/fixtures/assets.json @@ -881,6 +881,18 @@ "shortName": "aWETH.e", "collateral": true, "wrapped": false + }, + "MAI": { + "fullName": "MAI", + "shortName": "MAI", + "collateral": true, + "wrapped": false + }, + "FRAX": { + "fullName": "FRAX", + "shortName": "FRAX", + "collateral": true, + "wrapped": false } } } diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 537240c307..26fe85e926 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,28 +1,120 @@ import 'cypress-wait-until'; -// import { ChainId } from '@aave/contract-helpers'; -// import { MARKETS } from './steps/common'; -// import { configEnvWithTenderly } from './steps/configuration.steps'; - -// https://github.com/quasarframework/quasar/issues/2233 -const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/; -Cypress.on('uncaught:exception', (err) => { - /* returning false here prevents Cypress from failing the test */ - if (resizeObserverLoopErrRe.test(err.message)) { - return false; +declare global { + namespace Cypress { + interface Chainable { + /** + * This will set amount in Modal + * @param amount number + * @param max boolean optional + * @example cy.setAmount('137') + */ + setAmount(amount: number, max?: boolean): void; + /** + * This will make confirmation in Modal + * @param hasApproval boolean + * @param actionName string optional, verification button text + * @param assetName string optional, verification asset name + * @example cy.doConfirm(true) + */ + doConfirm(hasApproval: boolean, actionName?: string, assetName?: string): void; + /** + * This will return borrowed asset row from Dashboard + * @param assetName string + * @param apyType string + * @example cy.getDashBoardBorrowRow('ETH',constants.borrowAPYType.default) + */ + getDashBoardBorrowedRow( + assetName: string, + apyType: string + ): Cypress.Chainable>; + /** + * This will return supplied asset row from Dashboard + * @param assetName string + * @param isCollateralType boolean optional + * @example cy.getDashBoardSuppliedRow('ETH') + */ + getDashBoardSuppliedRow( + assetName: string, + isCollateralType?: boolean + ): Cypress.Chainable>; + /** + * This will switch dashboard view to Borrow + */ + doSwitchToDashboardBorrowView(): void; + /** + * This will switch dashboard view to Supply + */ + doSwitchToDashboardSupplyView(): void; + } + } +} + +Cypress.Commands.add('setAmount', (amount: number, max?: boolean) => { + cy.get('[data-cy=Modal]').find('button:contains("Enter an amount")').should('be.disabled'); + if (max) { + cy.wait(2000); //there is no way to know when real max amount will upload by UI + cy.get('[data-cy=Modal]').find('button:contains("Max")').click(); + } else { + cy.get('[data-cy=Modal] input').first().type(amount.toString()); } }); -// Cypress.Commands.add('initFork', (market, tokens = []) => { -// if (!Object.keys(MARKETS).includes(market)) throw new Error(`not sure how to setup ${market}`); -// if (market === MARKETS.fork_proto_mainnet) -// configEnvWithTenderly({ chainId: ChainId.mainnet, market, tokens }); -// if (market === MARKETS.fork_amm_mainnet) -// configEnvWithTenderly({ chainId: ChainId.mainnet, market, tokens }); -// if (market === MARKETS.fork_proto_avalanche) -// configEnvWithTenderly({ chainId: ChainId.avalanche, market, tokens }); -// if (market === MARKETS.fork_proto_matic) -// configEnvWithTenderly({ chainId: ChainId.polygon, market, tokens }); -// }); +Cypress.Commands.add( + 'doConfirm', + (hasApproval: boolean, actionName?: string, assetName?: string) => { + cy.log(`${hasApproval ? 'One step process' : 'Two step process'}`); + if (!hasApproval) { + cy.get(`[data-cy=approvalButton]`, { timeout: 20000 }).should('not.be.disabled').click(); + } + cy.get('[data-cy=actionButton]', { timeout: 30000 }) + .should('not.be.disabled') + .then(($btn) => { + if (assetName && actionName) { + expect($btn.first()).to.contain(`${actionName} ${assetName}`); + } + if (assetName && !actionName) { + expect($btn.first()).to.contain(`${actionName}`); + } + }) + .click(); + cy.get("[data-cy=Modal] h2:contains('All done!')").should('be.visible'); + } +); + +Cypress.Commands.add('getDashBoardBorrowedRow', (assetName: string, apyType: string) => { + return cy + .get(`[data-cy='dashboardBorrowedListItem_${assetName.toUpperCase()}_${apyType}']`) + .first(); +}); + +Cypress.Commands.add('getDashBoardSuppliedRow', (assetName: string, isCollateralType?: boolean) => { + if (isCollateralType) { + return cy + .get(`[data-cy='dashboardSuppliedListItem_${assetName.toUpperCase()}_Collateral']`) + .first(); + } else { + return cy.get(`[data-cy='dashboardSuppliedListItem_${assetName.toUpperCase()}_NoCollateral']`); + } +}); + +const switchDashboardView = (value: string, dashboardTitle: string) => { + cy.get('[role=group]') + .contains(value) + .then(($btn) => { + if (!$btn.is('disabled')) { + $btn.click(); + } + }); + cy.get(`*:contains("Your ${dashboardTitle.toLowerCase()}")`).should('be.visible'); +}; + +Cypress.Commands.add('doSwitchToDashboardBorrowView', () => { + switchDashboardView('Borrow', 'borrows'); +}); + +Cypress.Commands.add('doSwitchToDashboardSupplyView', () => { + switchDashboardView('Supply', 'supplies'); +}); export {}; diff --git a/cypress/support/cypress.d.ts b/cypress/support/cypress.d.ts deleted file mode 100644 index 76b38b8971..0000000000 --- a/cypress/support/cypress.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare namespace Cypress { - interface Chainable { - /** - * Custom command to select DOM element by data-cy attribute. - * @example cy.dataCy('greeting') - */ - getBySel(value: string): Chainable; - // initFork(market: string, tokens?: { address: string }[]): Promise; - } -} diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index 84ba745e4d..1221b17e09 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -1,33 +1 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: import './commands'; - -afterEach(function onAfterEach() { - if ((this.currentTest as Mocha.Test).state === 'failed') { - cy.setCookie('shouldSkip', 'true'); - Cypress.Cookies.defaults({ - preserve: 'shouldSkip', - }); - //set cookie to skip tests for further specs - // this.skip() - // Cypress.runner.stop(); - //this will skip tests only for current spec - } -}); - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/cypress/support/steps/actions.steps.ts b/cypress/support/steps/actions.steps.ts deleted file mode 100644 index f879ff4077..0000000000 --- a/cypress/support/steps/actions.steps.ts +++ /dev/null @@ -1,119 +0,0 @@ -type SetAmount = { - amount: number; - max?: boolean; -}; - -export const setAmount = ({ amount, max }: SetAmount) => { - cy.get('[data-cy=Modal]').find('button:contains("Enter an amount")').should('be.disabled'); - if (max) { - cy.get('[data-cy=Modal]').find('button:contains("Max")').click(); - } else { - cy.get('[data-cy=Modal] input').first().type(amount.toString()); - } -}; - -type ConfirmAction = { - hasApproval: boolean; - actionName?: string; - assetName?: string; -}; - -export const doConfirm = ({ hasApproval, actionName, assetName }: ConfirmAction) => { - cy.log(`${hasApproval ? 'One step process' : 'Two step process'}`); - if (!hasApproval) { - cy.get(`[data-cy=approvalButton]`, { timeout: 20000 }) - .should('not.be.disabled') - .wait(1000) - .click(); - } - cy.get('[data-cy=actionButton]', { timeout: 30000 }) - .should('not.be.disabled') - .then(($btn) => { - if (assetName && actionName) { - expect($btn.first()).to.contain(`${actionName} ${assetName}`); - } - if (assetName && !actionName) { - expect($btn.first()).to.contain(`${actionName}`); - } - }) - .wait(3000) - .click(); - cy.get("[data-cy=Modal] h2:contains('All done!')").should('be.visible'); -}; - -export const doCloseModal = () => { - return it(`Close modal popup`, () => { - cy.get('[data-cy=CloseModalIcon]').should('not.be.disabled').click(); - cy.get('[data-cy=Modal]').should('not.exist'); - }); -}; - -function doChooseSwapToOption(assetName: string) { - cy.get('.AssetSelect__reverse .AssetSelect__button').click(); - cy.get('.AssetSelect__reverse .TokenIcon__name').contains(assetName).click(); -} - -type SwapForRepayAction = { - amount: number; - assetName?: string; -}; - -export const doSwapForRepay = ({ amount, assetName }: SwapForRepayAction) => { - cy.log('assetName,' + assetName); - cy.get(':nth-child(1) > .AmountFieldWithSelect__field-inner [data-cy=amountInput]').type( - amount.toString(), - { delay: 0 } - ); - if (assetName) { - doChooseSwapToOption(assetName); - } - cy.get('.Button').contains('Continue').parents('.Button').should('not.be.disabled').click(); -}; - -type GetDashBoardBorrowRow = { - assetName: string; - apyType: string; -}; - -export const getDashBoardBorrowRow = ({ assetName, apyType }: GetDashBoardBorrowRow) => { - return cy - .get(`[data-cy='dashboardBorrowedListItem_${assetName.toUpperCase()}_${apyType}']`) - .first(); -}; - -type GetDashBoardDepositRow = { - assetName: string; - isCollateralType?: boolean; -}; - -export const getDashBoardDepositRow = ({ assetName, isCollateralType }: GetDashBoardDepositRow) => { - if (isCollateralType) { - return cy - .get(`[data-cy='dashboardSuppliedListItem_${assetName.toUpperCase()}_Collateral']`) - .first(); - } else { - return cy.get(`[data-cy='dashboardSuppliedListItem_${assetName.toUpperCase()}_NoCollateral']`); - } -}; - -export const doSwitchToDashboardBorrowView = () => { - cy.get('[role=group]') - .contains('Borrow') - .then(($btn) => { - if (!$btn.is('disabled')) { - $btn.click(); - } - }); - cy.get(`*:contains("Your borrows")`).should('be.visible'); -}; - -export const doSwitchToDashboardSupplyView = () => { - cy.get('[role=group]') - .contains('Supply') - .then(($btn) => { - if (!$btn.is('disabled')) { - $btn.click(); - } - }); - cy.get(`*:contains("Your supplies")`).should('be.visible'); -}; diff --git a/cypress/support/steps/configuration.steps.ts b/cypress/support/steps/configuration.steps.ts index 514b904134..10cc7b1134 100644 --- a/cypress/support/steps/configuration.steps.ts +++ b/cypress/support/steps/configuration.steps.ts @@ -1,8 +1,9 @@ -import { TenderlyFork, DEFAULT_TEST_ACCOUNT } from '../tools/tenderly'; +import { ChainId } from '@aave/contract-helpers'; import { JsonRpcProvider } from '@ethersproject/providers'; import { Wallet } from '@ethersproject/wallet'; + import { CustomizedBridge } from '../tools/bridge'; -import { ChainId } from '@aave/contract-helpers'; +import { DEFAULT_TEST_ACCOUNT, TenderlyFork } from '../tools/tenderly'; const URL = Cypress.env('URL'); const PERSIST_FORK_AFTER_RUN = Cypress.env('PERSIST_FORK_AFTER_RUN') || false; @@ -58,7 +59,6 @@ export const configEnvWithTenderly = ({ win.localStorage.setItem('selectedAccount', walletAddress.toLowerCase()); win.localStorage.setItem('selectedMarket', market); win.localStorage.setItem('testnetsEnabled', enableTestnet.toString()); - win.localStorage.setItem('mockWalletAddress', walletAddress.toLowerCase()); }, }); }); diff --git a/cypress/support/steps/main.steps.ts b/cypress/support/steps/main.steps.ts index ad9b8faaa9..24a72b0839 100644 --- a/cypress/support/steps/main.steps.ts +++ b/cypress/support/steps/main.steps.ts @@ -1,13 +1,3 @@ -/// -import { - setAmount, - doConfirm, - getDashBoardBorrowRow, - getDashBoardDepositRow, - doCloseModal, - doSwitchToDashboardBorrowView, - doSwitchToDashboardSupplyView, -} from './actions.steps'; import constants from '../../fixtures/constans.json'; type SkipType = { @@ -15,13 +5,15 @@ type SkipType = { get: () => boolean; }; +/** + * This skip all test steps if previous one was failed + */ const skipSetup = ({ skip, updateSkipStatus }: { skip: SkipType; updateSkipStatus: boolean }) => { before(function () { if (skip.get()) { this.skip(); } }); - afterEach(function onAfterEach() { if ((this.currentTest as Mocha.Test).state === 'failed' && updateSkipStatus) { skip.set(true); @@ -29,6 +21,21 @@ const skipSetup = ({ skip, updateSkipStatus }: { skip: SkipType; updateSkipStatu }); }; +/** + * This full step for supply any available asset from Dashboard view + * @example + *``` + * // Supply ETH + * supply({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * amount:10, + * hasApproval:true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const supply = ( { asset, @@ -50,27 +57,36 @@ export const supply = ( return describe(`Supply process for ${_shortName}`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open ${_shortName} supply popup view`, () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); cy.get(`[data-cy='dashboardSupplyListItem_${_shortName.toUpperCase()}']`) .find('button:contains("Supply")') .click(); cy.get(`[data-cy=Modal] h2:contains("Supply ${_shortName}")`).should('be.visible'); }); it(`Supply ${isMaxAmount ? 'MAX' : amount} amount for ${_shortName}`, () => { - setAmount({ - amount, - max: isMaxAmount, - }); - doConfirm({ - hasApproval, - actionName: _actionName, - assetName: _shortName, - }); + cy.setAmount(amount, isMaxAmount); + cy.doConfirm(hasApproval, _actionName, _shortName); }); doCloseModal(); }); }; +/** + * This full step for borrow any available asset from Dashboard view + * @example + *``` + * // Borrow ETH + * // apyType options: Variable, Stable, Default + * borrow({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * amount:10, + * hasApproval:true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const borrow = ( { asset, @@ -82,8 +98,8 @@ export const borrow = ( }: { asset: { shortName: string; fullName: string }; amount: number; - apyType?: string; hasApproval: boolean; + apyType?: string; isRisk?: boolean; isMaxAmount?: boolean; }, @@ -96,8 +112,7 @@ export const borrow = ( return describe(`Borrow process for ${_shortName}`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open ${_shortName} borrow popup view`, () => { - doSwitchToDashboardBorrowView(); - cy.wait(4000); + cy.doSwitchToDashboardBorrowView(); cy.get(`[data-cy='dashboardBorrowListItem_${_shortName.toUpperCase()}']`) .contains('Borrow') .should('not.be.disabled') @@ -119,10 +134,7 @@ export const borrow = ( } }); it(`Borrow ${isMaxAmount ? 'MAX' : amount} amount for ${_shortName}`, () => { - setAmount({ - amount, - max: isMaxAmount, - }); + cy.setAmount(amount, isMaxAmount); }); if (isRisk) { it(`Click risk checkbox`, () => { @@ -130,32 +142,48 @@ export const borrow = ( }); } it(`Confirmation process`, () => { - doConfirm({ - hasApproval, - actionName: _actionName, - assetName: _shortName, - }); + cy.doConfirm(hasApproval, _actionName, _shortName); }); doCloseModal(); }); }; +/** + * This full step for repay one asset by another asset + * @example + *``` + * // Repay ETH by USDC + * // apyType options: Variable, Stable, Default + * // repayOption options: collateral, wallet, default + * repay({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * apyType:'Variable', + * amount:10, + * repayOption:'collateral', + * hasApproval:true, + * repayableAsset:{shortName:'USDC'} + * }, + * skipTestState, + * false + * ) + * ``` + */ export const repay = ( { asset, apyType, amount, repayOption, - repayableAsset, hasApproval = false, + repayableAsset, isMaxAmount = false, }: { asset: { shortName: string; fullName: string }; apyType: string; amount: number; repayOption: string; - repayableAsset?: { shortName: string }; hasApproval: boolean; + repayableAsset?: { shortName: string }; isMaxAmount?: boolean; }, skip: SkipType, @@ -169,10 +197,8 @@ export const repay = ( }`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open ${_shortName} repay popup view`, () => { - doSwitchToDashboardBorrowView(); - getDashBoardBorrowRow({ assetName: _shortName, apyType }) - .find(`button:contains("Repay")`) - .click(); + cy.doSwitchToDashboardBorrowView(); + cy.getDashBoardBorrowedRow(_shortName, apyType).find(`button:contains("Repay")`).click(); cy.get(`[data-cy=Modal] h2:contains("Repay ${_shortName}")`).should('be.visible'); }); it(`Choose ${repayOption} repay option`, () => { @@ -209,20 +235,30 @@ export const repay = ( it(`Repay ${ isMaxAmount ? 'MAX' : amount } amount for ${_shortName}, with ${repayOption} repay option`, () => { - setAmount({ - amount, - max: isMaxAmount, - }); - doConfirm({ - hasApproval, - actionName: _actionName, - assetName: _shortName, - }); + cy.setAmount(amount, isMaxAmount); + cy.doConfirm(hasApproval, _actionName, _shortName); }); doCloseModal(); }); }; +/** + * This full step for withdraw any availble assets + * @example + *``` + * // Withdraw ETH + * // apyType options: Variable, Stable, Default + * withdraw({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * isCollateral:true, + * amount: 10, + * hasApproval:true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const withdraw = ( { asset, @@ -250,8 +286,8 @@ export const withdraw = ( return describe(`Withdraw process for ${_shortName}`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open ${_shortName} Withdraw popup view`, () => { - doSwitchToDashboardSupplyView(); - getDashBoardDepositRow({ assetName: _shortName, isCollateralType: isCollateral }) + cy.doSwitchToDashboardSupplyView(); + cy.getDashBoardSuppliedRow(_shortName, isCollateral) .find(`button:contains("Withdraw")`) .click(); cy.get(`[data-cy=Modal] h2:contains("Withdraw ${_shortName}")`).should('be.visible'); @@ -266,10 +302,7 @@ export const withdraw = ( }); it(`Withdraw ${isMaxAmount ? 'MAX' : amount} amount for ${_shortName}`, () => { if (isMaxAmount) cy.wait(2000); - setAmount({ - amount, - max: isMaxAmount, - }); + cy.setAmount(amount, isMaxAmount); }); if (isRisk) { it(`Click risk checkbox`, () => { @@ -277,16 +310,29 @@ export const withdraw = ( }); } it(`Confirmation process`, () => { - doConfirm({ - hasApproval, - actionName: _actionName, - assetName: forWrapped ? 'W' + _shortName : _shortName, - }); + cy.doConfirm(hasApproval, _actionName, forWrapped ? 'W' + _shortName : _shortName); }); doCloseModal(); }); }; +/** + * This full step to change borrow apy from Dashboard view + * @example + *``` + * // Change borrow type for ETH from Stable to Variable + * // apyType options: Variable, Stable + * changeBorrowType({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * apyType:'Stable', + * newAPY:'Variable', + * hasApproval:true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const changeBorrowType = ( { asset, @@ -308,8 +354,8 @@ export const changeBorrowType = ( describe('Change APY of borrowing', () => { skipSetup({ skip, updateSkipStatus }); it(`Open change apy popup`, () => { - doSwitchToDashboardBorrowView(); - getDashBoardBorrowRow({ assetName: _shortName, apyType }) + cy.doSwitchToDashboardBorrowView(); + cy.getDashBoardBorrowedRow(_shortName, apyType) .find(`[data-cy="apyButton_${apyType}"]`) .click(); }); @@ -317,15 +363,30 @@ export const changeBorrowType = ( cy.get(`[data-cy="apyMenu_${apyType}"]`).contains(`APY, ${newAPY.toLowerCase()}`).click(); }); it(`Make approve for ${_shortName}, on confirmation page`, () => { - doConfirm({ - hasApproval, - actionName: _actionName, - }); + cy.doConfirm(hasApproval, _actionName); }); doCloseModal(); }); }; +/** + * This full step to swap assets from Dashboard view + * @example + *``` + * // Swap from ETH to USDC + * // apyType options: Variable, Stable + * swap({ + * fromAsset:{shortName:'ETH', fullName:'Ethereum'}, + * toAsset:{shortName:'USDC', fullName:'USDC'}, + * isCollateralFromAsset: false, + * amount: 1.137, + * hasApproval: true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const swap = ( { fromAsset, @@ -352,8 +413,8 @@ export const swap = ( describe(`Swap ${amount} ${_shortNameFrom} to ${_shortNameTo}`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open Swap modal for ${_shortNameFrom}`, () => { - doSwitchToDashboardSupplyView(); - getDashBoardDepositRow({ assetName: _shortNameFrom, isCollateralType: isCollateralFromAsset }) + cy.doSwitchToDashboardSupplyView(); + cy.getDashBoardSuppliedRow(_shortNameFrom, isCollateralFromAsset) .find(`[data-cy=swapButton]`) .click(); cy.get(`[data-cy=Modal] h2:contains("Swap ${_shortNameFrom}")`).should('be.visible'); @@ -369,19 +430,28 @@ export const swap = ( }).should('be.visible', { timeout: 10000 }); }); it(`Make approve for ${isMaxAmount ? 'MAX' : amount} amount`, () => { - setAmount({ - amount, - max: isMaxAmount, - }); - doConfirm({ - hasApproval, - actionName: _actionName, - }); + cy.setAmount(amount, isMaxAmount); + cy.wait(2000); + cy.doConfirm(hasApproval, _actionName); }); doCloseModal(); }); }; +/** + * This full step to change collateral for any assets from Dashboard view with positive result + * @example + *``` + * // Change collateral status for ETH + * changeCollateral ({ + * asset:{shortName:'ETH', fullName:'Ethereum'}, + * isCollateralFromAsset: false, + * }, + * skipTestState, + * false + * ) + * ``` + */ export const changeCollateral = ( { asset, @@ -397,12 +467,10 @@ export const changeCollateral = ( return describe(`Switch collateral type from ${isCollateralType}`, () => { skipSetup({ skip, updateSkipStatus }); it('Open dashboard', () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); }); it('Open Switch type Modal', () => { - getDashBoardDepositRow({ assetName: _shortName, isCollateralType }) - .find('.MuiSwitch-input ') - .click(); + cy.getDashBoardSuppliedRow(_shortName, isCollateralType).find('.MuiSwitch-input ').click(); cy.get('[data-cy=Modal]').should('be.visible'); cy.get(`[data-cy=Modal] h2:contains('Review tx ${_shortName}')`).should('be.visible'); }); @@ -424,6 +492,19 @@ export const changeCollateral = ( }); }; +/** + * This full step to claim reward from Dashboard + * @example + *``` + * // Claim reward of Matic + * claimReward ({ + * asset:{shortName:'MATIC', fullName:'Matic'}, + * }, + * skipTestState, + * false + * ) + * ``` + */ export const claimReward = ( { asset, @@ -436,23 +517,33 @@ export const claimReward = ( return describe(`Claim reward`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open dashboard`, () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); }); it(`Open claim modal`, () => { cy.get('[data-cy=Claim_Box]').should('be.visible'); cy.get('[data-cy=Dashboard_Claim_Button]').click(); }); it('Confirm claim', () => { - doConfirm({ - hasApproval: true, - actionName: 'Claim', - assetName: asset.shortName, - }); + cy.doConfirm(true, 'Claim', asset.shortName); }); doCloseModal(); }); }; +/** + * This full step to change collateral with negative result from Dashboard + * @example + *``` + * // Change collateral have to blocked for Matic + * changeCollateralNegative ({ + * asset:{shortName:'MATIC', fullName:'Matic'}, + * isCollateralType + * }, + * skipTestState, + * false + * ) + * ``` + */ export const changeCollateralNegative = ( { asset, @@ -468,10 +559,8 @@ export const changeCollateralNegative = ( return describe(`Switch collateral type negative`, () => { skipSetup({ skip, updateSkipStatus }); it('Switch type', () => { - doSwitchToDashboardSupplyView(); - getDashBoardDepositRow({ assetName: _shortName, isCollateralType }) - .find('.MuiSwitch-input ') - .click(); + cy.doSwitchToDashboardSupplyView(); + cy.getDashBoardSuppliedRow(_shortName, isCollateralType).find('.MuiSwitch-input ').click(); }); it(`Check that switch type unavailable`, () => { cy.get('[data-cy=Modal]').contains( @@ -485,6 +574,19 @@ export const changeCollateralNegative = ( }); }; +/** + * This full step to activate emode from Dashboard + * @example + *``` + * // Turn on e-mode + * emodeActivating ({ + * turnOn: true + * }, + * skipTestState, + * false + * ) + * ``` + */ export const emodeActivating = ( { turnOn, @@ -501,17 +603,17 @@ export const emodeActivating = ( return describe(`${turnOn ? 'Turn on E-mode' : 'Turn off E-mode'}`, () => { skipSetup({ skip, updateSkipStatus }); it(`Open e-mode switcher`, () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); cy.get('[data-cy=emode-open]').click(); }); if (turnOn) { it(`Turn on e-mode`, () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); cy.get(`[data-cy="emode-enable"]`).click(); }); } else { it(`Turn off e-mode`, () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); cy.get(`[data-cy="emode-disable"]`).click(); }); } @@ -526,10 +628,7 @@ export const emodeActivating = ( } it(`Sign ${turnOn ? 'Turn on E-mode' : 'Turn off E-mode'}`, () => { const actionName = turnOn ? 'Enable E-Mode' : 'Disable E-Mode'; - doConfirm({ - hasApproval: true, - actionName, - }); + cy.doConfirm(true, actionName); }); doCloseModal(); it(`Check that E-mode was ${turnOn ? 'on' : 'off'}`, () => { @@ -537,3 +636,13 @@ export const emodeActivating = ( }); }); }; + +/** + * This step to close any modal + */ +export const doCloseModal = () => { + return it(`Close modal popup`, () => { + cy.get('[data-cy=CloseModalIcon]').should('not.be.disabled').click(); + cy.get('[data-cy=Modal]').should('not.exist'); + }); +}; diff --git a/cypress/support/steps/verification.steps.ts b/cypress/support/steps/verification.steps.ts index c332693c1d..77390f99b5 100644 --- a/cypress/support/steps/verification.steps.ts +++ b/cypress/support/steps/verification.steps.ts @@ -1,11 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/// -import { - doSwitchToDashboardBorrowView, - doSwitchToDashboardSupplyView, - getDashBoardBorrowRow, - getDashBoardDepositRow, -} from './actions.steps'; import constants from '../../fixtures/constans.json'; type SkipType = { @@ -21,12 +13,29 @@ const skipSetup = (skip: any) => { }); }; +/** + * This full step to verification any values from Dashboard, for supplied assets and for borrowed assets + * @example + *``` + * // Verify ETH supplied asset with amount = 10 + * // type can be 'deposit' or 'borrow' + * dashboardAssetValuesVerification ([{ + * type: 'deposit', + * assetName: 'ETH', + * isCollateral: true + * amount: 10 + * }], + * skipTestState, + * false + * ) + * ``` + */ export const dashboardAssetValuesVerification = ( estimatedCases: { - apyType?: string; + type: string; assetName: string; + apyType?: string; isCollateral?: boolean; - type: string; amount?: number; collateralType?: string; }[], @@ -42,10 +51,7 @@ export const dashboardAssetValuesVerification = ( it(`Check that asset name is ${estimatedCase.assetName}, with collateral type ${estimatedCase.collateralType} ${estimatedCase.amount ? ' and amount ' + estimatedCase.amount : ''}`, () => { - getDashBoardDepositRow({ - assetName: _assetName, - isCollateralType: estimatedCase.isCollateral, - }).within(($row) => { + cy.getDashBoardSuppliedRow(_assetName, estimatedCase.isCollateral).within(($row) => { expect($row.find(`[data-cy="assetName"]`)).to.contain(estimatedCase.assetName); if (estimatedCase.isCollateral) { expect($row.find('.MuiSwitch-input')).to.have.attr('checked'); @@ -62,12 +68,8 @@ export const dashboardAssetValuesVerification = ( it(`Check that asset name is ${estimatedCase.assetName}, with apy type ${estimatedCase.apyType} ${estimatedCase.amount ? ' and amount ' + estimatedCase.amount : ''}`, () => { - getDashBoardBorrowRow({ - assetName: _assetName, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - apyType: estimatedCase.apyType, - }).within(($row) => { + // @ts-ignore + cy.getDashBoardBorrowedRow(_assetName, estimatedCase.apyType).within(($row) => { expect($row.find(`[data-cy="assetName"]`)).to.contain(estimatedCase.assetName); expect($row.find(`[data-cy="apyButton_${estimatedCase.apyType}"]`)).to.exist; if (estimatedCase.amount) { @@ -84,11 +86,15 @@ export const dashboardAssetValuesVerification = ( }); }; +/** + * This full step to verification that borrowing unavailable + * @example borrowsUnavailable(skipTestState) + */ export const borrowsUnavailable = (skip: SkipType) => { return describe('Check that borrowing unavailable', () => { skipSetup(skip); it('Open Dashboard', () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); }); it('Check that Borrow unavailable', () => { cy.get('[data-cy^="dashboardBorrowListItem_"]') @@ -99,11 +105,15 @@ export const borrowsUnavailable = (skip: SkipType) => { }); }; +/** + * This full step to verification that borrowing unavailable + * @example borrowsAvailable(skipTestState) + */ export const borrowsAvailable = (skip: SkipType) => { return describe('Check that borrowing available', () => { skipSetup(skip); it('Open Dashboard', () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); }); it('Check that Borrow available', () => { cy.get('[data-cy^="dashboardBorrowListItem_"]') @@ -114,16 +124,24 @@ export const borrowsAvailable = (skip: SkipType) => { }); }; +/** + * This full step to verification that reward on dashboard not available + * @example rewardIsNotAvailable(skipTestState) + */ export const rewardIsNotAvailable = (skip: SkipType) => { return describe('Check that reward not available', () => { skipSetup(skip); it('Check that reward not exist on dashboard page', () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); cy.get('[data-cy=Claim_Box]').should('not.exist'); }); }); }; +/** + * This full step to verification that switch collateral blocked + * @example switchCollateralBlocked({{ shortName: 'ETH'; fullName: 'Ethereum' }}, skipTestState) + */ export const switchCollateralBlocked = ( { asset, @@ -136,16 +154,16 @@ export const switchCollateralBlocked = ( return describe('Check that collateral switcher disabled', () => { skipSetup(skip); it(`Check that collateral switcher for ${_shortName} disabled`, () => { - doSwitchToDashboardSupplyView(); - getDashBoardDepositRow({ - assetName: _shortName, - }) - .find('input[type="checkbox"]') - .should('be.disabled'); + cy.doSwitchToDashboardSupplyView(); + cy.getDashBoardSuppliedRow(_shortName).find('input[type="checkbox"]').should('be.disabled'); }); }); }; +/** + * This full step to verification that switch collateral blocked in Modal view + * @example switchCollateralBlockedInModal({{ shortName: 'ETH'; fullName: 'Ethereum' }, true}, skipTestState) + */ export const switchCollateralBlockedInModal = ( { asset, @@ -160,11 +178,8 @@ export const switchCollateralBlockedInModal = ( return describe('Check that collateral switcher disabled', () => { skipSetup(skip); it(`Check that collateral switching blocked in popup`, () => { - doSwitchToDashboardSupplyView(); - getDashBoardDepositRow({ - assetName: _shortName, - isCollateralType, - }) + cy.doSwitchToDashboardSupplyView(); + cy.getDashBoardSuppliedRow(_shortName, isCollateralType) .find('input[type="checkbox"]') .should('be.enabled') .click(); @@ -174,6 +189,10 @@ export const switchCollateralBlockedInModal = ( }); }; +/** + * This full step to verification that switch apy blocked blocked + * @example switchApyBlocked({{ shortName: 'ETH'; fullName: 'Ethereum' }, apyType:'Variable'}, skipTestState) + */ export const switchApyBlocked = ( { asset, @@ -188,10 +207,10 @@ export const switchApyBlocked = ( return describe('Check that apy switcher disabled', () => { skipSetup(skip); it(`Open dashboard`, () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); }); it(`Verify that switching button disabled with APY ${apyType}`, () => { - getDashBoardBorrowRow({ assetName: _shortName, apyType }) + cy.getDashBoardBorrowedRow(_shortName, apyType) .find(`[data-cy='apyButton_${apyType}']`) .should('be.disabled') .should('have.text', `${apyType}`); @@ -199,6 +218,10 @@ export const switchApyBlocked = ( }); }; +/** + * This full step to verification that switch apy blocked blocked + * @example changeBorrowTypeBlocked({{ shortName: 'ETH'; fullName: 'Ethereum' }, isCollateralType: true}, skipTestState) + */ export const changeBorrowTypeBlocked = ( { asset, @@ -214,16 +237,25 @@ export const changeBorrowTypeBlocked = ( return describe(`Verify that Switch borrow is unavailable`, () => { skipSetup(skip); it('Open dashboard page', () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); }); it('Try to change apy type', () => { - getDashBoardDepositRow({ assetName: _shortName, isCollateralType }) + cy.getDashBoardSuppliedRow(_shortName, isCollateralType) .find('.MuiSwitch-input ') .should('be.disabled'); }); }); }; +/** + * This full step to verification dashboard health factor + * + * Could be use one value by 'value' - varibale + * + * Could be use range by valueFrom and valueTo + * + * @example checkDashboardHealthFactor({value: 10.2, isCollateralType: true}, skipTestState) + */ export const checkDashboardHealthFactor = ( { value, @@ -241,7 +273,7 @@ export const checkDashboardHealthFactor = ( }`, () => { skipSetup(skip); it('Open dashboard page', () => { - doSwitchToDashboardSupplyView(); + cy.doSwitchToDashboardSupplyView(); }); it('Check value', () => { cy.get(`[data-cy=HealthFactorTopPannel]`).then(($health) => { @@ -264,6 +296,10 @@ export const checkDashboardHealthFactor = ( }); }; +/** + * This full step to verification that e-mode activating disabled + * @example checkEmodeActivatingDisabled({turnOn: true}, skipTestState) + */ export const checkEmodeActivatingDisabled = ( { turnOn, @@ -275,7 +311,7 @@ export const checkEmodeActivatingDisabled = ( return describe(`${turnOn ? 'Turn on E-mode' : 'Turn off E-mode'}`, () => { skipSetup(skip); it('Open E-mode switcher modal', () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); cy.get('[data-cy=emode-open]').click(); if (turnOn) cy.get(`[data-cy="emode-enable"]`).should('have.class', 'MuiButton-disableElevation'); @@ -284,6 +320,10 @@ export const checkEmodeActivatingDisabled = ( }); }; +/** + * This full step to verification available to borrow assets, usable with e-mode tests + * @example verifyCountOfBorrowAssets([{ shortName: 'ETH'; fullName: 'Ethereum' }, { shortName: 'USDC'; fullName: 'Usdc' }], skipTestState) + */ export const verifyCountOfBorrowAssets = ( { assets, @@ -295,7 +335,7 @@ export const verifyCountOfBorrowAssets = ( return describe(`Verify that count available borrowed assets is ${assets.length}`, () => { skipSetup(skip); it(`Open Borrow dashboard part`, () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); }); assets.forEach(($asset) => { it(`Verifying that ${$asset.shortName} is exist`, () => { @@ -305,7 +345,7 @@ export const verifyCountOfBorrowAssets = ( }); }); it('Verifying length', () => { - doSwitchToDashboardBorrowView(); + cy.doSwitchToDashboardBorrowView(); cy.get('[data-cy*=dashboardBorrowListItem_]').should('have.length', assets.length); }); }); diff --git a/cypress/support/tools/tenderly.ts b/cypress/support/tools/tenderly.ts index 006dc7d28a..048621051e 100644 --- a/cypress/support/tools/tenderly.ts +++ b/cypress/support/tools/tenderly.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { JsonRpcProvider } from '@ethersproject/providers'; import axios from 'axios'; -import { getDefaultProvider, Contract, utils, Wallet } from 'ethers'; +import { Contract, getDefaultProvider, utils, Wallet } from 'ethers'; + import ERC20_ABI from '../../fixtures/erc20_abi.json'; import POOL_CONFIG_ABI from '../../fixtures/poolConfig.json'; diff --git a/package.json b/package.json index e4720fe997..ce2aa0b85f 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "aave-ui", "version": "1.0.0", "private": true, + "license": "BSD-3-Clause", "scripts": { "dev": "next dev", "build": "next build", @@ -13,13 +14,16 @@ "serve:static": "npx serve out", "export": "next export", "start": "next start", - "lint": "next lint", - "lint:fix": "next lint --fix", + "lint:code": "next lint", + "lint:code:fix": "next lint --fix", + "lint:formatting": "prettier src pages scripts cypress . --check", + "lint:formatting:fix": "prettier src pages scripts cypress . --write", + "lint": "yarn lint:code && yarn lint:formatting", + "lint:fix": "yarn lint:code:fix && yarn lint:formatting:fix", "post-update": "echo \"codesandbox preview only, need an update\" && yarn upgrade --latest", "i18n:extract": "NODE_ENV=development lingui extract --clean --overwrite --locale en", "i18n:compile": "lingui compile", "i18n": "yarn i18n:extract && yarn i18n:compile", - "pre-commit": "lint-staged && yarn i18n && git add ./src/locales", "prepare": "husky install", "gql-gen": "graphql-codegen --config codegen.yml", "test:open": "DOTENV_CONFIG_PATH='.env.local' cypress open", @@ -43,7 +47,7 @@ "@heroicons/react": "^1.0.6", "@lingui/react": "^3.13.2", "@mui/icons-material": "^5.8.4", - "@mui/material": "^5.9.1", + "@mui/material": "^5.10.5", "@paraswap/sdk": "5.6.0-alpha.3", "@visx/axis": "^2.6.0", "@visx/curve": "^2.1.0", @@ -81,7 +85,8 @@ "react-number-format": "^4.9.1", "reflect-metadata": "^0.1.13", "remark-gfm": "^3.0.1", - "subscriptions-transport-ws": "^0.11.0" + "subscriptions-transport-ws": "^0.11.0", + "web3-ledgerhq-frame-connector": "^1.0.1" }, "devDependencies": { "@babel/core": "^7.18.9", @@ -106,7 +111,7 @@ "babel-loader": "^8.2.5", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-macros": "^3.1.0", - "cypress": "10.7.0", + "cypress": "10.8.0", "cypress-repeat": "^2.3.1", "cypress-wait-until": "^1.7.2", "dotenv": "^16.0.0", @@ -128,21 +133,22 @@ "@web3-react/walletlink-connector/@coinbase/wallet-sdk": "3.1.0" }, "contributors": [ - "Pol Sendra ", - "Lukas Strassel ", - "Oleksandr Tkach ", - "David Racero ", + "Anastasia Khovaeva ", "Andrew Schmidt ", + "Bojan Kopunovic Legetin ", + "David Racero ", + "Diego Prudencio ", + "Drew Cook ", + "Lukas Strassel ", + "Mark Grothe ", "Nikita Bokarev ", - "Anastasia Khovaeva ", - "Diego Prudencio diego@aave.com" + "Oleksandr Tkach ", + "Pol Sendra ", + "Vladimir Konstantinovich Yumatov " ], "lint-staged": { - "*.{js,jsx,ts,tsx}": [ - "eslint --cache --fix", - "prettier --write" - ], + "!(.prettierignore)*.*": "yarn lint:fix", "*.svg": "npx svgo --final-newline" }, "nextBundleAnalysis": { diff --git a/pages/404.page.tsx b/pages/404.page.tsx new file mode 100644 index 0000000000..f9424f1175 --- /dev/null +++ b/pages/404.page.tsx @@ -0,0 +1,51 @@ +import { Trans } from '@lingui/macro'; +import { Box, Button, Paper, Typography, useTheme } from '@mui/material'; +import Link from 'next/link'; +import { ContentContainer } from 'src/components/ContentContainer'; +import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel'; +import { MainLayout } from 'src/layouts/MainLayout'; + +export default function Aave404Page() { + const theme = useTheme(); + + return ( + <> + + + + + 404 - Page not found + + + Page not found + + + Sorry, we couldn't find the page you were looking for. +
+ We suggest you go back to the Dashboard. +
+ + + +
+
+ + ); +} + +Aave404Page.getLayout = function getLayout(page: React.ReactElement) { + return {page}; +}; diff --git a/pages/500.page.tsx b/pages/500.page.tsx new file mode 100644 index 0000000000..e5beba1e7f --- /dev/null +++ b/pages/500.page.tsx @@ -0,0 +1,90 @@ +import { DuplicateIcon, RefreshIcon } from '@heroicons/react/outline'; +import { Trans } from '@lingui/macro'; +import { Box, Button, Link, Paper, SvgIcon, Typography, useTheme } from '@mui/material'; +import { ContentContainer } from 'src/components/ContentContainer'; +import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel'; +import { MainLayout } from 'src/layouts/MainLayout'; + +export default function Aave500Page() { + const theme = useTheme(); + + const handleCopyError = () => { + console.log('copying error to clipboard'); + }; + + return ( + <> + + + + + Something went wrong + + + + Sorry, an unexpected error happened. In the meantime you may try reloading the page, + or come back later. + + + + + + + If the error continues to happen, +
you may report it to this +
{' '} + + Discord channel + + . +
+ +
+
+
+ + ); +} + +Aave500Page.getLayout = function getLayout(page: React.ReactElement) { + return {page}; +}; diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index 105a1e87bf..eda6600d0d 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -2,25 +2,33 @@ import '/public/fonts/inter/inter.css'; import { ApolloProvider } from '@apollo/client'; import { CacheProvider, EmotionCache } from '@emotion/react'; +import { Web3ReactProvider } from '@web3-react/core'; +import { providers } from 'ethers'; import { NextPage } from 'next'; import { AppProps } from 'next/app'; import Head from 'next/head'; +import AaveMetaImage from 'public/aaveMetaLogo-min.jpg'; import * as React from 'react'; +import { AddressBlocked } from 'src/components/AddressBlocked'; import { Meta } from 'src/components/Meta'; import { BorrowModal } from 'src/components/transactions/Borrow/BorrowModal'; import { ClaimRewardsModal } from 'src/components/transactions/ClaimRewards/ClaimRewardsModal'; import { CollateralChangeModal } from 'src/components/transactions/CollateralChange/CollateralChangeModal'; import { EmodeModal } from 'src/components/transactions/Emode/EmodeModal'; +import { FaucetModal } from 'src/components/transactions/Faucet/FaucetModal'; import { GasStationProvider } from 'src/components/transactions/GasStation/GasStationProvider'; import { RateSwitchModal } from 'src/components/transactions/RateSwitch/RateSwitchModal'; import { RepayModal } from 'src/components/transactions/Repay/RepayModal'; import { SupplyModal } from 'src/components/transactions/Supply/SupplyModal'; +import { SwapModal } from 'src/components/transactions/Swap/SwapModal'; import { WithdrawModal } from 'src/components/transactions/Withdraw/WithdrawModal'; import { BackgroundDataProvider } from 'src/hooks/app-data-provider/BackgroundDataProvider'; import { AppDataProvider } from 'src/hooks/app-data-provider/useAppDataProvider'; import { ConnectionStatusProvider } from 'src/hooks/useConnectionStatusContext'; import { ModalContextProvider } from 'src/hooks/useModal'; -// import { Web3ContextProvider } from 'src/libs/web3-data-provider/Web3ContextProvider'; +import { PermissionProvider } from 'src/hooks/usePermissions'; +import { WalletModalContextProvider } from 'src/hooks/useWalletModal'; +import { Web3ContextProvider } from 'src/libs/web3-data-provider/Web3Provider'; import { TxBuilderProvider } from 'src/providers/TxBuilderProvider'; import { apolloClient } from 'src/utils/apolloClient'; @@ -28,15 +36,6 @@ import createEmotionCache from '../src/createEmotionCache'; import { ProtocolDataProvider } from '../src/hooks/useProtocolDataContext'; import { AppGlobalStyles } from '../src/layouts/AppGlobalStyles'; import { LanguageProvider } from '../src/libs/LanguageProvider'; -import { SwapModal } from 'src/components/transactions/Swap/SwapModal'; -import { Web3ContextProvider } from 'src/libs/web3-data-provider/Web3Provider'; -import { Web3ReactProvider } from '@web3-react/core'; -import { providers } from 'ethers'; -import { WalletModalContextProvider } from 'src/hooks/useWalletModal'; -import { PermissionProvider } from 'src/hooks/usePermissions'; -import AaveMetaImage from 'public/aaveMetaLogo-min.jpg'; -import { FaucetModal } from 'src/components/transactions/Faucet/FaucetModal'; -import { AddressBlocked } from 'src/components/AddressBlocked'; // Client-side cache, shared for the whole session of the user in the browser. const clientSideEmotionCache = createEmotionCache(); diff --git a/pages/_error.page.tsx b/pages/_error.page.tsx new file mode 100644 index 0000000000..2031b8e5f8 --- /dev/null +++ b/pages/_error.page.tsx @@ -0,0 +1,20 @@ +import type { NextPageContext } from 'next'; +import Error from 'next/error'; + +type ErrorPageProps = { + statusCode: number; +}; + +function ErrorPage({ statusCode }: ErrorPageProps) { + return ; +} + +ErrorPage.getInitialProps = (ctx: NextPageContext) => { + const { res, err } = ctx; + // Inspect the status code and show the given template based off of it + // Default to 404 page + const statusCode = res ? res.statusCode : err ? err.statusCode : 404; + return { statusCode }; +}; + +export default ErrorPage; diff --git a/pages/faucet.page.tsx b/pages/faucet.page.tsx index a998117408..2ad8de1f39 100644 --- a/pages/faucet.page.tsx +++ b/pages/faucet.page.tsx @@ -3,6 +3,7 @@ import { FaucetModal } from 'src/components/transactions/Faucet/FaucetModal'; import { MainLayout } from 'src/layouts/MainLayout'; import FaucetAssetsList from 'src/modules/faucet/FaucetAssetsList'; import { FaucetTopPanel } from 'src/modules/faucet/FaucetTopPanel'; + import { ContentContainer } from '../src/components/ContentContainer'; export default function Faucet() { diff --git a/pages/governance/ipfs-preview.governance.tsx b/pages/governance/ipfs-preview.governance.tsx index 9ca729400b..ec41670e42 100644 --- a/pages/governance/ipfs-preview.governance.tsx +++ b/pages/governance/ipfs-preview.governance.tsx @@ -6,6 +6,7 @@ import { MainLayout } from 'src/layouts/MainLayout'; import { getProposalMetadata } from 'src/modules/governance/utils/getProposalMetadata'; import { IpfsType } from 'src/static-build/ipfs'; import { governanceConfig } from 'src/ui-config/governanceConfig'; + import ProposalPage from './proposal/[proposalId].governance'; export default function IpfsPreview() { @@ -14,10 +15,11 @@ export default function IpfsPreview() { const [ipfs, setIpfs] = useState(); async function fetchIpfs() { + const proposalMetadata = await getProposalMetadata(ipfsHash, governanceConfig.ipfsGateway); const newIpfs = { id: -1, originalHash: ipfsHash, - ...(await getProposalMetadata(ipfsHash, governanceConfig?.ipfsGateway)), + ...proposalMetadata, }; setIpfs(newIpfs); } diff --git a/pages/governance/proposal/[proposalId].governance.tsx b/pages/governance/proposal/[proposalId].governance.tsx index e31b8e3915..50b29d28a1 100644 --- a/pages/governance/proposal/[proposalId].governance.tsx +++ b/pages/governance/proposal/[proposalId].governance.tsx @@ -15,16 +15,21 @@ import { useTheme, } from '@mui/material'; import dayjs from 'dayjs'; +import AaveMetaImage from 'public/aaveMetaLogo-min.jpg'; import { useEffect, useState } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Meta } from 'src/components/Meta'; import { CheckBadge } from 'src/components/primitives/CheckBadge'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; +import { Link } from 'src/components/primitives/Link'; import { Row } from 'src/components/primitives/Row'; +import { Warning } from 'src/components/primitives/Warning'; +import { GovVoteModal } from 'src/components/transactions/GovVote/GovVoteModal'; import { GovernanceDataProvider } from 'src/hooks/governance-data-provider/GovernanceDataProvider'; import { usePolling } from 'src/hooks/usePolling'; import { MainLayout } from 'src/layouts/MainLayout'; +import { FormattedProposalTime } from 'src/modules/governance/FormattedProposalTime'; import { ProposalTopPanel } from 'src/modules/governance/proposal/ProposalTopPanel'; import { VoteInfo } from 'src/modules/governance/proposal/VoteInfo'; import { StateBadge } from 'src/modules/governance/StateBadge'; @@ -38,12 +43,8 @@ import { VoteBar } from 'src/modules/governance/VoteBar'; import { Ipfs, IpfsType } from 'src/static-build/ipfs'; import { CustomProposalType, Proposal } from 'src/static-build/proposal'; import { governanceConfig } from 'src/ui-config/governanceConfig'; -import { Link } from 'src/components/primitives/Link'; -import AaveMetaImage from 'public/aaveMetaLogo-min.jpg'; import { ContentContainer } from '../../../src/components/ContentContainer'; -import { GovVoteModal } from 'src/components/transactions/GovVote/GovVoteModal'; -import { FormattedProposalTime } from 'src/modules/governance/FormattedProposalTime'; // import { Vote } from 'src/static-build/vote'; export async function getStaticPaths() { @@ -79,6 +80,7 @@ interface ProposalPageProps { ipfs?: IpfsType; proposal?: CustomProposalType; prerendered?: boolean; + metadataError?: boolean; } const CenterAlignedImage = styled('img')({ @@ -95,6 +97,7 @@ export default function ProposalPage({ proposal: initialProposal, ipfs, prerendered, + metadataError = false, }: ProposalPageProps) { const [url, setUrl] = useState(''); const [proposal, setProposal] = useState(initialProposal); @@ -146,6 +149,7 @@ export default function ProposalPage({ requiredDiff: 0, diff: 0, }; + return ( <> {ipfs && ( @@ -160,94 +164,107 @@ export default function ProposalPage({ Proposal overview - - - {ipfs?.title || } - - {proposal && ipfs ? ( - - - - + {metadataError ? ( + + + An error has occurred fetching the proposal metadata from IPFS. + + + ) : ( + + + {ipfs?.title || } + + {proposal && ipfs ? ( + + + + + + {!loading && ( + + )} - {!loading && ( - - )} + + + - - - - - ) : ( - - - - )} - {ipfs ? ( - ; - }, - a({ node, ...rest }) { - return ; - }, - h2({ node, ...rest }) { - return ( - - ); - }, - p({ node, ...rest }) { - return ; - }, - }} - > - {ipfs.description} - - ) : ( - <> - - - - - - )} - + {ipfs.description} + + ) : ( + <> + + + + + + )} + + )} diff --git a/pages/governance/proposal/index.governance.tsx b/pages/governance/proposal/index.governance.tsx index 5be8da2cbe..170b5aa14c 100644 --- a/pages/governance/proposal/index.governance.tsx +++ b/pages/governance/proposal/index.governance.tsx @@ -9,6 +9,7 @@ import { governanceContract } from 'src/modules/governance/utils/governanceProvi import { IpfsType } from 'src/static-build/ipfs'; import { CustomProposalType } from 'src/static-build/proposal'; import { governanceConfig } from 'src/ui-config/governanceConfig'; + import ProposalPage from './[proposalId].governance'; export default function DynamicProposal() { @@ -16,29 +17,31 @@ export default function DynamicProposal() { const id = Number(router.query.proposalId); const [proposal, setProposal] = useState(); const [ipfs, setIpfs] = useState(); + const [fetchMetadataError, setFetchMetadataError] = useState(false); + + async function initialize(_ipfsGateway: string) { + const { values, ...rest } = await governanceContract.getProposal({ proposalId: id }); + const proposal = await enhanceProposalWithTimes(rest); + setProposal(proposal); - async function initialize() { try { - const { values, ...rest } = await governanceContract.getProposal({ proposalId: id }); - const proposal = await enhanceProposalWithTimes(rest); - setProposal(proposal); + const ipfsMetadata = await getProposalMetadata(proposal.ipfsHash, _ipfsGateway); const newIpfs = { id, originalHash: proposal.ipfsHash, - ...(await getProposalMetadata(proposal.ipfsHash, governanceConfig?.ipfsGateway)), + ...ipfsMetadata, }; setIpfs(newIpfs); } catch (e) { - console.log(e); - setTimeout(initialize, 5000); + setFetchMetadataError(true); } } useEffect(() => { - id && initialize(); + id && initialize(governanceConfig.ipfsGateway); }, [id]); - return ; + return ; } DynamicProposal.getLayout = function getLayout(page: React.ReactElement) { diff --git a/pages/index.page.tsx b/pages/index.page.tsx index 48a8229488..f08d8e3265 100644 --- a/pages/index.page.tsx +++ b/pages/index.page.tsx @@ -1,14 +1,10 @@ import { Trans } from '@lingui/macro'; -import { - Box, - ToggleButton, - ToggleButtonGroup, - Typography, - useMediaQuery, - useTheme, -} from '@mui/material'; +import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; import { useEffect, useState } from 'react'; +import StyledToggleButton from 'src/components/StyledToggleButton'; +import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; import { usePermissions } from 'src/hooks/usePermissions'; + import { ConnectWalletPaper } from '../src/components/ConnectWalletPaper'; import { ContentContainer } from '../src/components/ContentContainer'; import { MainLayout } from '../src/layouts/MainLayout'; @@ -43,24 +39,24 @@ export default function Home() { mb: { xs: 3, xsm: 4 }, }} > - setMode(value)} sx={{ width: { xs: '100%', xsm: '359px' }, height: '44px' }} > - + Supply - - + + Borrow - - + + )} diff --git a/pages/reserve-overview.page.tsx b/pages/reserve-overview.page.tsx index 504b17e577..e81a03531d 100644 --- a/pages/reserve-overview.page.tsx +++ b/pages/reserve-overview.page.tsx @@ -1,14 +1,9 @@ import { Trans } from '@lingui/macro'; -import { - Box, - ToggleButton, - ToggleButtonGroup, - Typography, - useMediaQuery, - useTheme, -} from '@mui/material'; +import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; +import StyledToggleButton from 'src/components/StyledToggleButton'; +import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; import { ComputedReserveData, useAppDataContext, @@ -18,6 +13,7 @@ import { MainLayout } from 'src/layouts/MainLayout'; import { ReserveActions } from 'src/modules/reserve-overview/ReserveActions'; import { ReserveConfiguration } from 'src/modules/reserve-overview/ReserveConfiguration'; import { ReserveTopDetails } from 'src/modules/reserve-overview/ReserveTopDetails'; + import { ContentContainer } from '../src/components/ContentContainer'; export default function ReserveOverview() { @@ -52,24 +48,24 @@ export default function ReserveOverview() { mb: { xs: 3, xsm: 4 }, }} > - setMode(value)} sx={{ width: { xs: '100%', xsm: '359px' }, height: '44px' }} > - + Overview - - + + Your info - - + + diff --git a/pages/staking.staking.tsx b/pages/staking.staking.tsx index c531376a90..8206461782 100644 --- a/pages/staking.staking.tsx +++ b/pages/staking.staking.tsx @@ -1,18 +1,11 @@ import { Trans } from '@lingui/macro'; -import { - Box, - Grid, - ToggleButton, - ToggleButtonGroup, - Typography, - useMediaQuery, - useTheme, -} from '@mui/material'; +import { Box, Grid, Typography, useMediaQuery, useTheme } from '@mui/material'; import { BigNumber } from 'ethers/lib/ethers'; import { formatEther } from 'ethers/lib/utils'; import { useEffect, useState } from 'react'; import { ContentContainer } from 'src/components/ContentContainer'; -import { GetABPToken } from 'src/modules/staking/GetABPToken'; +import StyledToggleButton from 'src/components/StyledToggleButton'; +import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; import { StakeModal } from 'src/components/transactions/Stake/StakeModal'; import { StakeCooldownModal } from 'src/components/transactions/StakeCooldown/StakeCooldownModal'; import { StakeRewardClaimModal } from 'src/components/transactions/StakeRewardClaim/StakeRewardClaimModal'; @@ -20,6 +13,7 @@ import { UnStakeModal } from 'src/components/transactions/UnStake/UnStakeModal'; import { StakeDataProvider, useStakeData } from 'src/hooks/stake-data-provider/StakeDataProvider'; import { useModalContext } from 'src/hooks/useModal'; import { MainLayout } from 'src/layouts/MainLayout'; +import { GetABPToken } from 'src/modules/staking/GetABPToken'; import { StakingHeader } from 'src/modules/staking/StakingHeader'; import { StakingPanel } from 'src/modules/staking/StakingPanel'; import { StakeTxBuilderProvider } from 'src/providers/StakeTxBuilderProvider'; @@ -77,24 +71,24 @@ export default function Staking() { mb: { xs: 3, xsm: 4 }, }} > - setMode(value)} sx={{ width: { xs: '100%', xsm: '359px' } }} > - + Stake AAVE - - + + Stake ABPT - - + + diff --git a/public/404/404.svg b/public/404/404.svg new file mode 100644 index 0000000000..fe6f39f38c --- /dev/null +++ b/public/404/404.svg @@ -0,0 +1 @@ + diff --git a/public/fork-config-example.png b/public/fork-config-example.png new file mode 100644 index 0000000000000000000000000000000000000000..dc0d316fe6578743124744edbec6b22c9ecee244 GIT binary patch literal 105014 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 diff --git a/scripts/populate-cache.js b/scripts/populate-cache.js index 9f5073876e..985edb7530 100644 --- a/scripts/populate-cache.js +++ b/scripts/populate-cache.js @@ -75240,6 +75240,7 @@ var governanceConfig = { AAVE_GOVERNANCE_V2_HELPER: '0x16ff7583ea21055bf5f929ec4b896d997ff35847', }, ipfsGateway: 'https://gateway.pinata.cloud/ipfs', + fallbackIpfsGateway: 'https://cloudflare-ipfs.com/ipfs', }; // src/utils/marketsAndNetworksConfig.ts @@ -75887,7 +75888,7 @@ var _a4; var FORK_CHAIN_ID = Number( ((_a4 = global == null ? void 0 : global.window) == null ? void 0 - : _a4.localStorage.getItem('forkChainId')) || 3030 + : _a4.localStorage.getItem('forkNetworkId')) || 3030 ); var _a5; var FORK_RPC_URL = @@ -76008,7 +76009,7 @@ var governanceContract = new import_contract_helpers5.AaveGovernanceService( ); // src/static-build/ipfs.ts -var import_path2 = require('path'); +var import_lodash = __toESM(require_lodash()); // node_modules/steno/lib/index.js var import_fs = __toESM(require('fs'), 1); @@ -76273,17 +76274,17 @@ var LowSync = class { }; // src/static-build/ipfs.ts -var import_lodash = __toESM(require_lodash()); +var import_path2 = require('path'); // src/modules/governance/utils/getProposalMetadata.ts var import_utils = __toESM(require_utils5()); -var import_isomorphic_unfetch = __toESM(require_isomorphic_unfetch()); var import_gray_matter = __toESM(require_gray_matter()); +var import_isomorphic_unfetch = __toESM(require_isomorphic_unfetch()); function getLink(hash, gateway) { return `${gateway}/${hash}`; } var MEMORIZE = {}; -function getProposalMetadata(hash, gateway = 'https://cloudflare-ipfs.com/ipfs') { +function getProposalMetadata(hash, gateway) { return __async(this, null, function* () { const ipfsHash = hash.startsWith('0x') ? import_utils.base58.encode(Buffer.from(`1220${hash.slice(2)}`, 'hex')) @@ -76343,7 +76344,7 @@ var Ipfs = class { const value = db.chain.get('ipfs').find({ id }).value(); if (value) return; if (!proposal) throw new Error(`error populating proposal ${id}`); - const ipfs = yield getProposalMetadata(proposal.ipfsHash); + const ipfs = yield getProposalMetadata(proposal.ipfsHash, governanceConfig.ipfsGateway); const newIpfs = __spreadProps(__spreadValues({}, ipfs), { originalHash: proposal.ipfsHash, id, @@ -76361,7 +76362,7 @@ var import_path3 = require('path'); // src/modules/governance/utils/formatProposal.ts var import_contract_helpers6 = __toESM(require_cjs()); var import_bignumber = __toESM(require_bignumber2()); -var averageBlockTime = 14; +var averageBlockTime = 12; function enhanceProposalWithTimes(proposal) { return __async(this, null, function* () { const provider = getProvider(import_contract_helpers6.ChainId.mainnet); diff --git a/src/components/AddressBlocked.tsx b/src/components/AddressBlocked.tsx index 49de7d77dd..92649e50be 100644 --- a/src/components/AddressBlocked.tsx +++ b/src/components/AddressBlocked.tsx @@ -2,6 +2,7 @@ import { ReactNode } from 'react'; import { useAddressAllowed } from 'src/hooks/useAddressAllowed'; import { MainLayout } from 'src/layouts/MainLayout'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { AddressBlockedModal } from './AddressBlockedModal'; export const AddressBlocked = ({ children }: { children: ReactNode }) => { diff --git a/src/components/AddressBlockedModal.tsx b/src/components/AddressBlockedModal.tsx index 3fd865c844..3da0f27818 100644 --- a/src/components/AddressBlockedModal.tsx +++ b/src/components/AddressBlockedModal.tsx @@ -1,6 +1,7 @@ import { ExclamationCircleIcon, LogoutIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; import { Box, Button, SvgIcon, Typography } from '@mui/material'; + import { BasicModal } from './primitives/BasicModal'; import { Link } from './primitives/Link'; diff --git a/src/components/ConnectWalletPaper.tsx b/src/components/ConnectWalletPaper.tsx index 93eda67fac..ce97e4b46c 100644 --- a/src/components/ConnectWalletPaper.tsx +++ b/src/components/ConnectWalletPaper.tsx @@ -1,10 +1,11 @@ import { Trans } from '@lingui/macro'; import { CircularProgress, Paper, PaperProps, Typography } from '@mui/material'; import { ReactNode } from 'react'; -import { ConnectWalletButton } from './WalletConnection/ConnectWalletButton'; import LoveGhost from '/public/loveGhost.svg'; +import { ConnectWalletButton } from './WalletConnection/ConnectWalletButton'; + interface ConnectWalletPaperProps extends PaperProps { loading?: boolean; description?: ReactNode; diff --git a/src/components/ContentWithTooltip.tsx b/src/components/ContentWithTooltip.tsx index 01c48e6357..801410bb9a 100644 --- a/src/components/ContentWithTooltip.tsx +++ b/src/components/ContentWithTooltip.tsx @@ -1,4 +1,4 @@ -import { Box, ClickAwayListener, Popper, styled, Tooltip, experimental_sx } from '@mui/material'; +import { Box, ClickAwayListener, experimental_sx, Popper, styled, Tooltip } from '@mui/material'; import { JSXElementConstructor, ReactElement, ReactNode, useState } from 'react'; interface ContentWithTooltipProps { diff --git a/src/components/FaucetButton.tsx b/src/components/FaucetButton.tsx index df65a87d12..74e5226bd2 100644 --- a/src/components/FaucetButton.tsx +++ b/src/components/FaucetButton.tsx @@ -2,6 +2,7 @@ import { ExternalLinkIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; import { Button, SvgIcon, Typography } from '@mui/material'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; + import { DarkTooltip } from './infoTooltips/DarkTooltip'; import { Link, ROUTES } from './primitives/Link'; diff --git a/src/components/HALLink.tsx b/src/components/HALLink.tsx index 7d7374d827..88fe1458d8 100644 --- a/src/components/HALLink.tsx +++ b/src/components/HALLink.tsx @@ -1,15 +1,15 @@ import { valueToBigNumber } from '@aave/math-utils'; import { Trans } from '@lingui/macro'; import { + Button, + experimental_sx, + Link, Popper, + Stack, styled, SvgIcon, Tooltip, Typography, - experimental_sx, - Button, - Link, - Stack, } from '@mui/material'; import BigNumber from 'bignumber.js'; import { useMemo } from 'react'; diff --git a/src/components/HealthFactorNumber.tsx b/src/components/HealthFactorNumber.tsx index 80cabcfd2c..2de3dba4af 100644 --- a/src/components/HealthFactorNumber.tsx +++ b/src/components/HealthFactorNumber.tsx @@ -1,6 +1,6 @@ import { valueToBigNumber } from '@aave/math-utils'; import { Trans } from '@lingui/macro'; -import { Typography, Box, Button, useTheme } from '@mui/material'; +import { Box, Button, Typography, useTheme } from '@mui/material'; import { TypographyProps } from '@mui/material/Typography'; import BigNumber from 'bignumber.js'; diff --git a/src/components/MarketSwitcher.tsx b/src/components/MarketSwitcher.tsx index d75a265fdb..c3c82deccd 100644 --- a/src/components/MarketSwitcher.tsx +++ b/src/components/MarketSwitcher.tsx @@ -6,8 +6,6 @@ import { MenuItem, SvgIcon, TextField, - ToggleButton, - ToggleButtonGroup, Tooltip, Typography, useMediaQuery, @@ -26,6 +24,8 @@ import { networkConfigs, STAGING_ENV, } from '../utils/marketsAndNetworksConfig'; +import StyledToggleButton from './StyledToggleButton'; +import StyledToggleButtonGroup from './StyledToggleButtonGroup'; export const getMarketInfoById = (marketId: CustomMarket) => { const market: MarketDataType = marketsData[marketId as CustomMarket]; @@ -94,6 +94,7 @@ enum SelectedMarketVersion { V2, V3, } + export const MarketSwitcher = () => { const { currentMarket, setCurrentMarket } = useProtocolDataContext(); const [selectedMarketVersion, setSelectedMarketVersion] = useState( @@ -201,7 +202,7 @@ export const MarketSwitcher = () => { {isV3MarketsAvailable && ( - { @@ -222,7 +223,7 @@ export const MarketSwitcher = () => { padding: '2px', }} > - { > Version 3 - - + { > Version 2 - - + + )} {availableMarkets.map((marketId: CustomMarket) => { diff --git a/src/components/Meta.tsx b/src/components/Meta.tsx index ed534f1f1e..b0f92ba342 100644 --- a/src/components/Meta.tsx +++ b/src/components/Meta.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import Head from 'next/head'; +import React from 'react'; type MetaProps = { title: string; diff --git a/src/components/ReserveOverviewBox.tsx b/src/components/ReserveOverviewBox.tsx index 7ac6bae29e..ca97e303a3 100644 --- a/src/components/ReserveOverviewBox.tsx +++ b/src/components/ReserveOverviewBox.tsx @@ -1,5 +1,5 @@ -import React, { ReactNode } from 'react'; import { Box, Typography } from '@mui/material'; +import React, { ReactNode } from 'react'; type ReserveOverviewBoxProps = { children: ReactNode; diff --git a/src/components/ReserveSubheader.tsx b/src/components/ReserveSubheader.tsx index 6ea1671dbf..2eb117bc05 100644 --- a/src/components/ReserveSubheader.tsx +++ b/src/components/ReserveSubheader.tsx @@ -1,5 +1,6 @@ -import React from 'react'; import Box from '@mui/material/Box'; +import React from 'react'; + import { FormattedNumber } from './primitives/FormattedNumber'; type ReserveSubheaderProps = { diff --git a/src/components/StyledToggleButton.tsx b/src/components/StyledToggleButton.tsx new file mode 100644 index 0000000000..2af296649f --- /dev/null +++ b/src/components/StyledToggleButton.tsx @@ -0,0 +1,36 @@ +import { styled, ToggleButton, ToggleButtonProps } from '@mui/material'; +import React from 'react'; + +const CustomToggleButton = styled(ToggleButton)(({ theme }) => ({ + border: '0px', + flex: 1, + backgroundColor: '#383D51', + borderRadius: '4px', + + '&.Mui-selected, &.Mui-selected:hover': { + backgroundColor: '#FFFFFF', + borderRadius: '4px !important', + }, + + '&.Mui-selected, &.Mui-disabled': { + zIndex: 100, + height: '100%', + display: 'flex', + justifyContent: 'center', + + '.MuiTypography-subheader1': { + background: theme.palette.gradients.aaveGradient, + backgroundClip: 'text', + textFillColor: 'transparent', + }, + '.MuiTypography-secondary14': { + background: theme.palette.gradients.aaveGradient, + backgroundClip: 'text', + textFillColor: 'transparent', + }, + }, +})) as typeof ToggleButton; + +export default function StyledToggleButton(props: ToggleButtonProps) { + return ; +} diff --git a/src/components/StyledToggleButtonGroup.tsx b/src/components/StyledToggleButtonGroup.tsx new file mode 100644 index 0000000000..adc8442b35 --- /dev/null +++ b/src/components/StyledToggleButtonGroup.tsx @@ -0,0 +1,11 @@ +import { styled, ToggleButtonGroup, ToggleButtonGroupProps } from '@mui/material'; + +const CustomToggleGroup = styled(ToggleButtonGroup)({ + backgroundColor: '#383D51', + border: '1px solid rgba(235, 235, 237, 0.12)', + padding: '4px', +}) as typeof ToggleButtonGroup; + +export default function StyledToggleGroup(props: ToggleButtonGroupProps) { + return ; +} diff --git a/src/components/WalletConnection/ConnectWalletButton.tsx b/src/components/WalletConnection/ConnectWalletButton.tsx index 61a6d98bbe..176981a3de 100644 --- a/src/components/WalletConnection/ConnectWalletButton.tsx +++ b/src/components/WalletConnection/ConnectWalletButton.tsx @@ -1,6 +1,7 @@ import { Trans } from '@lingui/macro'; import { Button } from '@mui/material'; import { useWalletModalContext } from 'src/hooks/useWalletModal'; + import { WalletModal } from './WalletModal'; export const ConnectWalletButton = () => { diff --git a/src/components/WalletConnection/WalletModal.tsx b/src/components/WalletConnection/WalletModal.tsx index 80ac4f5f85..c3ecbdf74f 100644 --- a/src/components/WalletConnection/WalletModal.tsx +++ b/src/components/WalletConnection/WalletModal.tsx @@ -1,4 +1,5 @@ import { useWalletModalContext } from 'src/hooks/useWalletModal'; + import { BasicModal } from '../primitives/BasicModal'; import { WalletSelector } from './WalletSelector'; diff --git a/src/components/WalletConnection/WalletSelector.tsx b/src/components/WalletConnection/WalletSelector.tsx index f9cd19032a..d7f8def080 100644 --- a/src/components/WalletConnection/WalletSelector.tsx +++ b/src/components/WalletConnection/WalletSelector.tsx @@ -1,11 +1,25 @@ -import { Alert, Box, Button, Link, Typography } from '@mui/material'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; -import { WalletType } from 'src/libs/web3-data-provider/WalletOptions'; -import { TxModalTitle } from '../transactions/FlowCommons/TxModalTitle'; import { Trans } from '@lingui/macro'; +import { + Alert, + Box, + Button, + InputBase, + Link, + Typography, + useMediaQuery, + useTheme, +} from '@mui/material'; import { UnsupportedChainIdError } from '@web3-react/core'; -import { UserRejectedRequestError } from '@web3-react/walletconnect-connector'; import { NoEthereumProviderError } from '@web3-react/injected-connector'; +import { UserRejectedRequestError } from '@web3-react/walletconnect-connector'; +import { utils } from 'ethers'; +import { useState } from 'react'; +import { WatchOnlyModeTooltip } from 'src/components/infoTooltips/WatchOnlyModeTooltip'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { WalletType } from 'src/libs/web3-data-provider/WalletOptions'; +import { getENSProvider } from 'src/utils/marketsAndNetworksConfig'; + +import { TxModalTitle } from '../transactions/FlowCommons/TxModalTitle'; export type WalletRowProps = { walletName: string; @@ -94,7 +108,12 @@ export enum ErrorType { } export const WalletSelector = () => { - const { error } = useWeb3Context(); + const { error, updateWatchModeOnlyAddress } = useWeb3Context(); + const [inputMockWalletAddress, setInputMockWalletAddress] = useState(''); + const [validAddressError, setValidAddressError] = useState(false); + const { breakpoints } = useTheme(); + const sm = useMediaQuery(breakpoints.down('sm')); + const mainnetProvider = getENSProvider(); let blockingError: ErrorType | undefined = undefined; if (error) { @@ -124,6 +143,31 @@ export const WalletSelector = () => { } }; + const handleWatchAddress = async (inputMockWalletAddress: string): Promise => { + if (validAddressError) setValidAddressError(false); + if (utils.isAddress(inputMockWalletAddress)) { + updateWatchModeOnlyAddress(inputMockWalletAddress); + } else { + // Check if address could be valid ENS before trying to resolve + if (inputMockWalletAddress.slice(-4) !== '.eth') { + setValidAddressError(true); + } else { + // Attempt to resolve ENS name and use resolved address if valid + const resolvedAddress = await mainnetProvider.resolveName(inputMockWalletAddress); + if (resolvedAddress && utils.isAddress(resolvedAddress)) { + updateWatchModeOnlyAddress(resolvedAddress); + } else { + setValidAddressError(true); + } + } + } + }; + + const handleSubmit = (event: React.FormEvent): void => { + event.preventDefault(); + handleWatchAddress(inputMockWalletAddress); + }; + return ( @@ -149,6 +193,56 @@ export const WalletSelector = () => { /> + + + Enter an address to track in watch-only mode + + + +
+ ({ + py: 1, + px: 3, + border: `1px solid ${theme.palette.divider}`, + borderRadius: '6px', + mb: 1, + overflow: 'show', + fontSize: sm ? '16px' : '14px', + })} + placeholder="Enter ethereum address or ENS name" + fullWidth + autoFocus + value={inputMockWalletAddress} + onChange={(e) => setInputMockWalletAddress(e.target.value)} + inputProps={{ + 'aria-label': 'watch mode only address', + }} + /> + + + {validAddressError && ( + + Please enter a valid wallet address. + + )} Need help connecting a wallet?{' '} diff --git a/src/components/Warnings/CooldownWarning.tsx b/src/components/Warnings/CooldownWarning.tsx index 5ebd4b5980..2d8a692967 100644 --- a/src/components/Warnings/CooldownWarning.tsx +++ b/src/components/Warnings/CooldownWarning.tsx @@ -1,8 +1,8 @@ import { Trans } from '@lingui/macro'; import { Typography } from '@mui/material'; -import { Warning } from '../primitives/Warning'; import { Link } from '../primitives/Link'; +import { Warning } from '../primitives/Warning'; export const CooldownWarning = () => { return ( diff --git a/src/components/caps/CapsCircularStatus.tsx b/src/components/caps/CapsCircularStatus.tsx index 55941bcd5a..cc6559170f 100644 --- a/src/components/caps/CapsCircularStatus.tsx +++ b/src/components/caps/CapsCircularStatus.tsx @@ -1,7 +1,8 @@ -import { ReactNode, useState } from 'react'; +import Box from '@mui/material/Box'; import CircularProgress, { circularProgressClasses } from '@mui/material/CircularProgress'; import Typography from '@mui/material/Typography'; -import Box from '@mui/material/Box'; +import { ReactNode, useState } from 'react'; + import { ContentWithTooltip } from '../ContentWithTooltip'; type CapsCircularStatusProps = { @@ -54,7 +55,6 @@ export const CapsCircularStatus = ({ value, tooltipContent }: CapsCircularStatus /> <> - Debt limits the amount possible to borrow against this asset by protocol users. Debt - ceiling is specific to assets in isolation mode and is denoted in USD. + Debt ceiling limits the amount possible to borrow against this asset by protocol + users. Debt ceiling is specific to assets in isolation mode and is denoted in USD. {' '} { + if (currentMarket && currentMarket === 'proto_harmony_v3') { + return 'https://snapshot.org/#/aave.eth/proposal/0x81a78109941e5e0ac6cb5ebf82597c839c20ad6821a8c3ff063dba39032533d4'; + } else if (currentMarket && currentMarket === 'proto_fantom_v3') { + return 'https://snapshot.org/#/aave.eth/proposal/0xeefcd76e523391a14cfd0a79b531ea0a3faf0eb4a058e255fac13a2d224cc647'; + } else if (symbol && frozenProposalMap[symbol]) { + return frozenProposalMap[symbol]; + } else { + return 'https://app.aave.com/governance'; + } +}; + +export const FrozenTooltip = ({ symbol, currentMarket }: FrozenTooltipProps) => { + return ( + + + This asset is frozen due to an Aave Protocol Governance decision.{' '} + + More details + + +
+ } + > + + + + + ); +}; diff --git a/src/components/infoTooltips/FrozenWarning.tsx b/src/components/infoTooltips/FrozenWarning.tsx deleted file mode 100644 index a32d0387f2..0000000000 --- a/src/components/infoTooltips/FrozenWarning.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ExclamationIcon } from '@heroicons/react/outline'; -import { Trans } from '@lingui/macro'; -import { Box, SvgIcon } from '@mui/material'; - -import { ContentWithTooltip } from '../ContentWithTooltip'; -import { Link } from '../primitives/Link'; -import { frozenProposalMap } from '../../utils/marketsAndNetworksConfig'; - -interface FrozenWarningProps { - symbol?: string; -} - -export const FrozenWarning = ({ symbol }: FrozenWarningProps) => { - return ( - - - {symbol} is frozen due to an Aave Protocol Governance decision.{' '} - - More details - - -
- } - > - - - - - ); -}; diff --git a/src/components/infoTooltips/ReserveFactorTooltip.tsx b/src/components/infoTooltips/ReserveFactorTooltip.tsx index ca4a4ea1e6..e5aaa11ea4 100644 --- a/src/components/infoTooltips/ReserveFactorTooltip.tsx +++ b/src/components/infoTooltips/ReserveFactorTooltip.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro'; -import { Link } from '../primitives/Link'; +import { Link } from '../primitives/Link'; import { TextWithTooltip, TextWithTooltipProps } from '../TextWithTooltip'; interface ReserveFactorTooltipProps extends TextWithTooltipProps { diff --git a/src/components/infoTooltips/SupplyCapMaxedTooltip.tsx b/src/components/infoTooltips/SupplyCapMaxedTooltip.tsx index 489bb7c223..e07717b0bc 100644 --- a/src/components/infoTooltips/SupplyCapMaxedTooltip.tsx +++ b/src/components/infoTooltips/SupplyCapMaxedTooltip.tsx @@ -2,8 +2,9 @@ import { ExclamationIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; import { Box } from '@mui/material'; import { AssetCapData } from 'src/hooks/useAssetCaps'; -import { TextWithTooltip, TextWithTooltipProps } from '../TextWithTooltip'; + import { Link } from '../primitives/Link'; +import { TextWithTooltip, TextWithTooltipProps } from '../TextWithTooltip'; type SupplyCapMaxedTooltipProps = TextWithTooltipProps & { supplyCap: AssetCapData; diff --git a/src/components/infoTooltips/WatchOnlyModeTooltip.tsx b/src/components/infoTooltips/WatchOnlyModeTooltip.tsx new file mode 100644 index 0000000000..20c6726755 --- /dev/null +++ b/src/components/infoTooltips/WatchOnlyModeTooltip.tsx @@ -0,0 +1,14 @@ +import { Trans } from '@lingui/macro'; + +import { TextWithTooltip, TextWithTooltipProps } from '../TextWithTooltip'; + +export const WatchOnlyModeTooltip = ({ ...rest }: TextWithTooltipProps) => { + return ( + + + Watch-only mode allows to see address positions in Aave, but you won't be able to + perform transactions. + + + ); +}; diff --git a/src/components/lists/ListMobileItem.tsx b/src/components/lists/ListMobileItem.tsx index dc56929560..2230dea941 100644 --- a/src/components/lists/ListMobileItem.tsx +++ b/src/components/lists/ListMobileItem.tsx @@ -1,10 +1,10 @@ import { Box, Divider, Skeleton, Typography } from '@mui/material'; import { ReactNode } from 'react'; +import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { CustomMarket } from 'src/ui-config/marketsConfig'; + import { Link, ROUTES } from '../primitives/Link'; import { TokenIcon } from '../primitives/TokenIcon'; -import { useAssetCaps } from 'src/hooks/useAssetCaps'; -import { ETHBorrowWarning } from '../transactions/Warnings/ETHBorrowWarning'; interface ListMobileItemProps { warningComponent?: ReactNode; @@ -18,7 +18,6 @@ interface ListMobileItemProps { showSupplyCapTooltips?: boolean; showBorrowCapTooltips?: boolean; showDebtCeilingTooltips?: boolean; - showETHBorrowWarning?: boolean; } export const ListMobileItem = ({ @@ -33,7 +32,6 @@ export const ListMobileItem = ({ showSupplyCapTooltips = false, showBorrowCapTooltips = false, showDebtCeilingTooltips = false, - showETHBorrowWarning = false, }: ListMobileItemProps) => { const { supplyCap, borrowCap, debtCeiling } = useAssetCaps(); @@ -66,15 +64,9 @@ export const ListMobileItem = ({ {symbol}
- {showETHBorrowWarning ? ( - - ) : ( - <> - {showSupplyCapTooltips && supplyCap.displayMaxedTooltip({ supplyCap })} - {showBorrowCapTooltips && borrowCap.displayMaxedTooltip({ borrowCap })} - {showDebtCeilingTooltips && debtCeiling.displayMaxedTooltip({ debtCeiling })} - - )} + {showSupplyCapTooltips && supplyCap.displayMaxedTooltip({ supplyCap })} + {showBorrowCapTooltips && borrowCap.displayMaxedTooltip({ borrowCap })} + {showDebtCeilingTooltips && debtCeiling.displayMaxedTooltip({ debtCeiling })} ) )} diff --git a/src/components/lists/ListWrapper.tsx b/src/components/lists/ListWrapper.tsx index ee32460110..40ae50a12a 100644 --- a/src/components/lists/ListWrapper.tsx +++ b/src/components/lists/ListWrapper.tsx @@ -5,7 +5,7 @@ import { ReactNode, useState } from 'react'; import { toggleLocalStorageClick } from '../../helpers/toggle-local-storage-click'; interface ListWrapperProps { - title: ReactNode; + titleComponent: ReactNode; localStorageName?: string; subTitleComponent?: ReactNode; subChildrenComponent?: ReactNode; @@ -13,19 +13,17 @@ interface ListWrapperProps { children: ReactNode; withTopMargin?: boolean; noData?: boolean; - captionSize?: 'h2' | 'h3'; } export const ListWrapper = ({ children, localStorageName, - title, + titleComponent, subTitleComponent, subChildrenComponent, topInfo, withTopMargin, noData, - captionSize = 'h3', }: ListWrapperProps) => { const [isCollapse, setIsCollapse] = useState( localStorageName ? localStorage.getItem(localStorageName) === 'true' : false @@ -51,15 +49,14 @@ export const ListWrapper = ({ > - - {title} - + {titleComponent} {subTitleComponent} diff --git a/src/components/primitives/CheckBadge.tsx b/src/components/primitives/CheckBadge.tsx index f8def81b7a..f1f2a2ac6a 100644 --- a/src/components/primitives/CheckBadge.tsx +++ b/src/components/primitives/CheckBadge.tsx @@ -1,4 +1,4 @@ -import { CheckCircleIcon, XCircleIcon, QuestionMarkCircleIcon } from '@heroicons/react/solid'; +import { CheckCircleIcon, QuestionMarkCircleIcon, XCircleIcon } from '@heroicons/react/solid'; import { Box, BoxProps, Typography, TypographyProps, useTheme } from '@mui/material'; import { ReactNode } from 'react'; diff --git a/src/components/primitives/Link.tsx b/src/components/primitives/Link.tsx index c034ce2de1..7db24272e9 100644 --- a/src/components/primitives/Link.tsx +++ b/src/components/primitives/Link.tsx @@ -1,10 +1,10 @@ import MuiLink, { LinkProps as MuiLinkProps } from '@mui/material/Link'; import { styled } from '@mui/material/styles'; +import clsx from 'clsx'; import NextLink, { LinkProps as NextLinkProps } from 'next/link'; import { useRouter } from 'next/router'; import * as React from 'react'; import { CustomMarket } from 'src/ui-config/marketsConfig'; -import clsx from 'clsx'; // Add support for the sx prop for consistency with the other branches. const Anchor = styled('a')({}); diff --git a/src/components/transactions/AssetInput.tsx b/src/components/transactions/AssetInput.tsx index 7e575e4dac..e53eb69930 100644 --- a/src/components/transactions/AssetInput.tsx +++ b/src/components/transactions/AssetInput.tsx @@ -1,9 +1,10 @@ +import { XCircleIcon } from '@heroicons/react/solid'; import { Trans } from '@lingui/macro'; import { Box, Button, - IconButton, FormControl, + IconButton, InputBase, ListItemText, MenuItem, @@ -11,7 +12,6 @@ import { SelectChangeEvent, Typography, } from '@mui/material'; -import { XCircleIcon } from '@heroicons/react/solid'; import React, { ReactNode } from 'react'; import NumberFormat, { NumberFormatProps } from 'react-number-format'; diff --git a/src/components/transactions/Borrow/BorrowActions.tsx b/src/components/transactions/Borrow/BorrowActions.tsx index 8f934b20c9..f9f85cb921 100644 --- a/src/components/transactions/Borrow/BorrowActions.tsx +++ b/src/components/transactions/Borrow/BorrowActions.tsx @@ -7,6 +7,7 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { optimizedPath } from 'src/utils/utils'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export interface BorrowActionsProps extends BoxProps { diff --git a/src/components/transactions/Borrow/BorrowModalContent.tsx b/src/components/transactions/Borrow/BorrowModalContent.tsx index 1cd118bebb..204eca9de3 100644 --- a/src/components/transactions/Borrow/BorrowModalContent.tsx +++ b/src/components/transactions/Borrow/BorrowModalContent.tsx @@ -5,17 +5,21 @@ import { valueToBigNumber, } from '@aave/math-utils'; import { Trans } from '@lingui/macro'; -import { Alert, Box, Checkbox, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material'; +import { Alert, Box, Checkbox, Typography } from '@mui/material'; import { useRef, useState } from 'react'; import { APYTypeTooltip } from 'src/components/infoTooltips/APYTypeTooltip'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Row } from 'src/components/primitives/Row'; +import { Warning } from 'src/components/primitives/Warning'; +import StyledToggleButton from 'src/components/StyledToggleButton'; +import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider'; import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { useModalContext } from 'src/hooks/useModal'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { ERC20TokenType } from 'src/libs/web3-data-provider/Web3Provider'; import { getMaxAmountAvailableToBorrow } from 'src/utils/getMaxAmountAvailableToBorrow'; + import { CapType } from '../../caps/helper'; import { AssetInput } from '../AssetInput'; import { GasEstimationError } from '../FlowCommons/GasEstimationError'; @@ -65,14 +69,14 @@ const BorrowModeSwitch = ({ align="flex-start" captionColor="text.secondary" > - setInterestRateMode(value)} sx={{ width: '100%', height: '36px', p: '2px', mt: 0.5 }} > - @@ -80,8 +84,8 @@ const BorrowModeSwitch = ({ Variable - - + @@ -89,8 +93,8 @@ const BorrowModeSwitch = ({ Stable - - + + ); }; @@ -218,7 +222,6 @@ export const BorrowModalContent = ({ return ( <> {borrowCap.determineWarningDisplay({ borrowCap })} - {debtCeiling.determineWarningDisplay({ debtCeiling })} {poolReserve.isIsolated && debtCeiling.determineWarningDisplay({ debtCeiling })} )} + + + Attention: Parameter changes via governance can alter your account health factor + and risk of liquidation. Follow the{' '} + Aave governance forum for updates. + + + { const { gasLimit, mainTxState: claimRewardsTxState, txError } = useModalContext(); const { user, reserves } = useAppDataContext(); const { currentChainId, currentMarketData, currentMarket } = useProtocolDataContext(); - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const [claimableUsd, setClaimableUsd] = useState('0'); const [selectedRewardSymbol, setSelectedRewardSymbol] = useState('all'); const [rewards, setRewards] = useState([]); @@ -140,7 +140,7 @@ export const ClaimRewardsModalContent = () => { return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} diff --git a/src/components/transactions/CollateralChange/CollateralChangeActions.tsx b/src/components/transactions/CollateralChange/CollateralChangeActions.tsx index 3179798965..cbdac00dee 100644 --- a/src/components/transactions/CollateralChange/CollateralChangeActions.tsx +++ b/src/components/transactions/CollateralChange/CollateralChangeActions.tsx @@ -5,6 +5,7 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { optimizedPath } from 'src/utils/utils'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export type CollateralChangeActionsProps = { diff --git a/src/components/transactions/CollateralChange/CollateralChangeModalContent.tsx b/src/components/transactions/CollateralChange/CollateralChangeModalContent.tsx index 8c405cec91..95e5694068 100644 --- a/src/components/transactions/CollateralChange/CollateralChangeModalContent.tsx +++ b/src/components/transactions/CollateralChange/CollateralChangeModalContent.tsx @@ -4,6 +4,7 @@ import { Alert, Typography } from '@mui/material'; import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider'; import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { useModalContext } from 'src/hooks/useModal'; + import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; import { TxSuccessView } from '../FlowCommons/Success'; diff --git a/src/components/transactions/Emode/EmodeActions.tsx b/src/components/transactions/Emode/EmodeActions.tsx index 4044e79387..c0f087dc03 100644 --- a/src/components/transactions/Emode/EmodeActions.tsx +++ b/src/components/transactions/Emode/EmodeActions.tsx @@ -3,6 +3,7 @@ import { Trans } from '@lingui/macro'; import { useTransactionHandler } from 'src/helpers/useTransactionHandler'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export type EmodeActionsProps = { diff --git a/src/components/transactions/Emode/EmodeModalContent.tsx b/src/components/transactions/Emode/EmodeModalContent.tsx index 33a7d2b991..85062276ac 100644 --- a/src/components/transactions/Emode/EmodeModalContent.tsx +++ b/src/components/transactions/Emode/EmodeModalContent.tsx @@ -17,6 +17,8 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; +import LightningBoltGradient from '/public/lightningBoltGradient.svg'; + import { TxErrorView } from '../FlowCommons/Error'; import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { TxSuccessView } from '../FlowCommons/Success'; @@ -26,7 +28,6 @@ import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; import { EmodeActions } from './EmodeActions'; import { getEmodeMessage } from './EmodeNaming'; import { EmodeSelect } from './EmodeSelect'; -import LightningBoltGradient from '/public/lightningBoltGradient.svg'; export enum ErrorType { EMODE_DISABLED_LIQUIDATION, @@ -71,7 +72,7 @@ export const EmodeModalContent = ({ mode }: EmodeModalContentProps) => { userReserves, } = useAppDataContext(); const { currentChainId } = useProtocolDataContext(); - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const currentTimestamp = useCurrentTimestamp(1); const { gasLimit, mainTxState: emodeTxState, txError } = useModalContext(); @@ -177,7 +178,7 @@ export const EmodeModalContent = ({ mode }: EmodeModalContentProps) => { return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} diff --git a/src/components/transactions/Emode/EmodeSelect.tsx b/src/components/transactions/Emode/EmodeSelect.tsx index 284771e0f5..cab71e9a03 100644 --- a/src/components/transactions/Emode/EmodeSelect.tsx +++ b/src/components/transactions/Emode/EmodeSelect.tsx @@ -5,6 +5,7 @@ import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; import * as React from 'react'; import { EmodeCategory } from 'src/helpers/types'; + import { getEmodeMessage } from './EmodeNaming'; export type EmodeSelectProps = { diff --git a/src/components/transactions/Faucet/FaucetActions.tsx b/src/components/transactions/Faucet/FaucetActions.tsx index 29f0b53783..62062924e9 100644 --- a/src/components/transactions/Faucet/FaucetActions.tsx +++ b/src/components/transactions/Faucet/FaucetActions.tsx @@ -3,6 +3,7 @@ import { useTransactionHandler } from 'src/helpers/useTransactionHandler'; import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export type FaucetActionsProps = { diff --git a/src/components/transactions/Faucet/FaucetModalContent.tsx b/src/components/transactions/Faucet/FaucetModalContent.tsx index a409a70d59..91b7620cfa 100644 --- a/src/components/transactions/Faucet/FaucetModalContent.tsx +++ b/src/components/transactions/Faucet/FaucetModalContent.tsx @@ -2,6 +2,7 @@ import { mintAmountsPerToken } from '@aave/contract-helpers'; import { normalize } from '@aave/math-utils'; import { Trans } from '@lingui/macro'; import { useModalContext } from 'src/hooks/useModal'; + import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; import { TxSuccessView } from '../FlowCommons/Success'; diff --git a/src/components/transactions/FlowCommons/ModalWrapper.tsx b/src/components/transactions/FlowCommons/ModalWrapper.tsx index c76bb48e8a..c7d225bce7 100644 --- a/src/components/transactions/FlowCommons/ModalWrapper.tsx +++ b/src/components/transactions/FlowCommons/ModalWrapper.tsx @@ -13,6 +13,7 @@ import { usePermissions } from 'src/hooks/usePermissions'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { getNetworkConfig, isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig'; + import { TxModalTitle } from '../FlowCommons/TxModalTitle'; import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; import { TxErrorView } from './Error'; @@ -45,7 +46,7 @@ export const ModalWrapper: React.FC<{ requiredPermission, keepWrappedSymbol, }) => { - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { walletBalances } = useWalletBalances(); const { currentChainId: marketChainId, @@ -94,7 +95,7 @@ export const ModalWrapper: React.FC<{ {!mainTxState.success && ( )} - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( { - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { daveTokens: { aave, stkAave }, } = useAaveTokensProviderContext(); @@ -101,7 +101,7 @@ export const GovDelegationModalContent = () => { return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} diff --git a/src/components/transactions/GovVote/GovVoteActions.tsx b/src/components/transactions/GovVote/GovVoteActions.tsx index 83300e1ca4..1556389157 100644 --- a/src/components/transactions/GovVote/GovVoteActions.tsx +++ b/src/components/transactions/GovVote/GovVoteActions.tsx @@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'; import { useTransactionHandler } from 'src/helpers/useTransactionHandler'; import { useGovernanceDataProvider } from 'src/hooks/governance-data-provider/GovernanceDataProvider'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export type GovVoteActionsProps = { diff --git a/src/components/transactions/GovVote/GovVoteModalContent.tsx b/src/components/transactions/GovVote/GovVoteModalContent.tsx index 059ed90403..46f0ad477f 100644 --- a/src/components/transactions/GovVote/GovVoteModalContent.tsx +++ b/src/components/transactions/GovVote/GovVoteModalContent.tsx @@ -5,6 +5,7 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { governanceConfig } from 'src/ui-config/governanceConfig'; import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; + import { TxErrorView } from '../FlowCommons/Error'; import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { TxSuccessView } from '../FlowCommons/Success'; @@ -35,7 +36,7 @@ export const GovVoteModalContent = ({ support, power: votingPower, }: GovVoteModalContentProps) => { - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { gasLimit, mainTxState: txState, txError } = useModalContext(); const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); @@ -77,7 +78,7 @@ export const GovVoteModalContent = ({ return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} {blockingError !== undefined && ( diff --git a/src/components/transactions/RateSwitch/RateSwitchActions.tsx b/src/components/transactions/RateSwitch/RateSwitchActions.tsx index 340d42caa3..1ccd91c29b 100644 --- a/src/components/transactions/RateSwitch/RateSwitchActions.tsx +++ b/src/components/transactions/RateSwitch/RateSwitchActions.tsx @@ -6,6 +6,7 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { optimizedPath } from 'src/utils/utils'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export type RateSwitchActionsProps = { diff --git a/src/components/transactions/RateSwitch/RateSwitchModalContent.tsx b/src/components/transactions/RateSwitch/RateSwitchModalContent.tsx index ba370fdf17..86bb783846 100644 --- a/src/components/transactions/RateSwitch/RateSwitchModalContent.tsx +++ b/src/components/transactions/RateSwitch/RateSwitchModalContent.tsx @@ -3,8 +3,8 @@ import { valueToBigNumber } from '@aave/math-utils'; import { Trans } from '@lingui/macro'; import { Alert } from '@mui/material'; import { useModalContext } from 'src/hooks/useModal'; -import { GasEstimationError } from '../FlowCommons/GasEstimationError'; +import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; import { TxSuccessView } from '../FlowCommons/Success'; import { diff --git a/src/components/transactions/Repay/CollateralRepayModalContent.tsx b/src/components/transactions/Repay/CollateralRepayModalContent.tsx index a2bd93087c..f4983da8c5 100644 --- a/src/components/transactions/Repay/CollateralRepayModalContent.tsx +++ b/src/components/transactions/Repay/CollateralRepayModalContent.tsx @@ -1,7 +1,12 @@ import { InterestRate } from '@aave/contract-helpers'; import { valueToBigNumber } from '@aave/math-utils'; +import { ArrowDownIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; +import { Box, SvgIcon, Typography } from '@mui/material'; +import BigNumber from 'bignumber.js'; import { useRef, useState } from 'react'; +import { PriceImpactTooltip } from 'src/components/infoTooltips/PriceImpactTooltip'; +import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { ComputedReserveData, ComputedUserReserveData, @@ -11,24 +16,20 @@ import { useModalContext } from 'src/hooks/useModal'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { SwapVariant, useCollateralRepaySwap } from 'src/hooks/useSwap'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { ListSlippageButton } from 'src/modules/dashboard/lists/SlippageList'; +import { calculateHFAfterRepay } from 'src/utils/hfUtils'; + import { Asset, AssetInput } from '../AssetInput'; +import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; +import { TxSuccessView } from '../FlowCommons/Success'; import { DetailsHFLine, DetailsNumberLineWithSub, TxModalDetails, } from '../FlowCommons/TxModalDetails'; -import { CollateralRepayActions } from './CollateralRepayActions'; -import BigNumber from 'bignumber.js'; -import { calculateHFAfterRepay } from 'src/utils/hfUtils'; -import { Box, Typography, SvgIcon } from '@mui/material'; -import { GasEstimationError } from '../FlowCommons/GasEstimationError'; -import { TxSuccessView } from '../FlowCommons/Success'; -import { ListSlippageButton } from 'src/modules/dashboard/lists/SlippageList'; -import { ArrowDownIcon } from '@heroicons/react/outline'; -import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; -import { PriceImpactTooltip } from 'src/components/infoTooltips/PriceImpactTooltip'; import { ErrorType, flashLoanNotAvailable, useFlashloan } from '../utils'; +import { CollateralRepayActions } from './CollateralRepayActions'; export function CollateralRepayModalContent({ poolReserve, diff --git a/src/components/transactions/Repay/RepayActions.tsx b/src/components/transactions/Repay/RepayActions.tsx index 75013bfebe..b35d4c9916 100644 --- a/src/components/transactions/Repay/RepayActions.tsx +++ b/src/components/transactions/Repay/RepayActions.tsx @@ -9,6 +9,7 @@ import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { permitByChainAndToken } from 'src/ui-config/permitConfig'; import { optimizedPath } from 'src/utils/utils'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export interface RepayActionProps extends BoxProps { diff --git a/src/components/transactions/Repay/RepayModal.tsx b/src/components/transactions/Repay/RepayModal.tsx index cf324fa367..5ebaf5f3db 100644 --- a/src/components/transactions/Repay/RepayModal.tsx +++ b/src/components/transactions/Repay/RepayModal.tsx @@ -5,6 +5,7 @@ import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvide import { ModalContextType, ModalType, useModalContext } from 'src/hooks/useModal'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig'; + import { BasicModal } from '../../primitives/BasicModal'; import { ModalWrapper } from '../FlowCommons/ModalWrapper'; import { CollateralRepayModalContent } from './CollateralRepayModalContent'; diff --git a/src/components/transactions/Repay/RepayModalContent.tsx b/src/components/transactions/Repay/RepayModalContent.tsx index ce1d997af6..1041fe6d35 100644 --- a/src/components/transactions/Repay/RepayModalContent.tsx +++ b/src/components/transactions/Repay/RepayModalContent.tsx @@ -16,6 +16,7 @@ import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvide import { useModalContext } from 'src/hooks/useModal'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; + import { Asset, AssetInput } from '../AssetInput'; import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; diff --git a/src/components/transactions/Repay/RepayTypeSelector.tsx b/src/components/transactions/Repay/RepayTypeSelector.tsx index 7eeec06524..97981ea306 100644 --- a/src/components/transactions/Repay/RepayTypeSelector.tsx +++ b/src/components/transactions/Repay/RepayTypeSelector.tsx @@ -1,5 +1,7 @@ -import { Box, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material'; import { Trans } from '@lingui/macro'; +import { Box, Typography } from '@mui/material'; +import StyledToggleButton from 'src/components/StyledToggleButton'; +import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; export enum RepayType { @@ -21,25 +23,28 @@ export function RepayTypeSelector({ Repay with - setRepayType(value)} sx={{ width: '100%', height: '36px', p: '2px' }} > - + Wallet balance - + - + Collateral - - + + ); } diff --git a/src/components/transactions/Stake/StakeModalContent.tsx b/src/components/transactions/Stake/StakeModalContent.tsx index dd2bdf0986..ed9cbc4a95 100644 --- a/src/components/transactions/Stake/StakeModalContent.tsx +++ b/src/components/transactions/Stake/StakeModalContent.tsx @@ -33,7 +33,7 @@ type StakingType = 'aave' | 'bpt'; export const StakeModalContent = ({ stakeAssetName, icon }: StakeProps) => { const data = useStakeData(); const stakeData = data.stakeGeneralResult?.stakeGeneralUIData[stakeAssetName as StakingType]; - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { gasLimit, mainTxState: txState, txError } = useModalContext(); const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); @@ -97,7 +97,7 @@ export const StakeModalContent = ({ stakeAssetName, icon }: StakeProps) => { return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} diff --git a/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx b/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx index 50bda2a4d2..bbe87247ec 100644 --- a/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx +++ b/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx @@ -34,7 +34,7 @@ type StakingType = 'aave' | 'bpt'; export const StakeCooldownModalContent = ({ stakeAssetName }: StakeCooldownProps) => { const { stakeUserResult, stakeGeneralResult } = useStakeData(); - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { gasLimit, mainTxState: txState, txError } = useModalContext(); const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); @@ -108,7 +108,7 @@ export const StakeCooldownModalContent = ({ stakeAssetName }: StakeCooldownProps return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} diff --git a/src/components/transactions/StakeRewardClaim/StakeRewardClaimActions.tsx b/src/components/transactions/StakeRewardClaim/StakeRewardClaimActions.tsx index 7c27cde0fc..f4dfbd7005 100644 --- a/src/components/transactions/StakeRewardClaim/StakeRewardClaimActions.tsx +++ b/src/components/transactions/StakeRewardClaim/StakeRewardClaimActions.tsx @@ -1,8 +1,9 @@ import { Trans } from '@lingui/macro'; import { BoxProps } from '@mui/material'; +import { useStakeTxBuilderContext } from 'src/hooks/useStakeTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { useTransactionHandler } from '../../../helpers/useTransactionHandler'; -import { useStakeTxBuilderContext } from 'src/hooks/useStakeTxBuilder'; import { TxActionsWrapper } from '../TxActionsWrapper'; export interface StakeRewardClaimActionProps extends BoxProps { diff --git a/src/components/transactions/StakeRewardClaim/StakeRewardClaimModal.tsx b/src/components/transactions/StakeRewardClaim/StakeRewardClaimModal.tsx index 83fbcf3566..e339658dda 100644 --- a/src/components/transactions/StakeRewardClaim/StakeRewardClaimModal.tsx +++ b/src/components/transactions/StakeRewardClaim/StakeRewardClaimModal.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { ModalType, useModalContext } from 'src/hooks/useModal'; + import { BasicModal } from '../../primitives/BasicModal'; import { StakeRewardClaimModalContent } from './StakeRewardClaimModalContent'; diff --git a/src/components/transactions/StakeRewardClaim/StakeRewardClaimModalContent.tsx b/src/components/transactions/StakeRewardClaim/StakeRewardClaimModalContent.tsx index 80b786e46b..326b63a688 100644 --- a/src/components/transactions/StakeRewardClaim/StakeRewardClaimModalContent.tsx +++ b/src/components/transactions/StakeRewardClaim/StakeRewardClaimModalContent.tsx @@ -1,20 +1,21 @@ -import React from 'react'; -import { Typography } from '@mui/material'; import { normalize } from '@aave/math-utils'; +import { Trans } from '@lingui/macro'; +import { Typography } from '@mui/material'; +import React from 'react'; +import { useStakeData } from 'src/hooks/stake-data-provider/StakeDataProvider'; +import { useModalContext } from 'src/hooks/useModal'; +import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { stakeConfig } from 'src/ui-config/stakeConfig'; import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; + import { TxErrorView } from '../FlowCommons/Error'; +import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { TxSuccessView } from '../FlowCommons/Success'; -import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; -import { TxModalTitle } from '../FlowCommons/TxModalTitle'; import { DetailsNumberLineWithSub, TxModalDetails } from '../FlowCommons/TxModalDetails'; -import { GasEstimationError } from '../FlowCommons/GasEstimationError'; -import { Trans } from '@lingui/macro'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; -import { useStakeData } from 'src/hooks/stake-data-provider/StakeDataProvider'; -import { stakeConfig } from 'src/ui-config/stakeConfig'; +import { TxModalTitle } from '../FlowCommons/TxModalTitle'; +import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; import { StakeRewardClaimActions } from './StakeRewardClaimActions'; -import { useModalContext } from 'src/hooks/useModal'; -import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; export type StakeRewardClaimProps = { stakeAssetName: string; @@ -29,7 +30,7 @@ type StakingType = 'aave' | 'bpt'; export const StakeRewardClaimModalContent = ({ stakeAssetName }: StakeRewardClaimProps) => { const data = useStakeData(); const stakeData = data.stakeGeneralResult?.stakeGeneralUIData[stakeAssetName as StakingType]; - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { gasLimit, mainTxState: txState, txError } = useModalContext(); const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); @@ -88,7 +89,7 @@ export const StakeRewardClaimModalContent = ({ stakeAssetName }: StakeRewardClai return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} {blockingError !== undefined && ( diff --git a/src/components/transactions/Supply/SupplyModalContent.tsx b/src/components/transactions/Supply/SupplyModalContent.tsx index ea63d560f5..e127e70fb5 100644 --- a/src/components/transactions/Supply/SupplyModalContent.tsx +++ b/src/components/transactions/Supply/SupplyModalContent.tsx @@ -9,11 +9,13 @@ import { Typography } from '@mui/material'; import BigNumber from 'bignumber.js'; import React, { useRef, useState } from 'react'; import { CollateralType } from 'src/helpers/types'; +import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { useModalContext } from 'src/hooks/useModal'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { ERC20TokenType } from 'src/libs/web3-data-provider/Web3Provider'; import { getMaxAmountAvailableToSupply } from 'src/utils/getMaxAmountAvailableToSupply'; import { isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig'; + import { useAppDataContext } from '../../../hooks/app-data-provider/useAppDataProvider'; import { CapType } from '../../caps/helper'; import { AssetInput } from '../AssetInput'; @@ -29,11 +31,9 @@ import { } from '../FlowCommons/TxModalDetails'; import { AAVEWarning } from '../Warnings/AAVEWarning'; import { AMPLWarning } from '../Warnings/AMPLWarning'; -import { HarmonyWarning } from '../Warnings/HarmonyWarning'; import { IsolationModeWarning } from '../Warnings/IsolationModeWarning'; import { SNXWarning } from '../Warnings/SNXWarning'; import { SupplyActions } from './SupplyActions'; -import { useAssetCaps } from 'src/hooks/useAssetCaps'; export enum ErrorType { CAP_REACHED, @@ -217,7 +217,6 @@ export const SupplyModalContent = ({ poolReserve.symbol === 'AAVE' && isFeatureEnabled.staking(currentMarketData) && } {poolReserve.symbol === 'SNX' && !maxAmountToSupply.eq('0') && } - {currentNetworkConfig.name === 'Harmony' && } { diff --git a/src/components/transactions/Swap/SwapModalContent.tsx b/src/components/transactions/Swap/SwapModalContent.tsx index 8f99dcc008..ad8e02bd19 100644 --- a/src/components/transactions/Swap/SwapModalContent.tsx +++ b/src/components/transactions/Swap/SwapModalContent.tsx @@ -1,28 +1,29 @@ -import React, { useRef, useState } from 'react'; -import { - ComputedUserReserveData, - useAppDataContext, -} from '../../../hooks/app-data-provider/useAppDataProvider'; -import { SwapActions } from './SwapActions'; -import { Typography, Box, SvgIcon } from '@mui/material'; -import BigNumber from 'bignumber.js'; -import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { SwitchVerticalIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; -import { remainingCap } from 'src/utils/getMaxAmountAvailableToSupply'; -import { useSwap } from 'src/hooks/useSwap'; +import { Box, SvgIcon, Typography } from '@mui/material'; +import BigNumber from 'bignumber.js'; +import React, { useRef, useState } from 'react'; +import { PriceImpactTooltip } from 'src/components/infoTooltips/PriceImpactTooltip'; +import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Asset, AssetInput } from 'src/components/transactions/AssetInput'; -import { TxModalDetails } from 'src/components/transactions/FlowCommons/TxModalDetails'; import { GasEstimationError } from 'src/components/transactions/FlowCommons/GasEstimationError'; +import { TxModalDetails } from 'src/components/transactions/FlowCommons/TxModalDetails'; import { useModalContext } from 'src/hooks/useModal'; -import { TxSuccessView } from '../FlowCommons/Success'; -import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; +import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { useSwap } from 'src/hooks/useSwap'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { ListSlippageButton } from 'src/modules/dashboard/lists/SlippageList'; +import { remainingCap } from 'src/utils/getMaxAmountAvailableToSupply'; import { calculateHFAfterSwap } from 'src/utils/hfUtils'; + +import { + ComputedUserReserveData, + useAppDataContext, +} from '../../../hooks/app-data-provider/useAppDataProvider'; import { ModalWrapperProps } from '../FlowCommons/ModalWrapper'; +import { TxSuccessView } from '../FlowCommons/Success'; import { ErrorType, flashLoanNotAvailable, useFlashloan } from '../utils'; -import { PriceImpactTooltip } from 'src/components/infoTooltips/PriceImpactTooltip'; -import { SwitchVerticalIcon } from '@heroicons/react/outline'; -import { ListSlippageButton } from 'src/modules/dashboard/lists/SlippageList'; +import { SwapActions } from './SwapActions'; import { SwapModalDetails } from './SwapModalDetails'; export type SupplyProps = { diff --git a/src/components/transactions/Swap/SwapModalDetails.tsx b/src/components/transactions/Swap/SwapModalDetails.tsx index 9300b158a5..d6fe7b38b8 100644 --- a/src/components/transactions/Swap/SwapModalDetails.tsx +++ b/src/components/transactions/Swap/SwapModalDetails.tsx @@ -1,18 +1,18 @@ -import React from 'react'; -import { ComputedUserReserveData } from '../../../hooks/app-data-provider/useAppDataProvider'; -import { Typography, Box, SvgIcon, useTheme } from '@mui/material'; +import { valueToBigNumber } from '@aave/math-utils'; +import { ArrowNarrowRightIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; - +import { Box, SvgIcon, Typography, useTheme } from '@mui/material'; +import React from 'react'; +import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; +import { Row } from 'src/components/primitives/Row'; +import { TokenIcon } from 'src/components/primitives/TokenIcon'; import { DetailsHFLine, DetailsIncentivesLine, DetailsNumberLine, } from 'src/components/transactions/FlowCommons/TxModalDetails'; -import { Row } from 'src/components/primitives/Row'; -import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; -import { ArrowNarrowRightIcon } from '@heroicons/react/outline'; -import { TokenIcon } from 'src/components/primitives/TokenIcon'; -import { valueToBigNumber } from '@aave/math-utils'; + +import { ComputedUserReserveData } from '../../../hooks/app-data-provider/useAppDataProvider'; export type SupplyModalDetailsProps = { showHealthFactor: boolean; diff --git a/src/components/transactions/TxActionsWrapper.tsx b/src/components/transactions/TxActionsWrapper.tsx index 67b2837e3d..26c3f0b846 100644 --- a/src/components/transactions/TxActionsWrapper.tsx +++ b/src/components/transactions/TxActionsWrapper.tsx @@ -1,11 +1,13 @@ import { Trans } from '@lingui/macro'; -import { Box, BoxProps, Button, CircularProgress } from '@mui/material'; +import { Box, BoxProps, Button, CircularProgress, Typography } from '@mui/material'; +import isEmpty from 'lodash/isEmpty'; import { ReactNode } from 'react'; import { TxStateType, useModalContext } from 'src/hooks/useModal'; -import isEmpty from 'lodash/isEmpty'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { TxAction } from 'src/ui-config/errorMapping'; + import { LeftHelperText } from './FlowCommons/LeftHelperText'; import { RightHelperText } from './FlowCommons/RightHelperText'; -import { TxAction } from 'src/ui-config/errorMapping'; interface TxActionsWrapperProps extends BoxProps { actionInProgressText: ReactNode; @@ -41,6 +43,7 @@ export const TxActionsWrapper = ({ ...rest }: TxActionsWrapperProps) => { const { txError, retryWithApproval } = useModalContext(); + const { watchModeOnlyAddress } = useWeb3Context(); const hasApprovalError = requiresApproval && txError && txError.txAction === TxAction.APPROVAL && txError.actionBlocked; @@ -86,14 +89,14 @@ export const TxActionsWrapper = ({ return ( - {requiresApproval && ( + {requiresApproval && !watchModeOnlyAddress && ( )} - {approvalParams && ( + {approvalParams && !watchModeOnlyAddress && ( + {watchModeOnlyAddress && ( + + Watch-only mode. Connect to a wallet to perform transactions. + + )} ); }; diff --git a/src/components/transactions/UnStake/UnStakeActions.tsx b/src/components/transactions/UnStake/UnStakeActions.tsx index dc6ab4748b..b5802edd02 100644 --- a/src/components/transactions/UnStake/UnStakeActions.tsx +++ b/src/components/transactions/UnStake/UnStakeActions.tsx @@ -1,8 +1,9 @@ import { Trans } from '@lingui/macro'; import { BoxProps } from '@mui/material'; +import { useStakeTxBuilderContext } from 'src/hooks/useStakeTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { useTransactionHandler } from '../../../helpers/useTransactionHandler'; -import { useStakeTxBuilderContext } from 'src/hooks/useStakeTxBuilder'; import { TxActionsWrapper } from '../TxActionsWrapper'; export interface UnStakeActionProps extends BoxProps { diff --git a/src/components/transactions/UnStake/UnStakeModal.tsx b/src/components/transactions/UnStake/UnStakeModal.tsx index 6e99993b90..c2fee6a6b7 100644 --- a/src/components/transactions/UnStake/UnStakeModal.tsx +++ b/src/components/transactions/UnStake/UnStakeModal.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { ModalType, useModalContext } from 'src/hooks/useModal'; + import { BasicModal } from '../../primitives/BasicModal'; import { UnStakeModalContent } from './UnStakeModalContent'; diff --git a/src/components/transactions/UnStake/UnStakeModalContent.tsx b/src/components/transactions/UnStake/UnStakeModalContent.tsx index 0252e607f2..49e653fac0 100644 --- a/src/components/transactions/UnStake/UnStakeModalContent.tsx +++ b/src/components/transactions/UnStake/UnStakeModalContent.tsx @@ -1,22 +1,23 @@ -import React, { useRef, useState } from 'react'; -import { Typography } from '@mui/material'; -import { AssetInput } from '../AssetInput'; import { normalize, valueToBigNumber } from '@aave/math-utils'; +import { Trans } from '@lingui/macro'; +import { Typography } from '@mui/material'; +import { parseUnits } from 'ethers/lib/utils'; +import React, { useRef, useState } from 'react'; +import { useStakeData } from 'src/hooks/stake-data-provider/StakeDataProvider'; +import { useModalContext } from 'src/hooks/useModal'; +import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { stakeConfig } from 'src/ui-config/stakeConfig'; import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; + +import { AssetInput } from '../AssetInput'; import { TxErrorView } from '../FlowCommons/Error'; +import { GasEstimationError } from '../FlowCommons/GasEstimationError'; import { TxSuccessView } from '../FlowCommons/Success'; -import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; import { TxModalTitle } from '../FlowCommons/TxModalTitle'; -import { GasEstimationError } from '../FlowCommons/GasEstimationError'; -import { Trans } from '@lingui/macro'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; -import { useStakeData } from 'src/hooks/stake-data-provider/StakeDataProvider'; -import { stakeConfig } from 'src/ui-config/stakeConfig'; -import { UnStakeActions } from './UnStakeActions'; import { GasStation } from '../GasStation/GasStation'; -import { parseUnits } from 'ethers/lib/utils'; -import { useModalContext } from 'src/hooks/useModal'; -import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; +import { UnStakeActions } from './UnStakeActions'; export type UnStakeProps = { stakeAssetName: string; @@ -32,7 +33,7 @@ type StakingType = 'aave' | 'bpt'; export const UnStakeModalContent = ({ stakeAssetName, icon }: UnStakeProps) => { const data = useStakeData(); const stakeData = data.stakeGeneralResult?.stakeGeneralUIData[stakeAssetName as StakingType]; - const { chainId: connectedChainId } = useWeb3Context(); + const { chainId: connectedChainId, watchModeOnlyAddress } = useWeb3Context(); const { gasLimit, mainTxState: txState, txError } = useModalContext(); const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); @@ -96,7 +97,7 @@ export const UnStakeModalContent = ({ stakeAssetName, icon }: UnStakeProps) => { return ( <> - {isWrongNetwork && ( + {isWrongNetwork && !watchModeOnlyAddress && ( )} { - return ( - - - As per the community vote, ETH borrowing on the Ethereum Market has been paused ahead of - the merge to mitigate liquidity risk.{' '} - - Learn more - - {'.'} - - - } - > - - - - - ); -}; diff --git a/src/components/transactions/Warnings/HarmonyWarning.tsx b/src/components/transactions/Warnings/HarmonyWarning.tsx deleted file mode 100644 index 6d2820fea0..0000000000 --- a/src/components/transactions/Warnings/HarmonyWarning.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Trans } from '@lingui/macro'; -import { Link, Typography } from '@mui/material'; - -import { Warning } from '../../primitives/Warning'; - -interface HarmonyWarningProps { - learnMore?: boolean; // Modify wording on link text -} - -export const HarmonyWarning = ({ learnMore }: HarmonyWarningProps) => { - return ( - - - - Due to the Horizon bridge exploit, certain assets on the Harmony network are not at parity - with Ethereum, which affects the Aave V3 Harmony market.{' '} - - {learnMore ? 'Learn More' : 'Join the community discussion'} - - - - - ); -}; diff --git a/src/components/transactions/Warnings/MarketWarning.tsx b/src/components/transactions/Warnings/MarketWarning.tsx new file mode 100644 index 0000000000..7228d50e37 --- /dev/null +++ b/src/components/transactions/Warnings/MarketWarning.tsx @@ -0,0 +1,55 @@ +import { Trans } from '@lingui/macro'; +import { Link, Typography } from '@mui/material'; + +import { Warning } from '../../primitives/Warning'; + +const WarningMessage = ({ market }: { market: string }) => { + if (market === 'Harmony') { + return ( + + Due to the Horizon bridge exploit, certain assets on the Harmony network are not at parity + with Ethereum, which affects the Aave V3 Harmony market. + + ); + } else if (market === 'Fantom') { + return Per the community, the Fantom market has been frozen.; + } else { + return <>; + } +}; + +const getLink = (market: string, forum: boolean | undefined): string => { + if (market === 'Harmony') { + if (forum) { + return 'https://governance.aave.com/t/harmony-horizon-bridge-exploit-consequences-to-aave-v3-harmony/8614'; + } else { + return 'https://snapshot.org/#/aave.eth/proposal/0x81a78109941e5e0ac6cb5ebf82597c839c20ad6821a8c3ff063dba39032533d4'; + } + } else if (market === 'Fantom') { + if (forum) { + return 'https://governance.aave.com/t/arc-aave-v3-fantom-freeze-reserves/9166'; + } else { + return 'https://snapshot.org/#/aave.eth/proposal/0xeefcd76e523391a14cfd0a79b531ea0a3faf0eb4a058e255fac13a2d224cc647'; + } + } else { + return ''; + } +}; + +interface MarketWarningProps { + marketName: string; + forum?: boolean; +} + +export const MarketWarning = ({ marketName, forum }: MarketWarningProps) => { + return ( + + + {' '} + + {forum ? Join the community discussion : Learn more} + + + + ); +}; diff --git a/src/components/transactions/Warnings/SupplyCapWarning.tsx b/src/components/transactions/Warnings/SupplyCapWarning.tsx index c359f9f765..6417d9f942 100644 --- a/src/components/transactions/Warnings/SupplyCapWarning.tsx +++ b/src/components/transactions/Warnings/SupplyCapWarning.tsx @@ -1,5 +1,6 @@ import { Trans } from '@lingui/macro'; import { AssetCapData } from 'src/hooks/useAssetCaps'; + import { Link } from '../../primitives/Link'; import { Warning } from '../../primitives/Warning'; diff --git a/src/components/transactions/Withdraw/WithdrawActions.tsx b/src/components/transactions/Withdraw/WithdrawActions.tsx index 6774d18d7c..0eae035995 100644 --- a/src/components/transactions/Withdraw/WithdrawActions.tsx +++ b/src/components/transactions/Withdraw/WithdrawActions.tsx @@ -6,6 +6,7 @@ import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useTxBuilderContext } from 'src/hooks/useTxBuilder'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { optimizedPath } from 'src/utils/utils'; + import { TxActionsWrapper } from '../TxActionsWrapper'; export interface WithdrawActionsProps extends BoxProps { diff --git a/src/hooks/governance-data-provider/AaveTokensDataProvider.tsx b/src/hooks/governance-data-provider/AaveTokensDataProvider.tsx index e7375be429..f5ca68e48d 100644 --- a/src/hooks/governance-data-provider/AaveTokensDataProvider.tsx +++ b/src/hooks/governance-data-provider/AaveTokensDataProvider.tsx @@ -1,11 +1,12 @@ -import React, { useContext } from 'react'; import { WalletBalanceProvider } from '@aave/contract-helpers'; import { normalize } from '@aave/math-utils'; +import React, { useContext } from 'react'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { governanceConfig } from 'src/ui-config/governanceConfig'; import { getProvider } from 'src/utils/marketsAndNetworksConfig'; -import { useProtocolDataContext } from '../useProtocolDataContext'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; + import { usePolling } from '../usePolling'; +import { useProtocolDataContext } from '../useProtocolDataContext'; type WalletBalanceProviderContext = { daveTokens: { aave: string; aAave: string; stkAave: string }; diff --git a/src/hooks/stake-data-provider/_useStakeDataRPC.tsx b/src/hooks/stake-data-provider/_useStakeDataRPC.tsx index 2ecbec82a0..6f065bc134 100644 --- a/src/hooks/stake-data-provider/_useStakeDataRPC.tsx +++ b/src/hooks/stake-data-provider/_useStakeDataRPC.tsx @@ -1,15 +1,16 @@ import { UiStakeDataProvider } from '@aave/contract-helpers'; -import { usePolling } from '../usePolling'; import { useApolloClient } from '@apollo/client'; +import { stakeConfig } from 'src/ui-config/stakeConfig'; import { getProvider } from 'src/utils/marketsAndNetworksConfig'; + +import { usePolling } from '../usePolling'; +import { useProtocolDataContext } from '../useProtocolDataContext'; import { C_StakeGeneralUiDataDocument, C_StakeGeneralUiDataQuery, C_StakeUserUiDataDocument, C_StakeUserUiDataQuery, } from './graphql/hooks'; -import { stakeConfig } from 'src/ui-config/stakeConfig'; -import { useProtocolDataContext } from '../useProtocolDataContext'; export function _useStakeDataRPC(currentAccount: string, chainId: number, skip = false) { const { cache } = useApolloClient(); diff --git a/src/hooks/useAddressAllowed.tsx b/src/hooks/useAddressAllowed.tsx index 907f6ad6be..98d332800d 100644 --- a/src/hooks/useAddressAllowed.tsx +++ b/src/hooks/useAddressAllowed.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; + import { usePolling } from './usePolling'; export interface AddressAllowedResult { diff --git a/src/hooks/useAssetCaps.tsx b/src/hooks/useAssetCaps.tsx index ffb38b6514..25b73b2664 100644 --- a/src/hooks/useAssetCaps.tsx +++ b/src/hooks/useAssetCaps.tsx @@ -1,12 +1,13 @@ -import { useContext, createContext, ReactNode } from 'react'; import { valueToBigNumber } from '@aave/math-utils'; -import { SupplyCapWarning } from 'src/components/transactions/Warnings/SupplyCapWarning'; +import { createContext, ReactNode, useContext } from 'react'; +import { BorrowCapMaxedTooltip } from 'src/components/infoTooltips/BorrowCapMaxedTooltip'; +import { DebtCeilingMaxedTooltip } from 'src/components/infoTooltips/DebtCeilingMaxedTooltip'; +import { SupplyCapMaxedTooltip } from 'src/components/infoTooltips/SupplyCapMaxedTooltip'; import { BorrowCapWarning } from 'src/components/transactions/Warnings/BorrowCapWarning'; import { DebtCeilingWarning } from 'src/components/transactions/Warnings/DebtCeilingWarning'; +import { SupplyCapWarning } from 'src/components/transactions/Warnings/SupplyCapWarning'; + import { ComputedReserveData } from './app-data-provider/useAppDataProvider'; -import { SupplyCapMaxedTooltip } from 'src/components/infoTooltips/SupplyCapMaxedTooltip'; -import { BorrowCapMaxedTooltip } from 'src/components/infoTooltips/BorrowCapMaxedTooltip'; -import { DebtCeilingMaxedTooltip } from 'src/components/infoTooltips/DebtCeilingMaxedTooltip'; type WarningDisplayProps = { supplyCap?: AssetCapData; diff --git a/src/hooks/usePermissions.tsx b/src/hooks/usePermissions.tsx index d4f6a398f1..75eec0c41c 100644 --- a/src/hooks/usePermissions.tsx +++ b/src/hooks/usePermissions.tsx @@ -1,9 +1,9 @@ +import { PERMISSION, PermissionManager } from '@aave/contract-helpers'; import React, { useContext, useEffect, useState } from 'react'; -import { PermissionManager, PERMISSION } from '@aave/contract-helpers'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { getProvider, isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig'; import { useProtocolDataContext } from './useProtocolDataContext'; -import { getProvider, isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig'; -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; type PermissionsContext = { permissions: PERMISSION[]; diff --git a/src/hooks/useReservesHistory.tsx b/src/hooks/useReservesHistory.tsx index 95bd3e7200..ebc57f5fe1 100644 --- a/src/hooks/useReservesHistory.tsx +++ b/src/hooks/useReservesHistory.tsx @@ -1,8 +1,20 @@ +/** + * This hook is used for getting historical reserve data, and it is primarily used with charts. + * In particular, this hook is used in the ApyGraph. + */ import dayjs from 'dayjs'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { makeCancelable } from 'src/utils/utils'; +export const reserveRateTimeRangeOptions = ['1m', '6m', '1y'] as const; +export type ReserveRateTimeRange = typeof reserveRateTimeRangeOptions[number]; + +type RatesHistoryParams = { + from: number; + resolutionInHours: number; +}; + type APIResponse = { liquidityRate_avg: number; variableBorrowRate_avg: number; @@ -11,12 +23,15 @@ type APIResponse = { x: { year: number; month: number; date: number; hours: number }; }; -const fetchStats = async (address: string, endpointURL: string) => { - const thirtyDaysAgo = dayjs().subtract(45, 'day').unix(); +const fetchStats = async ( + address: string, + timeRange: ReserveRateTimeRange, + endpointURL: string +) => { + const { from, resolutionInHours } = resolutionForTimeRange(timeRange); try { - const result = await fetch( - `${endpointURL}?reserveId=${address}&from=${thirtyDaysAgo}&resolutionInHours=6` - ); + const url = `${endpointURL}?reserveId=${address}&from=${from}&resolutionInHours=${resolutionInHours}`; + const result = await fetch(url); const json = await result.json(); return json; } catch (e) { @@ -24,6 +39,28 @@ const fetchStats = async (address: string, endpointURL: string) => { } }; +// TODO: there is possibly a bug here, as Polygon and Avalanche v2 data is coming through empty and erroring in our hook +// The same asset without the 'from' field comes through just fine. +const resolutionForTimeRange = (timeRange: ReserveRateTimeRange): RatesHistoryParams => { + switch (timeRange) { + case '1m': + return { + from: dayjs().subtract(30, 'day').unix(), + resolutionInHours: 6, + }; + case '6m': + return { + from: dayjs().subtract(6, 'month').unix(), + resolutionInHours: 24, + }; + case '1y': + return { + from: dayjs().subtract(1, 'year').unix(), + resolutionInHours: 24, + }; + } +}; + export type FormattedReserveHistoryItem = { date: number; liquidityRate: number; @@ -38,20 +75,27 @@ const BROKEN_ASSETS = [ ]; // TODO: api need to be altered to expect chainId underlying asset and poolConfig -export function useReserveRatesHistory(reserveAddress: string) { +export function useReserveRatesHistory(reserveAddress: string, timeRange: ReserveRateTimeRange) { const { currentNetworkConfig } = useProtocolDataContext(); const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); const [data, setData] = useState([]); - useEffect(() => { + const refetchData = useCallback<() => () => void>(() => { + // reset + setLoading(true); + setError(false); + setData([]); + if ( reserveAddress && currentNetworkConfig.ratesHistoryApiUrl && !BROKEN_ASSETS.includes(reserveAddress) ) { const cancelable = makeCancelable( - fetchStats(reserveAddress, currentNetworkConfig.ratesHistoryApiUrl) + fetchStats(reserveAddress, timeRange, currentNetworkConfig.ratesHistoryApiUrl) ); + cancelable.promise .then((data: APIResponse[]) => { setData( @@ -65,16 +109,28 @@ export function useReserveRatesHistory(reserveAddress: string) { ); setLoading(false); }) - .catch((e) => console.log('error fetching result', e)); + .catch((e) => { + console.error('useReservesHistory(): Failed to fetch historical reserve data.', e); + setError(true); + setLoading(false); + }); + return cancelable.cancel; - } else { - setLoading(false); } - }, [reserveAddress, currentNetworkConfig]); + + setLoading(false); + return () => null; + }, [reserveAddress, timeRange, currentNetworkConfig]); + + useEffect(() => { + const cancel = refetchData(); + return () => cancel(); + }, [refetchData]); return { loading, data, - error: BROKEN_ASSETS.includes(reserveAddress) || (!loading && data.length === 0), + error: error || BROKEN_ASSETS.includes(reserveAddress) || (!loading && data.length === 0), + refetch: refetchData, }; } diff --git a/src/hooks/useSwap.ts b/src/hooks/useSwap.ts index 6c484c325b..ac1c495823 100644 --- a/src/hooks/useSwap.ts +++ b/src/hooks/useSwap.ts @@ -1,16 +1,17 @@ +import { ChainId } from '@aave/contract-helpers'; +import { BigNumberZeroDecimal, normalize, normalizeBN, valueToBigNumber } from '@aave/math-utils'; import { - constructPartialSDK, + constructBuildTx, constructFetchFetcher, constructGetRate, - constructBuildTx, + constructPartialSDK, TransactionParams, } from '@paraswap/sdk'; -import { OptimalRate, SwapSide, ContractMethod } from 'paraswap-core'; +import { RateOptions } from '@paraswap/sdk/dist/rates'; +import { ContractMethod, OptimalRate, SwapSide } from 'paraswap-core'; import { useCallback, useEffect, useState } from 'react'; + import { ComputedReserveData } from './app-data-provider/useAppDataProvider'; -import { ChainId } from '@aave/contract-helpers'; -import { BigNumberZeroDecimal, normalize, normalizeBN, valueToBigNumber } from '@aave/math-utils'; -import { RateOptions } from '@paraswap/sdk/dist/rates'; const ParaSwap = (chainId: number) => { const fetcher = constructFetchFetcher(fetch); // alternatively constructFetchFetcher diff --git a/src/hooks/useWalletModal.tsx b/src/hooks/useWalletModal.tsx index cf17ba0946..e859dc02bd 100644 --- a/src/hooks/useWalletModal.tsx +++ b/src/hooks/useWalletModal.tsx @@ -11,15 +11,15 @@ export const WalletModalContext = createContext( ); export const WalletModalContextProvider: React.FC = ({ children }) => { - const { connected } = useWeb3Context(); + const { connected, watchModeOnlyAddress } = useWeb3Context(); const [isWalletModalOpen, setWalletModalOpen] = useState(false); useEffect(() => { - if (connected) { + if (connected || watchModeOnlyAddress) { setWalletModalOpen(false); } - }, [connected]); + }, [connected, watchModeOnlyAddress]); return ( ) => { - if (!connected) { + if (!connected && !watchModeOnlyAddress) { setWalletModalOpen(true); } else { setOpen(true); @@ -86,10 +93,10 @@ export default function WalletWidget({ open, setOpen, headerHeight }: WalletWidg }; const handleDisconnect = () => { - if (connected) { + if (connected || watchModeOnlyAddress) { disconnectWallet(); handleClose(); - localStorage.removeItem('mockWalletAddress'); + localStorage.removeItem('watchModeOnlyAddress'); } }; @@ -98,7 +105,12 @@ export default function WalletWidget({ open, setOpen, headerHeight }: WalletWidg handleClose(); }; - const hideWalletAccountText = xsm && (ENABLE_TESTNET || STAGING_ENV); + const handleSwitchWallet = (): void => { + setWalletModalOpen(true); + handleClose(); + }; + + const hideWalletAccountText = xsm && (ENABLE_TESTNET || STAGING_ENV || watchModeOnlyAddress); const accountAvatar = ( setUseBlockie(true)} /> + {watchModeOnlyAddress && ( + + + + )} ); @@ -146,47 +174,99 @@ export default function WalletWidget({ open, setOpen, headerHeight }: WalletWidg - - - setUseBlockie(true)} - /> - - - {ensNameAbbreviated && ( - - {ensNameAbbreviated} - - )} - - + + - {textCenterEllipsis(currentAccount, ensNameAbbreviated ? 12 : 7, 4)} - + {watchModeOnlyAddress && ( + + + + )} + setUseBlockie(true)} + /> + + + {ensNameAbbreviated && ( + + {ensNameAbbreviated} + + )} + + + {textCenterEllipsis(currentAccount, ensNameAbbreviated ? 12 : 7, 4)} + + + {watchModeOnlyAddress && ( + + Watch-only mode. + + )} + {!md && ( + + + + + )} @@ -273,37 +353,50 @@ export default function WalletWidget({ open, setOpen, headerHeight }: WalletWidg )} - - - - - - - - - Disconnect Wallet - - + {md && ( + <> + + + + + + + )} ); return ( <> - {md && connected && open ? ( + {md && (connected || watchModeOnlyAddress) && open ? ( ) : loading ? ( ) : ( + )} + + )} + + + ); +}; diff --git a/src/modules/markets/AssetSearchInput.tsx b/src/modules/markets/AssetSearchInput.tsx new file mode 100644 index 0000000000..f831e73564 --- /dev/null +++ b/src/modules/markets/AssetSearchInput.tsx @@ -0,0 +1,99 @@ +import { SearchIcon } from '@heroicons/react/outline'; +import { XCircleIcon } from '@heroicons/react/solid'; +import { Box, IconButton, InputBase, useMediaQuery, useTheme } from '@mui/material'; +import debounce from 'lodash/debounce'; +import { useMemo, useRef, useState } from 'react'; + +export interface AssetSearchInputProps { + onSearchTermChange: (value: string) => void; +} + +export const AssetSearchInput = ({ onSearchTermChange }: AssetSearchInputProps) => { + const inputEl = useRef(null); + const [searchTerm, setSearchTerm] = useState(''); + + const { breakpoints } = useTheme(); + const sm = useMediaQuery(breakpoints.down('sm')); + + const handleClear = () => { + setSearchTerm(''); + onSearchTermChange(''); + inputEl.current?.focus(); + }; + + const debounchedChangeHandler = useMemo(() => { + return debounce((value: string) => { + onSearchTermChange(value); + }, 300); + }, [onSearchTermChange]); + + if (sm) { + return ( + ({ + display: 'flex', + alignItems: 'center', + flexGrow: 1, + gap: 2, + border: `1px solid ${theme.palette.divider}`, + borderRadius: '6px', + height: '36px', + })} + > + + + + { + setSearchTerm(e.target.value); + debounchedChangeHandler(e.target.value); + }} + /> + handleClear()} + > + + + + ); + } else { + return ( + ({ + display: 'flex', + alignItems: 'center', + gap: 2, + border: `1px solid ${theme.palette.divider}`, + borderRadius: '6px', + height: '36px', + })} + > + + + + { + setSearchTerm(e.target.value); + debounchedChangeHandler(e.target.value); + }} + /> + handleClear()} + > + + + + ); + } +}; diff --git a/src/modules/markets/AssetsList.tsx b/src/modules/markets/AssetsList.tsx index 244450b3e5..ee5ed783a0 100644 --- a/src/modules/markets/AssetsList.tsx +++ b/src/modules/markets/AssetsList.tsx @@ -1,10 +1,10 @@ import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers'; import { Trans } from '@lingui/macro'; -import { Box, useMediaQuery } from '@mui/material'; +import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; import { useState } from 'react'; import { StableAPYTooltip } from 'src/components/infoTooltips/StableAPYTooltip'; import { VariableAPYTooltip } from 'src/components/infoTooltips/VariableAPYTooltip'; -import { HarmonyWarning } from 'src/components/transactions/Warnings/HarmonyWarning'; +import { MarketWarning } from 'src/components/transactions/Warnings/MarketWarning'; import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider'; import { fetchIconSymbolAndName } from 'src/ui-config/reservePatches'; @@ -13,6 +13,7 @@ import { ListHeaderTitle } from '../../components/lists/ListHeaderTitle'; import { ListHeaderWrapper } from '../../components/lists/ListHeaderWrapper'; import { ListWrapper } from '../../components/lists/ListWrapper'; import { useProtocolDataContext } from '../../hooks/useProtocolDataContext'; +import { AssetListTitle } from './AssetListTitle'; import { AssetsListItem } from './AssetsListItem'; import { AssetsListItemLoader } from './AssetsListItemLoader'; import { AssetsListMobileItem } from './AssetsListMobileItem'; @@ -23,9 +24,24 @@ export default function AssetsList() { const { currentMarketData, currentNetworkConfig } = useProtocolDataContext(); const isTableChangedToCards = useMediaQuery('(max-width:1125px)'); + const { breakpoints } = useTheme(); + const sm = useMediaQuery(breakpoints.down('sm')); + + const [sortName, setSortName] = useState(''); + const [sortDesc, setSortDesc] = useState(false); + const [searchTerm, setSearchTerm] = useState(''); const filteredData = reserves .filter((res) => res.isActive) + .filter((res) => { + if (!searchTerm) return true; + const term = searchTerm.toLowerCase().trim(); + return ( + res.symbol.toLowerCase().includes(term) || + res.name.toLowerCase().includes(term) || + res.underlyingAsset.toLowerCase().includes(term) + ); + }) .map((reserve) => ({ ...reserve, ...(reserve.isWrappedBaseAsset @@ -36,9 +52,6 @@ export default function AssetsList() { : {}), })); - const [sortName, setSortName] = useState(''); - const [sortDesc, setSortDesc] = useState(false); - if (sortDesc) { if (sortName === 'symbol') { filteredData.sort((a, b) => (a.symbol.toUpperCase() < b.symbol.toUpperCase() ? -1 : 1)); @@ -96,20 +109,29 @@ export default function AssetsList() { }, ]; + const marketFrozen = !reserves.some((reserve) => !reserve.isFrozen); + return ( - {currentMarketData.marketTitle} assets - + titleComponent={ + } - captionSize="h2" > - {currentNetworkConfig.name === 'Harmony' && ( + {marketFrozen && currentNetworkConfig.name === 'Harmony' && ( - + )} + + {marketFrozen && currentNetworkConfig.name === 'Fantom' && ( + + + + )} + {!isTableChangedToCards && ( {header.map((col) => ( @@ -158,6 +180,47 @@ export default function AssetsList() { ) ) )} + {!loading && filteredData?.length === 0 && ( + + {sm ? ( + + + No search results for + + + '{searchTerm}' + + + ) : ( + + No search results for '{searchTerm}' + + )} + + + We couldn't find any asset matching your search. Try again with a different asset + name, symbol, or address. + + + + )} ); } diff --git a/src/modules/markets/AssetsListItem.tsx b/src/modules/markets/AssetsListItem.tsx index 170475427a..ab18183029 100644 --- a/src/modules/markets/AssetsListItem.tsx +++ b/src/modules/markets/AssetsListItem.tsx @@ -1,10 +1,8 @@ import { Trans } from '@lingui/macro'; -import { Button, Typography, Box } from '@mui/material'; +import { Box, Button, Typography } from '@mui/material'; import { useRouter } from 'next/router'; import { ReserveSubheader } from 'src/components/ReserveSubheader'; -import { ETHBorrowWarning } from 'src/components/transactions/Warnings/ETHBorrowWarning'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; -import { CustomMarket } from 'src/ui-config/marketsConfig'; import { IncentivesCard } from '../../components/incentives/IncentivesCard'; import { AMPLWarning } from '../../components/infoTooltips/AMPLWarning'; @@ -45,9 +43,6 @@ export const AssetsListItem = ({ ...reserve }: ComputedReserveData) => { {reserve.symbol === 'AMPL' && } - {reserve.symbol === 'ETH' && currentMarket === CustomMarket.proto_mainnet && ( - - )} diff --git a/src/modules/markets/AssetsListMobileItem.tsx b/src/modules/markets/AssetsListMobileItem.tsx index 268b4cf27c..5ae9a16007 100644 --- a/src/modules/markets/AssetsListMobileItem.tsx +++ b/src/modules/markets/AssetsListMobileItem.tsx @@ -1,10 +1,10 @@ import { Trans } from '@lingui/macro'; -import { Button, Divider, Box } from '@mui/material'; +import { Box, Button, Divider } from '@mui/material'; import { StableAPYTooltip } from 'src/components/infoTooltips/StableAPYTooltip'; import { VariableAPYTooltip } from 'src/components/infoTooltips/VariableAPYTooltip'; import { ReserveSubheader } from 'src/components/ReserveSubheader'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; -import { CustomMarket } from 'src/ui-config/marketsConfig'; + import { IncentivesCard } from '../../components/incentives/IncentivesCard'; import { FormattedNumber } from '../../components/primitives/FormattedNumber'; import { Link, ROUTES } from '../../components/primitives/Link'; @@ -21,9 +21,6 @@ export const AssetsListMobileItem = ({ ...reserve }: ComputedReserveData) => { name={reserve.name} underlyingAsset={reserve.underlyingAsset} currentMarket={currentMarket} - showETHBorrowWarning={ - currentMarket === CustomMarket.proto_mainnet && reserve.symbol === 'ETH' - } > Total supplied} captionVariant="description" mb={3}> { const { reserves, loading } = useAppDataContext(); diff --git a/src/modules/reserve-overview/ReserveActions.tsx b/src/modules/reserve-overview/ReserveActions.tsx index 83af5569ba..0ebcf7db13 100644 --- a/src/modules/reserve-overview/ReserveActions.tsx +++ b/src/modules/reserve-overview/ReserveActions.tsx @@ -13,29 +13,31 @@ import { } from '@mui/material'; import React, { ReactNode } from 'react'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; +import { Warning } from 'src/components/primitives/Warning'; +import { MarketWarning } from 'src/components/transactions/Warnings/MarketWarning'; +import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWalletButton'; import { ComputedReserveData, useAppDataContext, } from 'src/hooks/app-data-provider/useAppDataProvider'; import { useWalletBalances } from 'src/hooks/app-data-provider/useWalletBalances'; +import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { useModalContext } from 'src/hooks/useModal'; import { usePermissions } from 'src/hooks/usePermissions'; +import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { assetCanBeBorrowedByUser, getMaxAmountAvailableToBorrow, } from 'src/utils/getMaxAmountAvailableToBorrow'; import { getMaxAmountAvailableToSupply } from 'src/utils/getMaxAmountAvailableToSupply'; + import { CapType } from '../../components/caps/helper'; import { AvailableTooltip } from '../../components/infoTooltips/AvailableTooltip'; -import { Row } from '../../components/primitives/Row'; import { Link, ROUTES } from '../../components/primitives/Link'; +import { Row } from '../../components/primitives/Row'; import { getEmodeMessage } from '../../components/transactions/Emode/EmodeNaming'; -import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; -import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWalletButton'; -import { Warning } from 'src/components/primitives/Warning'; -import { HarmonyWarning } from 'src/components/transactions/Warnings/HarmonyWarning'; -import { useAssetCaps } from 'src/hooks/useAssetCaps'; +import { WalletEmptyInfo } from '../dashboard/lists/SupplyAssetsList/WalletEmptyInfo'; const PaperWrapper = ({ children }: { children: ReactNode }) => { return ( @@ -61,7 +63,7 @@ export const ReserveActions = ({ underlyingAsset }: ReserveActionsProps) => { const { user, reserves, loading: loadingReserves, eModes } = useAppDataContext(); const { walletBalances, loading: loadingBalance } = useWalletBalances(); const { isPermissionsLoading } = usePermissions(); - const { currentNetworkConfig } = useProtocolDataContext(); + const { currentNetworkConfig, currentChainId } = useProtocolDataContext(); const { bridge, name: networkName } = currentNetworkConfig; const { supplyCap, borrowCap, debtCeiling } = useAssetCaps(); @@ -190,14 +192,12 @@ export const ReserveActions = ({ underlyingAsset }: ReserveActionsProps) => { ) : ( - - Your {networkName} wallet is empty. Purchase or transfer assets{' '} - {bridge && ( - - or use {{bridge.name}} to transfer your ETH assets. - - )} - + )} )} @@ -306,9 +306,14 @@ export const ReserveActions = ({ underlyingAsset }: ReserveActionsProps) => { - {currentNetworkConfig.name === 'Harmony' && ( + {poolReserve.isFrozen && currentNetworkConfig.name === 'Harmony' && ( + + + + )} + {poolReserve.isFrozen && currentNetworkConfig.name === 'Fantom' && ( - + )} diff --git a/src/modules/reserve-overview/ReserveConfiguration.tsx b/src/modules/reserve-overview/ReserveConfiguration.tsx index f1b3dc5f50..ec93719079 100644 --- a/src/modules/reserve-overview/ReserveConfiguration.tsx +++ b/src/modules/reserve-overview/ReserveConfiguration.tsx @@ -1,135 +1,36 @@ -import React, { ReactNode } from 'react'; +import { valueToBigNumber } from '@aave/math-utils'; +import { ExternalLinkIcon } from '@heroicons/react/solid'; import { Trans } from '@lingui/macro'; -import { - Alert, - Box, - BoxProps, - Divider, - SvgIcon, - Typography, - TypographyProps, - useMediaQuery, - useTheme, -} from '@mui/material'; -import Paper from '@mui/material/Paper'; -import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; -import { useReserveRatesHistory } from 'src/hooks/useReservesHistory'; -import { ParentSize } from '@visx/responsive'; -import { ApyChart } from '../reserve-overview/ApyChart'; -import { InterestRateModelChart } from '../reserve-overview/InterestRateModelChart'; -import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; -import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; +import { Box, Button, Divider, SvgIcon, Typography } from '@mui/material'; +import Paper from '@mui/material/Paper'; +import { CapsCircularStatus } from 'src/components/caps/CapsCircularStatus'; +import { DebtCeilingStatus } from 'src/components/caps/DebtCeilingStatus'; +import { IncentivesButton } from 'src/components/incentives/IncentivesButton'; +import { getFrozenProposalLink } from 'src/components/infoTooltips/FrozenTooltip'; +import { LiquidationPenaltyTooltip } from 'src/components/infoTooltips/LiquidationPenaltyTooltip'; +import { LiquidationThresholdTooltip } from 'src/components/infoTooltips/LiquidationThresholdTooltip'; +import { MaxLTVTooltip } from 'src/components/infoTooltips/MaxLTVTooltip'; import { StableAPYTooltip } from 'src/components/infoTooltips/StableAPYTooltip'; import { VariableAPYTooltip } from 'src/components/infoTooltips/VariableAPYTooltip'; -import { IncentivesButton } from 'src/components/incentives/IncentivesButton'; -import { ReserveOverviewBox } from 'src/components/ReserveOverviewBox'; -import { getEmodeMessage } from 'src/components/transactions/Emode/EmodeNaming'; -import LightningBoltGradient from '/public/lightningBoltGradient.svg'; +import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link, ROUTES } from 'src/components/primitives/Link'; -import { MaxLTVTooltip } from 'src/components/infoTooltips/MaxLTVTooltip'; -import { LiquidationThresholdTooltip } from 'src/components/infoTooltips/LiquidationThresholdTooltip'; -import { LiquidationPenaltyTooltip } from 'src/components/infoTooltips/LiquidationPenaltyTooltip'; +import { Warning } from 'src/components/primitives/Warning'; +import { ReserveOverviewBox } from 'src/components/ReserveOverviewBox'; import { ReserveSubheader } from 'src/components/ReserveSubheader'; -import { CustomMarket, frozenProposalMap } from 'src/utils/marketsAndNetworksConfig'; -import { CapsCircularStatus } from 'src/components/caps/CapsCircularStatus'; -import { DebtCeilingStatus } from 'src/components/caps/DebtCeilingStatus'; -import { ReserveFactorOverview } from 'src/modules/reserve-overview/ReserveFactorOverview'; -import { useAssetCaps } from 'src/hooks/useAssetCaps'; import { TextWithTooltip } from 'src/components/TextWithTooltip'; -import { valueToBigNumber } from '@aave/math-utils'; - -export const PanelRow: React.FC = (props) => ( - -); -export const PanelTitle: React.FC = (props) => ( - -); - -interface PanelColumnProps { - children?: ReactNode; -} - -export const PanelColumn = ({ children }: PanelColumnProps) => { - return ( - - {children} - - ); -}; - -interface PanelItemProps { - title: ReactNode; -} - -export const PanelItem: React.FC = ({ title, children }) => { - const theme = useTheme(); - const mdUp = useMediaQuery(theme.breakpoints.up('md')); +import { getEmodeMessage } from 'src/components/transactions/Emode/EmodeNaming'; +import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; +import { useAssetCaps } from 'src/hooks/useAssetCaps'; +import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { ReserveFactorOverview } from 'src/modules/reserve-overview/ReserveFactorOverview'; - return ( - `1px solid ${theme.palette.divider}`, - }, - } - : {}), - }} - > - {title} - {children} - - ); -}; +import LightningBoltGradient from '/public/lightningBoltGradient.svg'; -const ChartContainer: React.FC = (props) => ( - -); +import { CustomMarket, marketsData } from '../../utils/marketsAndNetworksConfig'; +import { ApyGraphContainer } from './graphs/ApyGraphContainer'; +import { InterestRateModelGraphContainer } from './graphs/InterestRateModelGraphContainer'; +import { PanelItem, PanelRow, PanelTitle } from './ReservePanels'; type ReserveConfigurationProps = { reserve: ComputedReserveData; @@ -137,13 +38,10 @@ type ReserveConfigurationProps = { export const ReserveConfiguration: React.FC = ({ reserve }) => { const { currentNetworkConfig, currentMarketData, currentMarket } = useProtocolDataContext(); - const renderCharts = !!currentNetworkConfig.ratesHistoryApiUrl; - const { data, error } = useReserveRatesHistory( - reserve - ? `${reserve.underlyingAsset}${currentMarketData.addresses.LENDING_POOL_ADDRESS_PROVIDER}` - : '' - ); // TODO: might make sense to move this to gql as well - + const { v3 } = marketsData[currentMarket as CustomMarket]; + // V3 and V2 Polygon will be enabled once support is added to API + const renderCharts = + !v3 && !!currentNetworkConfig.ratesHistoryApiUrl && currentMarket !== 'proto_polygon'; const { supplyCap, borrowCap, debtCeiling } = useAssetCaps(); const showSupplyCapStatus = reserve.supplyCap && reserve.supplyCap !== '0'; const showBorrowCapStatus = reserve.borrowCap && reserve.borrowCap !== '0'; @@ -164,47 +62,25 @@ export const ReserveConfiguration: React.FC = ({ rese - {reserve.symbol === 'WETH' && currentMarket === CustomMarket.proto_mainnet && ( - - - - As per the community vote, ETH borrowing on the Ethereum Market has been paused ahead - of the merge to mitigate liquidity risk.{' '} - - Learn more - - {'.'} - - - - )} - {reserve.isFrozen && ( - + - {reserve.symbol} is frozen due to an Aave community decision.{' '} + This asset is frozen due to an Aave community decision.{' '} More details - + )} Supply Info - + = ({ rese )} - {renderCharts && !error && (reserve.borrowingEnabled || Number(reserve.totalDebt) > 0) && ( - - - {(parent) => ( - - )} - - + {renderCharts && (reserve.borrowingEnabled || Number(reserve.totalDebt) > 0) && ( + )}
{reserve.isIsolated ? ( @@ -337,7 +206,7 @@ export const ReserveConfiguration: React.FC = ({ rese Collateral usage - + Asset can only be used as collateral in isolation mode only. @@ -349,7 +218,7 @@ export const ReserveConfiguration: React.FC = ({ rese Learn more - + ) : reserve.usageAsCollateralEnabled ? ( = ({ rese Collateral usage - + Asset cannot be used as collateral. - + )}
@@ -584,34 +453,14 @@ export const ReserveConfiguration: React.FC = ({ rese )}
- {renderCharts && !error && ( - - - {(parent) => ( - - )} - - + {renderCharts && ( + )} = ({ rese Interest rate model - - - {(parent) => ( - + + Utilization Rate} className="borderless"> + - )} - - + + + + +
)} diff --git a/src/modules/reserve-overview/ReservePanels.tsx b/src/modules/reserve-overview/ReservePanels.tsx new file mode 100644 index 0000000000..8f6cfee23b --- /dev/null +++ b/src/modules/reserve-overview/ReservePanels.tsx @@ -0,0 +1,73 @@ +import { Box, BoxProps, Typography, TypographyProps, useMediaQuery, useTheme } from '@mui/material'; +import type { ReactNode } from 'react'; + +export const PanelRow: React.FC = (props) => ( + +); +export const PanelTitle: React.FC = (props) => ( + +); + +interface PanelItemProps { + title: ReactNode; + className?: string; +} + +export const PanelItem: React.FC = ({ title, children, className }) => { + const theme = useTheme(); + const mdUp = useMediaQuery(theme.breakpoints.up('md')); + + return ( + `1px solid ${theme.palette.divider}`, + }, + } + : {}), + }} + className={className} + > + + {title} + + + {children} + + + ); +}; diff --git a/src/modules/reserve-overview/ReserveTopDetails.tsx b/src/modules/reserve-overview/ReserveTopDetails.tsx index 6f8b8ea6ee..167c142d98 100644 --- a/src/modules/reserve-overview/ReserveTopDetails.tsx +++ b/src/modules/reserve-overview/ReserveTopDetails.tsx @@ -12,10 +12,12 @@ import { useTheme, } from '@mui/material'; import { useRouter } from 'next/router'; +import { CircleIcon } from 'src/components/CircleIcon'; import { getMarketInfoById, MarketLogo } from 'src/components/MarketSwitcher'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link } from 'src/components/primitives/Link'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { TopInfoPanel } from '../../components/TopInfoPanel/TopInfoPanel'; import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem'; @@ -23,9 +25,6 @@ import { ComputedReserveData, useAppDataContext, } from '../../hooks/app-data-provider/useAppDataProvider'; - -import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; -import { CircleIcon } from 'src/components/CircleIcon'; import { AddTokenDropdown } from './AddTokenDropdown'; import { TokenLinkDropdown } from './TokenLinkDropdown'; diff --git a/src/modules/reserve-overview/TokenLinkDropdown.tsx b/src/modules/reserve-overview/TokenLinkDropdown.tsx index a3992284a9..ce6637d2d7 100644 --- a/src/modules/reserve-overview/TokenLinkDropdown.tsx +++ b/src/modules/reserve-overview/TokenLinkDropdown.tsx @@ -1,3 +1,4 @@ +import { ExternalLinkIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; import { Box, Menu, MenuItem, SvgIcon, Typography } from '@mui/material'; import * as React from 'react'; @@ -5,7 +6,6 @@ import { useState } from 'react'; import { CircleIcon } from 'src/components/CircleIcon'; import { TokenIcon } from 'src/components/primitives/TokenIcon'; import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; -import { ExternalLinkIcon } from '@heroicons/react/outline'; import { useProtocolDataContext } from 'src/hooks/useProtocolDataContext'; interface TokenLinkDropdownProps { diff --git a/src/modules/reserve-overview/ApyChart.tsx b/src/modules/reserve-overview/graphs/ApyGraph.tsx similarity index 52% rename from src/modules/reserve-overview/ApyChart.tsx rename to src/modules/reserve-overview/graphs/ApyGraph.tsx index a9c103a6bb..00686db80a 100644 --- a/src/modules/reserve-overview/ApyChart.tsx +++ b/src/modules/reserve-overview/graphs/ApyGraph.tsx @@ -1,34 +1,50 @@ -import React, { useMemo, useCallback, Fragment } from 'react'; -import { AreaClosed, Line, Bar, LinePath } from '@visx/shape'; +import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; +import { AxisBottom, AxisLeft } from '@visx/axis'; import { curveMonotoneX } from '@visx/curve'; -import { scaleTime, scaleLinear } from '@visx/scale'; -import { withTooltip, defaultStyles, TooltipWithBounds } from '@visx/tooltip'; -import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip'; import { localPoint } from '@visx/event'; -import { LinearGradient } from '@visx/gradient'; -import { AxisLeft, AxisBottom } from '@visx/axis'; -import { max, extent, bisector } from 'd3-array'; -import { timeFormat } from 'd3-time-format'; -import { Group } from '@visx/group'; -import { FormattedReserveHistoryItem } from 'src/hooks/useReservesHistory'; -import { useTheme, lighten } from '@mui/material'; -import { ChartLegend } from './ChartLegend'; import { GridRows } from '@visx/grid'; +import { Group } from '@visx/group'; +import { scaleLinear, scaleTime } from '@visx/scale'; +import { Bar, Line, LinePath } from '@visx/shape'; +import { defaultStyles, TooltipWithBounds, withTooltip } from '@visx/tooltip'; +import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip'; +import { bisector, extent, max } from 'd3-array'; +import { timeFormat } from 'd3-time-format'; +import React, { Fragment, useCallback, useMemo } from 'react'; +import { FormattedReserveHistoryItem, ReserveRateTimeRange } from 'src/hooks/useReservesHistory'; type TooltipData = FormattedReserveHistoryItem; -const background = '#3b6978'; -const accentColorDark = '#75daad'; -const tooltipStyles = { - ...defaultStyles, - background, - border: '1px solid white', - color: 'white', +/** + * Formats the given date for the specified time range to display in the tooltip. + * + * If the provided timeRange is '1m', the date will be formatted as 'MM DD, HH:MM UTC'. + * For example, a date of `Wed Sep 21 2022 18:00:00 GMT-0500 (Central Daylight Time)` will be formatted as `Sep 21, 18:00 UTC-05:00` + * + * If the provided timeRange is '6m' or '1y', the date will be formatted as 'MM DD, YYYY'. + * For example, a date of `Wed Sep 21 2022 18:00:00 GMT-0500 (Central Daylight Time)` will be formatted as `Sep 21, 2022` + * + * @param {Date} d - The date to format + * @param {ReserveRateTimeRange} timeRange - The time range of the graph + * + */ +const formatDate = (d: Date, timeRange: ReserveRateTimeRange) => { + if (timeRange === '1m') { + const formatted = timeFormat('%b %d, %H:%M UTC%Z'); + const date = formatted(d); + const offsetSign = date.toString().split('UTC')[1].split('')[0]; + const time = date.toString().split(offsetSign); + const hours = time[1].split('').slice(0, 2).join(''); + const mins = time[1].split('').slice(2, 4).join(''); + const formattedTime = `${hours}:${mins}`; + const formattedDate = `${time[0]}${offsetSign}${formattedTime}`; + return formattedDate; + } else { + const formatted = timeFormat('%b %d, %Y'); + return formatted(d); + } }; -// util -const formatDate = timeFormat("%b %d, '%y"); - // accessors const getDate = (d: FormattedReserveHistoryItem) => new Date(d.date); const bisectDate = bisector((d) => new Date(d.date)).left; @@ -42,28 +58,48 @@ export type AreaProps = { margin?: { top: number; right: number; bottom: number; left: number }; data: FormattedReserveHistoryItem[]; fields: { name: Field; color: string; text: string }[]; + selectedTimeRange: ReserveRateTimeRange; }; -export const ApyChart = withTooltip( +export const ApyGraph = withTooltip( ({ width, height, - margin = { top: 0, right: 10, bottom: 20, left: 40 }, + margin = { top: 20, right: 10, bottom: 20, left: 40 }, showTooltip, hideTooltip, tooltipData, tooltipLeft = 0, data, fields, + selectedTimeRange, }: AreaProps & WithTooltipProvidedProps) => { if (width < 10) return null; const theme = useTheme(); + const isXsm = useMediaQuery(theme.breakpoints.down('xsm')); + + // Tooltip Styles + const accentColorDark = theme.palette.mode === 'light' ? '#383D511F' : '#a5a8b647'; + const tooltipStyles = { + ...defaultStyles, + padding: '8px 12px', + boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)', + borderRadius: '4px', + fontSize: '12px', + lineHeight: '16px', + letterSpacing: '0.15px', + }; + const tooltipStylesDark = { + ...tooltipStyles, + background: theme.palette.background.default, + }; // bounds const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; // scales + const xAxisNumTicks = selectedTimeRange !== '6m' || isXsm ? 3 : 4; const dateScale = useMemo( () => scaleTime({ @@ -106,68 +142,61 @@ export const ApyChart = withTooltip( return ( <> - + {/* Horizontal Background Lines */} + + {/* Data Value Lines */} {fields.map((field) => ( - - - - data={data} - x={(d) => dateScale(getDate(d)) ?? 0} - y={(d) => yValueScale(getData(d, field.name)) ?? 0} - yScale={yValueScale} - strokeWidth={0} - fill={`url(#area-gradient-${field.name})`} - curve={curveMonotoneX} - /> - dateScale(getDate(d)) ?? 0} - y={(d) => yValueScale(getData(d, field.name)) ?? 0} - curve={curveMonotoneX} - /> - + dateScale(getDate(d)) ?? 0} + y={(d) => yValueScale(getData(d, field.name)) ?? 0} + curve={curveMonotoneX} + /> ))} + {/* X Axis */} ({ - fill: theme.palette.text.secondary, - fontSize: 8, - dx: -8, + fill: theme.palette.text.muted, + fontSize: 10, + textAnchor: 'middle', + dy: 4, })} /> + + {/* Y Axis */} `${value}%`} tickLabelProps={() => ({ - fill: theme.palette.text.secondary, - fontSize: 8, - dx: -margin.left + 8, + fill: theme.palette.text.muted, + fontSize: 10, + dx: -margin.left + 10, })} - numTicks={5} - tickFormat={(value) => `${(value as number).toFixed(2)} %`} /> + + {/* Background */} ( onMouseLeave={() => hideTooltip()} /> + {/* Tooltip */} {tooltipData && ( @@ -195,9 +225,7 @@ export const ApyChart = withTooltip( cx={tooltipLeft} cy={yValueScale(getData(tooltipData, field.name)) + 1} r={4} - fill="black" fillOpacity={0.1} - stroke="black" strokeOpacity={0.1} strokeWidth={2} pointerEvents="none" @@ -218,13 +246,36 @@ export const ApyChart = withTooltip( )} + + {/* Tooltip Info */} {tooltipData && (
- - {formatDate(getDate(tooltipData))} -
+ + + {formatDate(getDate(tooltipData), selectedTimeRange)} + {fields.map((field) => ( -
{getData(tooltipData, field.name).toFixed(2)} %
+ + + {field.text} + + + {getData(tooltipData, field.name).toFixed(2)}% + + ))}
diff --git a/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx b/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx new file mode 100644 index 0000000000..3b925ce488 --- /dev/null +++ b/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx @@ -0,0 +1,144 @@ +import { Trans } from '@lingui/macro'; +import { Box, Button, CircularProgress, Typography } from '@mui/material'; +import { ParentSize } from '@visx/responsive'; +import { useState } from 'react'; +import type { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; +import { ReserveRateTimeRange, useReserveRatesHistory } from 'src/hooks/useReservesHistory'; + +import { ApyGraph } from './ApyGraph'; +import { GraphLegend } from './GraphLegend'; +import { GraphTimeRangeSelector } from './GraphTimeRangeSelector'; + +type Field = 'liquidityRate' | 'stableBorrowRate' | 'variableBorrowRate'; + +type Fields = { name: Field; color: string; text: string }[]; + +type ApyGraphContainerKey = 'supply' | 'borrow'; + +type ApyGraphContainerProps = { + graphKey: ApyGraphContainerKey; + reserve: ComputedReserveData; + lendingPoolAddressProvider: string; +}; + +/** + * NOTES: + * This may not be named accurately. + * This container uses the same graph but with different fields, so we use a 'graphKey' to determine which to show + * This likely may need to be turned into two different container components if the graphs become wildly different. + * This graph gets its data via an external API call, thus having loading/error states + */ +export const ApyGraphContainer = ({ + graphKey, + reserve, + lendingPoolAddressProvider, +}: ApyGraphContainerProps): JSX.Element => { + const [selectedTimeRange, setSelectedTimeRange] = useState('1m'); + + const CHART_HEIGHT = 155; + const CHART_HEIGHT_LOADING_FIX = 3.5; + const reserveAddress = reserve ? `${reserve.underlyingAsset}${lendingPoolAddressProvider}` : ''; + const { data, loading, error, refetch } = useReserveRatesHistory( + reserveAddress, + selectedTimeRange + ); + + // Supply fields + const supplyFields: Fields = [{ name: 'liquidityRate', color: '#2EBAC6', text: 'Supply APR' }]; + + // Borrow fields + const borrowFields: Fields = [ + ...(reserve.stableBorrowRateEnabled + ? ([ + { + name: 'stableBorrowRate', + color: '#E7C6DF', + text: 'Borrow APR, stable', + }, + ] as const) + : []), + { + name: 'variableBorrowRate', + color: '#B6509E', + text: 'Borrow APR, variable', + }, + ]; + + const fields = graphKey === 'supply' ? supplyFields : borrowFields; + + const graphLoading = ( + + + + Loading data... + + + ); + + const graphError = ( + + + Something went wrong + + + Data couldn't be fetched, please reload graph. + + + + ); + + return ( + + + + + + {loading && graphLoading} + {error && graphError} + {!loading && !error && data.length > 0 && ( + + {({ width }) => ( + + )} + + )} + + ); +}; diff --git a/src/modules/reserve-overview/ChartLegend.tsx b/src/modules/reserve-overview/graphs/GraphLegend.tsx similarity index 54% rename from src/modules/reserve-overview/ChartLegend.tsx rename to src/modules/reserve-overview/graphs/GraphLegend.tsx index 87fa821b25..5a4679bd0e 100644 --- a/src/modules/reserve-overview/ChartLegend.tsx +++ b/src/modules/reserve-overview/graphs/GraphLegend.tsx @@ -1,27 +1,27 @@ -import { Typography, Box } from '@mui/material'; +import { Box, Typography } from '@mui/material'; -interface ChartLegendProps { +interface GraphLegendProps { labels: { text: string; color: string }[]; } -export function ChartLegend({ +export function GraphLegend({ labels = [ { text: 'test', color: '#000' }, { text: 'bla', color: '#ff0' }, ], -}: ChartLegendProps) { +}: GraphLegendProps) { return ( - + {labels.map((label) => ( - + {label.text} diff --git a/src/modules/reserve-overview/graphs/GraphTimeRangeSelector.tsx b/src/modules/reserve-overview/graphs/GraphTimeRangeSelector.tsx new file mode 100644 index 0000000000..a56484f81a --- /dev/null +++ b/src/modules/reserve-overview/graphs/GraphTimeRangeSelector.tsx @@ -0,0 +1,65 @@ +import { ToggleButtonGroup, ToggleButton, Typography } from '@mui/material'; +import { ReserveRateTimeRange, reserveRateTimeRangeOptions } from 'src/hooks/useReservesHistory'; + +export interface GraphTimeRangeSelectorProps { + disabled?: boolean; + timeRange: ReserveRateTimeRange; + handleTimeRangeChanged: (value: ReserveRateTimeRange) => void; +} + +export const GraphTimeRangeSelector = ({ + disabled = false, + timeRange, + handleTimeRangeChanged, +}: GraphTimeRangeSelectorProps) => { + const handleChange = ( + _event: React.MouseEvent, + newInterval: ReserveRateTimeRange + ) => { + if (newInterval !== null) { + handleTimeRangeChanged(newInterval); + } + }; + + return ( + + {reserveRateTimeRangeOptions.map((interval) => { + return ( + ({ + '&.MuiToggleButtonGroup-grouped:not(.Mui-selected)': { + borderRadius: '4px', + border: '0.5px solid transparent', + }, + '&.MuiToggleButtonGroup-grouped&.Mui-selected': { + borderRadius: '4px', + border: `0.5px solid ${theme.palette.divider}`, + boxShadow: '0px 2px 1px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.25)', + backgroundColor: 'background.surface', + }, + '&.MuiToggleButtonGroup-grouped&.Mui-disabled': { + color: 'text.disabled', + }, + })} + > + {interval} + + ); + })} + + ); +}; diff --git a/src/modules/reserve-overview/InterestRateModelChart.tsx b/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx similarity index 60% rename from src/modules/reserve-overview/InterestRateModelChart.tsx rename to src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx index 95e0a88b3a..196dbf775c 100644 --- a/src/modules/reserve-overview/InterestRateModelChart.tsx +++ b/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx @@ -1,29 +1,23 @@ -import React, { useMemo, useCallback, Fragment } from 'react'; -import { Line, Bar, LinePath } from '@visx/shape'; +import { normalizeBN, RAY, rayDiv, rayMul } from '@aave/math-utils'; +import { Trans } from '@lingui/macro'; +import { Box, Typography, useTheme } from '@mui/material'; +import { AxisBottom, AxisLeft } from '@visx/axis'; import { curveMonotoneX } from '@visx/curve'; -import { scaleLinear } from '@visx/scale'; -import { withTooltip, defaultStyles, TooltipWithBounds } from '@visx/tooltip'; -import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip'; import { localPoint } from '@visx/event'; -import { AxisLeft } from '@visx/axis'; -import { max, bisector } from 'd3-array'; +import { GridRows } from '@visx/grid'; import { Group } from '@visx/group'; -import { useTheme } from '@mui/material'; -import { normalizeBN, RAY, rayDiv, rayMul } from '@aave/math-utils'; -import { BigNumber } from 'bignumber.js'; +import { scaleLinear } from '@visx/scale'; +import { Bar, Line, LinePath } from '@visx/shape'; import { Text } from '@visx/text'; -import { ChartLegend } from './ChartLegend'; +import { defaultStyles, TooltipWithBounds, withTooltip } from '@visx/tooltip'; +import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip'; +import { BigNumber } from 'bignumber.js'; +import { bisector, max } from 'd3-array'; +import React, { Fragment, useCallback, useMemo } from 'react'; -type TooltipData = Rate; +import type { Fields } from './InterestRateModelGraphContainer'; -const background = '#3b6978'; -const accentColorDark = '#75daad'; -const tooltipStyles = { - ...defaultStyles, - background, - border: '1px solid white', - color: 'white', -}; +type TooltipData = Rate; type InterestRateModelType = { variableRateSlope1: string; @@ -46,8 +40,13 @@ type Rate = { // accessors const getDate = (d: Rate) => d.utilization; const bisectDate = bisector((d) => d.utilization).center; -const getVariableRate = (d: Rate) => d.variableRate * 100; -const getStableRate = (d: Rate) => d.stableRate * 100; +const getVariableBorrowRate = (d: Rate) => d.variableRate * 100; +const getStableBorrowRate = (d: Rate) => d.stableRate * 100; +const tooltipValueAccessors = { + stableBorrowRate: getStableBorrowRate, + variableBorrowRate: getVariableBorrowRate, + utilizationRate: () => 38, +}; const resolution = 200; const step = 100 / resolution; @@ -67,17 +66,20 @@ function getRates({ baseStableBorrowRate, }: InterestRateModelType): Rate[] { const rates: Rate[] = []; - const formattedOptimalUtilisationRate = normalizeBN(optimalUsageRatio, 25).toNumber(); + const formattedOptimalUtilizationRate = normalizeBN(optimalUsageRatio, 25).toNumber(); for (let i = 0; i <= resolution; i++) { const utilization = i * step; + // When zero if (utilization === 0) { rates.push({ stableRate: 0, variableRate: 0, utilization, }); - } else if (utilization < formattedOptimalUtilisationRate) { + } + // When hovering below optimal utilization rate, actual data + else if (utilization < formattedOptimalUtilizationRate) { const theoreticalStableAPY = normalizeBN( new BigNumber(baseStableBorrowRate).plus( rayDiv(rayMul(stableRateSlope1, normalizeBN(utilization, -25)), optimalUsageRatio) @@ -95,7 +97,9 @@ function getRates({ variableRate: theoreticalVariableAPY, utilization, }); - } else { + } + // When hovering above optimal utilization rate, hypothetical predictions + else { const excess = rayDiv( normalizeBN(utilization, -25).minus(optimalUsageRatio), RAY.minus(optimalUsageRatio) @@ -126,26 +130,47 @@ export type AreaProps = { width: number; height: number; margin?: { top: number; right: number; bottom: number; left: number }; + fields: Fields; reserve: InterestRateModelType; }; -export const InterestRateModelChart = withTooltip( +export const InterestRateModelGraph = withTooltip( ({ width, height, - margin = { top: 20 /** needed for absolute labels on top */, right: 10, bottom: 0, left: 40 }, + margin = { top: 20, right: 10, bottom: 20, left: 40 }, showTooltip, hideTooltip, tooltipData, tooltipLeft = 0, + fields, reserve, }: AreaProps & WithTooltipProvidedProps) => { if (width < 10) return null; const theme = useTheme(); - const data = useMemo(() => getRates(reserve), [JSON.stringify(reserve)]); + // Formatting + const formattedCurrentUtilizationRate = (parseFloat(reserve.utilizationRate) * 100).toFixed(2); + const formattedOptimalUtilizationRate = normalizeBN(reserve.optimalUsageRatio, 25).toNumber(); - const utilizationColor = theme.palette.mode === 'dark' ? '#fff' : '#000'; + // Tooltip Styles + const accentColorDark = theme.palette.mode === 'light' ? '#383D511F' : '#a5a8b647'; + const tooltipStyles = { + ...defaultStyles, + padding: '8px 12px', + boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)', + borderRadius: '4px', + color: '#62677B', + fontSize: '12px', + lineHeight: '16px', + letterSpacing: '0.15px', + }; + const tooltipStylesDark = { + ...tooltipStyles, + background: theme.palette.background.default, + }; + + const data = useMemo(() => getRates(reserve), [JSON.stringify(reserve)]); // bounds const innerWidth = width - margin.left - margin.right; @@ -164,10 +189,10 @@ export const InterestRateModelChart = withTooltip( const yValueScale = useMemo(() => { const maxY = reserve.stableBorrowRateEnabled ? Math.max( - max(data, (d) => getStableRate(d)) as number, - max(data, (d) => getVariableRate(d)) as number + max(data, (d) => getStableBorrowRate(d)) as number, + max(data, (d) => getVariableBorrowRate(d)) as number ) - : (max(data, (d) => getVariableRate(d)) as number); + : (max(data, (d) => getVariableBorrowRate(d)) as number); return scaleLinear({ range: [innerHeight, 0], domain: [0, (maxY || 0) * 1.1], @@ -209,45 +234,69 @@ export const InterestRateModelChart = withTooltip( return ( <> - + {/* Horizontal Background Lines */} + + + {/* Variable Borrow APR Line */} + dateScale(getDate(d)) ?? 0} + y={(d) => yValueScale(getVariableBorrowRate(d)) ?? 0} + curve={curveMonotoneX} + /> + + {/* Stable Borrow APR Line */} {reserve.stableBorrowRateEnabled && ( dateScale(getDate(d)) ?? 0} - y={(d) => yValueScale(getStableRate(d)) ?? 0} + y={(d) => yValueScale(getStableBorrowRate(d)) ?? 0} curve={curveMonotoneX} /> )} - dateScale(getDate(d)) ?? 0} - y={(d) => yValueScale(getVariableRate(d)) ?? 0} - curve={curveMonotoneX} + + {/* X Axis */} + ({ + fill: theme.palette.text.muted, + fontSize: 10, + textAnchor: 'middle', + })} + tickFormat={(n) => `${n}%`} /> + + {/* Y Axis */} ({ - fill: theme.palette.text.secondary, + fill: theme.palette.text.muted, fontSize: 8, - dx: -margin.left + 8, + dx: -margin.left + 10, })} - tickFormat={(value) => `${(value as number).toFixed(2)} %`} + numTicks={2} + tickFormat={(value) => `${value}%`} /> + + {/* Background */} ( onMouseMove={handleTooltip} onMouseLeave={() => hideTooltip()} /> + + {/* Current Utilization Line */} - - Current + {`Current ${formattedCurrentUtilizationRate}%`} + + {/* Optimal Utilization Line */} - Optimal + {`Optimal ${formattedOptimalUtilizationRate}%`} + + {/* Tooltip */} {tooltipData && ( + {/* Vertical line */} + {/* Variable borrow rate circle */} ( /> + {/* Stable borrow rate circle */} {reserve.stableBorrowRateEnabled && ( ( /> ( + {/* Tooltip Info */} {tooltipData && (
- -
Utilization: {tooltipData.utilization}%
- {reserve.stableBorrowRateEnabled && ( -
Stable: {getStableRate(tooltipData).toFixed(2)} %
- )} -
Variable: {getVariableRate(tooltipData).toFixed(2)} %
+ + + + Utilization Rate + + + {tooltipData.utilization}% + + + {fields.map((field) => ( + + + {field.text} + + + {tooltipValueAccessors[field.name](tooltipData).toFixed(2)}% + + + ))}
)} diff --git a/src/modules/reserve-overview/graphs/InterestRateModelGraphContainer.tsx b/src/modules/reserve-overview/graphs/InterestRateModelGraphContainer.tsx new file mode 100644 index 0000000000..a734a4d576 --- /dev/null +++ b/src/modules/reserve-overview/graphs/InterestRateModelGraphContainer.tsx @@ -0,0 +1,62 @@ +import { Box } from '@mui/material'; +import { ParentSize } from '@visx/responsive'; +import type { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; + +import { GraphLegend } from './GraphLegend'; +import { InterestRateModelGraph } from './InterestRateModelGraph'; + +type InteresetRateModelGraphContainerProps = { + reserve: ComputedReserveData; +}; + +export type Field = 'stableBorrowRate' | 'variableBorrowRate' | 'utilizationRate'; + +export type Fields = { name: Field; color: string; text: string }[]; + +// This graph takes in its data via props, thus having no loading/error states +export const InterestRateModelGraphContainer = ({ + reserve, +}: InteresetRateModelGraphContainerProps): JSX.Element => { + const CHART_HEIGHT = 155; + const fields: Fields = [ + { name: 'variableBorrowRate', text: 'Borrow APR, variable', color: '#B6509E' }, + ...(reserve.stableBorrowRateEnabled + ? ([{ name: 'stableBorrowRate', text: 'Borrow APR, stable', color: '#E7C6DF' }] as const) + : []), + ]; + + return ( + + + + + + {({ width }) => ( + + )} + + + ); +}; diff --git a/src/modules/staking/GetABPToken.tsx b/src/modules/staking/GetABPToken.tsx index 7fda49212a..55801abbb9 100644 --- a/src/modules/staking/GetABPToken.tsx +++ b/src/modules/staking/GetABPToken.tsx @@ -1,10 +1,11 @@ -import { useState } from 'react'; import { Trans } from '@lingui/macro'; import { Box, Button } from '@mui/material'; +import { useState } from 'react'; import { DarkTooltip } from 'src/components/infoTooltips/DarkTooltip'; -import { GetAPBTokenModal } from './GetABPTokenModal'; import { TokenIcon } from 'src/components/primitives/TokenIcon'; +import { GetAPBTokenModal } from './GetABPTokenModal'; + export const GetABPToken = () => { const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); diff --git a/src/modules/staking/GetABPTokenModal.tsx b/src/modules/staking/GetABPTokenModal.tsx index 0e46911c12..9a9a3fe3ba 100644 --- a/src/modules/staking/GetABPTokenModal.tsx +++ b/src/modules/staking/GetABPTokenModal.tsx @@ -1,8 +1,8 @@ import { ExternalLinkIcon } from '@heroicons/react/outline'; +import { Trans } from '@lingui/macro'; import { Box, Button, SvgIcon, Typography } from '@mui/material'; import { BasicModal } from 'src/components/primitives/BasicModal'; import { TokenIcon } from 'src/components/primitives/TokenIcon'; -import { Trans } from '@lingui/macro'; type GetAPBTokenModalProps = { open: boolean; diff --git a/src/modules/staking/StakingHeader.tsx b/src/modules/staking/StakingHeader.tsx index a81ca51631..22dc0808cb 100644 --- a/src/modules/staking/StakingHeader.tsx +++ b/src/modules/staking/StakingHeader.tsx @@ -3,10 +3,10 @@ import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel'; -import { Link } from '../../components/primitives/Link'; -import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem'; import EmissionIcon from '../../../public/icons/staking/emission-staking-icon.svg'; import TrustIcon from '../../../public/icons/staking/trust-staking-icon.svg'; +import { Link } from '../../components/primitives/Link'; +import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem'; interface StakingHeaderProps { tvl: string; diff --git a/src/static-build/ipfs.ts b/src/static-build/ipfs.ts index be93d1a984..3717881903 100644 --- a/src/static-build/ipfs.ts +++ b/src/static-build/ipfs.ts @@ -1,8 +1,10 @@ -import { join } from 'path'; -import { LowSync, JSONFileSync } from 'lowdb'; import lodash from 'lodash'; -import { CustomProposalType } from './proposal'; +import { JSONFileSync, LowSync } from 'lowdb'; +import { join } from 'path'; import { getProposalMetadata } from 'src/modules/governance/utils/getProposalMetadata'; +import { governanceConfig } from 'src/ui-config/governanceConfig'; + +import { CustomProposalType } from './proposal'; export interface IpfsType { id: number; @@ -40,7 +42,7 @@ export class Ipfs { const value = db.chain.get('ipfs').find({ id }).value(); if (value) return; if (!proposal) throw new Error(`error populating proposal ${id}`); - const ipfs = await getProposalMetadata(proposal.ipfsHash); + const ipfs = await getProposalMetadata(proposal.ipfsHash, governanceConfig.ipfsGateway); const newIpfs = { ...ipfs, originalHash: proposal.ipfsHash, id }; db.data.ipfs.push(newIpfs); return db.write(); diff --git a/src/static-build/ipfsFiles.json b/src/static-build/ipfsFiles.json index bdb4e22b67..1cbdf0f3f2 100644 --- a/src/static-build/ipfsFiles.json +++ b/src/static-build/ipfsFiles.json @@ -1484,6 +1484,35 @@ "ipfsHash": "QmQ9QmhRr8A7agxofKckXVN9CDgGE5tfLdYsAaf64BW8KD", "originalHash": "0x1ad639a0fe12d46fdce998b69b9f9d9d13108baf13d35fea601f1017c3723364", "id": 101 + }, + { + "aip": 102, + "title": "Risk Parameter Updates for Aave V2 (2022-09-22)", + "status": "Proposed", + "author": "Paul Lei, Sandeep Gupta, Jonathan Reem, Nick Cannon, Watson Fu, Tony Salvatore, Sarah Chen", + "shortDescription": "Risk parameter updates", + "discussions": "https://governance.aave.com/t/arc-risk-parameter-updates-for-aave-v2-and-aave-arc-fireblocks-2022-09-22/9983", + "created": "2021-09-27T00:00:00.000Z", + "preview": "## Simple Summary\n\nA proposal to adjust seven (7) …", + "basename": "AIP-102", + "description": "## Simple Summary\n\nA proposal to adjust seven (7) total risk parameters, including Liquidation Threshold and Loan To Value, across four (4) Aave V2 assets. \n\n## Abstract\nThis proposal is a batch update of risk parameters to align with the [Moderate risk level](https://snapshot.org/#/aave.eth/proposal/QmT9T4Rfnh1Z2HYmMPXvtFotMK2vSxiXK8dKStg2b6n4gi) chosen by the Aave community. These parameter updates are a continuation of Gauntlet’s regular parameter recommendations. Our simulation engine has ingested the latest market data (outlined below) to recalibrate parameters for the Aave protocol. The community has aligned on a [Risk Off Framework](https://snapshot.org/#/aave.eth/proposal/bafkreigdmcfmwvnxfolpds4xkdicgrszgmknig7pz2r2t37tltupdpyfu4) regarding lowering liquidation thresholds.\n\n\n\n## Motivation\n\n\nThis set of parameter updates seeks to maintain the overall risk tolerance of the protocol while making risk trade-offs between specific assets.\n\nGauntlet's parameter recommendations are driven by an optimization function that balances 3 core metrics: insolvencies, liquidations, and borrow usage. Parameter recommendations seek to optimize for this objective function. Our agent-based simulations use a wide array of varied input data that changes on a daily basis (including but not limited to asset volatility, asset correlation, asset collateral usage, DEX / CEX liquidity, trading volume, expected market impact of trades, and liquidator behavior). Gauntlet's simulations tease out complex relationships between these inputs that cannot be simply expressed as heuristics. As such, the input metrics we show below can help understand why some of the param recs have been made but should not be taken as the only reason for recommendation. The individual collateral pages on the [Gauntlet Risk Dashboard](https://gov.gauntlet.network/aave) cover other key statistics and outputs from our simulations that can help with understanding interesting inputs and results related to our simulations. \n\nFor more details, please see [Gauntlet's Parameter Recommendation Methodology](https://medium.com/gauntlet-networks/gauntlets-parameter-recommendation-methodology-8591478a0c1c) and [Gauntlet's Model Methodology](https://medium.com/gauntlet-networks/gauntlets-model-methodology-ea150ff0bafd). \n\n### Supporting Data on Aave V2\n\n**Top 30 borrowers' aggregate positions & borrow usages**\n\n![](https://i.imgur.com/M7RmBhu.png)\n\n**Top 30 borrowers' entire supply**\n\n![](https://i.imgur.com/sSePhV4.png)\n\n**Top 30 borrowers' entire borrows**\n\n![](https://i.imgur.com/VvMhK26.png)\n\n\n**Top USDC non-recursive supplies and collateralization ratios:**\n\n![](https://i.imgur.com/qYutxwQ.png)\n\n**Top WETH non-recursive supplies and collateralization ratios:**\n\n![](https://i.imgur.com/qbdHyQv.png)\n\n**Top AAVE non-recursive supplies and collateralization ratios:**\n\n![](https://i.imgur.com/tu0XhpN.png)\n\n**Top UNI non-recursive supplies and collateralization ratios:**\n\n![](https://i.imgur.com/nHis6TW.png)\n\n## Aave V2 Parameter Changes Specification\n\nGauntlet's simulation engine will continue to adjust risk parameters to maintain protocol market risk at safe levels while optimizing for capital efficiency.\n\n![](https://i.imgur.com/jiBbDA0.png)\n\nAs shown in the below chart and dashboard screenshot, our simulations show that Aave can increase capital efficiency while also decreasing the risk of bad debt.\n\nThese liquidation threshold increases, assuming unit elasticity, result in a projected increase of $8.22M in total borrows across assets. The following chart breaks down the top projected borrow increases by symbol:\n\n![](https://i.imgur.com/jVqTfXL.png)\n\n\n\n## Risk Dashboard\n\n\n\nThe community should use Gauntlet's [Aave V2 Risk Dashboard](https://gov.gauntlet.network/aave) to understand better the updated parameter suggestions and general market risk in Aave V2. Gauntlet has also launched the [Aave Arc Risk Dashboard](https://gov.gauntlet.network/aave_arc).\n\nValue at Risk represents the 95th percentile **insolvency value** that occurs from simulations we run over a range of volatilities to approximate a tail event. \n\nLiquidations at Risk represents the 95th percentile **liquidation volume** that occurs from simulations we run over a range of volatilities to approximate a tail event.\n\n\n\n### Aave V2 Dashboard\n\n\n\n![](https://i.imgur.com/YFSkvU3.png)\n\n\n\n## Implementation\n\nThe proposal sets the liquidation bonus, LTV and liquidation threshold ratios by calling `configureReserveAsCollateral` on the `LendingPoolConfigurator` contract at `0x311Bb771e4F8952E6Da169b425E7e92d6Ac45756`, using the address and parameters specific to each token.\n\nThe full list of parameter updates can be found in the [forum](https://governance.aave.com/t/arc-risk-parameter-updates-for-aave-v2-and-aave-arc-fireblocks-2022-09-22/9983).\n\n## Copyright\n\nCopyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).\n\n*By approving this proposal, you agree that any services provided by Gauntlet shall be governed by the terms of service available at gauntlet.network/tos.*\n\n", + "ipfsHash": "QmU5ofkHiuuBYzz4JdYh3mLcYy8eybvg3gAWrqX3WBAPua", + "originalHash": "0x55588a2d7fa9c28509ea269bd1245ce25dbe2dffc45ae905e2d22d5862648ba9", + "id": 102 + }, + { + "title": "Ethereum v2 Reserve Factor - aFEI Holding Update", + "status": "Proposed", + "author": "Llama, Matthew Graham, defijesus", + "shortDescription": "Redeem aFEI for aDAI", + "discussions": "https://governance.aave.com/t/arc-ethereum-v2-reserve-factor-afei-holding-update/9401", + "created": "2022-09-28T00:00:00.000Z", + "preview": "# Simple Summary\n\nThis proposal proposes unwinding the aFEI holding …", + "basename": "7CDA90A3999E-AIP-REDEEM-AFEI-FOR-DAI", + "description": "\n\n# Simple Summary\n\nThis proposal proposes unwinding the aFEI holding for DAI and depositing the DAI into the Aave v2 Ethereum Pool DAI Reserve.\n\n# Abstract\n\nDue to recent events within the Tribe DAO community, the FEI stablecoin is being removed from circulation. This proposal details how Aave DAO will enable anyone with an EOA to convert the DAO’s aFEI holding to an aDAI holding. \n\nBased upon available FEI liquidity in the FEI Reserve, Aave DAO’s aFEI will be redeemed for FEI, swapped for DAI, and then deposited into the DAI Reserve on Ethereum. The Reserve Factor will transition from holding aFEI to aDAI over time. \n\n# Motivation\n\nAave DAO is currently holding 94.3% (624,866.75 units) of aFEI supply while there are only 80,069.50 units of FEI in the FEI Reserve available for redeeming aFEI to FEI, [2]. \n\nThe Reserve currently supports 662,518.93 units of aFEI, with 582,449.43 units of debt and 80,069.50 units available for lenders to redeem their collateral from or for users to borrow, [3,4]. Any user seeking to claim 80,069.50 units or less is able to do so now, but this may change as others start redeeming aFEI. \n\nMigrating Aave’s aFEI holding in the Reserve Factor to aDAI can not be performed in a single transaction and therefore must occur gradually over time as FEI liquidity becomes available.\n\n# Specification\n\nThe below provides a broad, high level summary of how any EOA calling the `swapAllAvailable` function from the Swapper contract will convert aFEI to aDAI. \n\nThe contract determines the maximum amount of aFEI to be redeemed by assessing the amount of aFEI held in the Reserve Factor and the amount FEI in the FEI Reserve. The lower of the two amounts will be used to redeem aFEI to FEI.\n\nAll FEI held within the Reserve Factor will then be swapped for DAI via Tribe DAO’s SimpleFeiDaiPSM. There are no fees, the exchange rate is fixed at 1:1 (Example: unwrapping wETH to ETH), and there is around $57.4 Million of liquidity at the time of writing, [1]. \n\nDAI is then deposited into the DAI Reserve and the Ethereum Reserve Factor will receive aDAI.\n\n# Test Cases\n\nThis proposal has been tested and peer reviewed by Bored Ghost Developing, [5].\n\nTest cases, payload, and Swapper can be found at reference [6], [7] & [8] respectively.\n\n## Implementation\n\nThe governance payload is deployed at [9], the Swapper contract is deployed at [10].\n\nThe payload gives the Swapper aFEI max allowance from the Aave Ethereum Reserve Factor and calls the `swapAllAvailable` function on the Swapper.\n\nWhen called, Swapper transfers all possible aFEI from the Aave Ethereum Reserve Factor ([0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c](https://etherscan.io/address/0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)) to the payload contract via the Aave Ecosystem Controller ([0x3d569673dAa0575c936c7c67c4E6AedA69CC630C](https://etherscan.io/address/0x3d569673dAa0575c936c7c67c4E6AedA69CC630C)) transfer function.\n\nThe proposal redeems aFEI for FEI via the FEI Reserve ([0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9](https://etherscan.io/address/0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9)).\n\nThe proposal redeems all available FEI for DAI via the Tribe DAO Fixed Price PSM ([0x2A188F9EB761F70ECEa083bA6c2A40145078dfc2](https://etherscan.io/address/0x2A188F9EB761F70ECEa083bA6c2A40145078dfc2)), depositing all DAI in the Aave V2 Ethereum DAI Reserve with the Aave Ethereum Reserve Factor ([0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c](0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)) as the beneficiary.\n\n# References\n\n1. [https://etherscan.io/address/0x7842186cdd11270c4af8c0a99a5e0589c7f249ce#code](https://etherscan.io/address/0x7842186cdd11270c4af8c0a99a5e0589c7f249ce#code)\n2. [https://etherscan.io/token/0x683923db55fead99a79fa01a27eec3cb19679cc3#balances](https://etherscan.io/token/0x683923db55fead99a79fa01a27eec3cb19679cc3#balances)\n3. [https://etherscan.io/address/0x683923db55fead99a79fa01a27eec3cb19679cc3](https://etherscan.io/address/0x683923db55fead99a79fa01a27eec3cb19679cc3)\n4. [https://tribe.fei.money/t/tip-121-proposal-for-the-future-of-the-tribe-dao/4475](https://tribe.fei.money/t/tip-121-proposal-for-the-future-of-the-tribe-dao/4475)\n5. [https://twitter.com/bgdlabs](https://twitter.com/bgdlabs)\n6. [https://github.com/llama-community/aave-redeem-fei/blob/main/src/test/ValidateRedeemFei.sol](https://github.com/llama-community/aave-redeem-fei/blob/main/src/test/ValidateRedeemFei.sol)\n7. [https://github.com/llama-community/aave-redeem-fei/blob/main/src/RedeemFei.sol](https://github.com/llama-community/aave-redeem-fei/blob/main/src/RedeemFei.sol)\n8. [https://github.com/llama-community/aave-redeem-fei/blob/main/src/AFeiToDaiSwapper.sol](https://github.com/llama-community/aave-redeem-fei/blob/main/src/AFeiToDaiSwapper.sol)\n9. [https://etherscan.io/address/0x8dfd2255a9d38c182a14f49afcb8a4a4763c6098#code](https://etherscan.io/address/0x8dfd2255a9d38c182a14f49afcb8a4a4763c6098#code)\n10. [https://etherscan.io/address/0x9a953ac1090c7014d00fd205d89c6ba1c219af8b#code](https://etherscan.io/address/0x9a953ac1090c7014d00fd205d89c6ba1c219af8b#code)\n\n# Copyright\n\nCopyright and related rights waived via CC0.\n", + "ipfsHash": "QmQvuQ2Z4Go9AKEWtuokyD682poSqpg4WWNJGbRHzHBTvY", + "originalHash": "0x267dbabe4b9efc5aaa4b883462eb81b98ad717f7d3db4720420f2a58aa3b2cf9", + "id": 103 } ] } \ No newline at end of file diff --git a/src/static-build/proposal.ts b/src/static-build/proposal.ts index 5d07a8a758..57296c1cb5 100644 --- a/src/static-build/proposal.ts +++ b/src/static-build/proposal.ts @@ -2,6 +2,7 @@ import { Proposal as ProposalType } from '@aave/contract-helpers'; import lodash from 'lodash'; import { JSONFileSync, LowSync } from 'lowdb'; import { join } from 'path'; + import { enhanceProposalWithTimes } from '../modules/governance/utils/formatProposal'; import { governanceContract } from '../modules/governance/utils/governanceProvider'; import { isProposalStateImmutable } from '../modules/governance/utils/immutableStates'; diff --git a/src/static-build/proposals.json b/src/static-build/proposals.json index c28c7dc43e..7e764e8ba4 100644 --- a/src/static-build/proposals.json +++ b/src/static-build/proposals.json @@ -4194,22 +4194,22 @@ ], "startBlock": 15528369, "endBlock": 15547569, - "executionTime": 0, - "forVotes": "269091824761021395693570", - "againstVotes": "694453214027670636", - "executed": false, + "executionTime": 1663436519, + "forVotes": "364979253634172094847598", + "againstVotes": "704453214027670636", + "executed": true, "canceled": false, "strategy": "0xb7e383ef9B1E9189Fc0F71fb30af8aa14377429e", - "state": "Active", + "state": "Executed", "minimumQuorum": "200", "minimumDiff": "50", - "executionTimeWithGracePeriod": 0, + "executionTimeWithGracePeriod": 1663868519, "proposalCreated": 15528369, "totalVotingSupply": "16000000000000000000000000", "ipfsHash": "0x0a387fa966f5616423bea53801a843496b1eac5cab5e6bc9426c0958e6496e77", "startTimestamp": 1663092505, "creationTimestamp": 1663092505, - "expirationTimestamp": 1663353955 + "expirationTimestamp": 1663361305 }, { "id": 101, @@ -4229,22 +4229,104 @@ ], "startBlock": 15529036, "endBlock": 15548236, - "executionTime": 0, - "forVotes": "102530687290713665338724", + "executionTime": 1663452623, + "forVotes": "503812619134689846812957", "againstVotes": "1547632949233611373", - "executed": false, + "executed": true, "canceled": false, "strategy": "0xb7e383ef9B1E9189Fc0F71fb30af8aa14377429e", - "state": "Active", + "state": "Executed", "minimumQuorum": "200", "minimumDiff": "50", - "executionTimeWithGracePeriod": 0, + "executionTimeWithGracePeriod": 1663884623, "proposalCreated": 15529036, "totalVotingSupply": "16000000000000000000000000", "ipfsHash": "0x1ad639a0fe12d46fdce998b69b9f9d9d13108baf13d35fea601f1017c3723364", "startTimestamp": 1663102442, "creationTimestamp": 1663102442, - "expirationTimestamp": 1663363293 + "expirationTimestamp": 1663371242 + }, + { + "id": 102, + "creator": "0x683a4F9915D6216f73d6Df50151725036bD26C02", + "executor": "0xEE56e2B3D491590B5b31738cC34d5232F378a8D5", + "targets": [ + "0x311Bb771e4F8952E6Da169b425E7e92d6Ac45756", + "0x311Bb771e4F8952E6Da169b425E7e92d6Ac45756", + "0x311Bb771e4F8952E6Da169b425E7e92d6Ac45756", + "0x311Bb771e4F8952E6Da169b425E7e92d6Ac45756" + ], + "signatures": [ + "", + "", + "", + "" + ], + "calldatas": [ + "0x7c4e560b000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000021fc00000000000000000000000000000000000000000000000000000000000022c400000000000000000000000000000000000000000000000000000000000028d2", + "0x7c4e560b0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae900000000000000000000000000000000000000000000000000000000000019c80000000000000000000000000000000000000000000000000000000000001c8400000000000000000000000000000000000000000000000000000000000029fe", + "0x7c4e560b0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f98400000000000000000000000000000000000000000000000000000000000019640000000000000000000000000000000000000000000000000000000000001e140000000000000000000000000000000000000000000000000000000000002a94", + "0x7c4e560b000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000203a00000000000000000000000000000000000000000000000000000000000021980000000000000000000000000000000000000000000000000000000000002904" + ], + "withDelegatecalls": [ + false, + false, + false, + false + ], + "startBlock": 15627550, + "endBlock": 15646750, + "executionTime": 1664642195, + "forVotes": "326453010882067153166752", + "againstVotes": "926133910638260020", + "executed": true, + "canceled": false, + "strategy": "0xb7e383ef9B1E9189Fc0F71fb30af8aa14377429e", + "state": "Executed", + "minimumQuorum": "200", + "minimumDiff": "50", + "executionTimeWithGracePeriod": 1665074195, + "proposalCreated": 15627550, + "totalVotingSupply": "16000000000000000000000000", + "ipfsHash": "0x55588a2d7fa9c28509ea269bd1245ce25dbe2dffc45ae905e2d22d5862648ba9", + "startTimestamp": 1664316383, + "creationTimestamp": 1664316383, + "expirationTimestamp": 1664546783 + }, + { + "id": 103, + "creator": "0x55B16934C3661E1990939bC57322554d9B09f262", + "executor": "0xEE56e2B3D491590B5b31738cC34d5232F378a8D5", + "targets": [ + "0x8Dfd2255a9d38c182a14f49afCB8a4a4763C6098" + ], + "signatures": [ + "execute()" + ], + "calldatas": [ + "0x" + ], + "withDelegatecalls": [ + true + ], + "startBlock": 15640489, + "endBlock": 15659689, + "executionTime": 0, + "forVotes": "311965148201048984476963", + "againstVotes": "2629395999515136972", + "executed": false, + "canceled": false, + "strategy": "0xb7e383ef9B1E9189Fc0F71fb30af8aa14377429e", + "state": "Failed", + "minimumQuorum": "200", + "minimumDiff": "50", + "executionTimeWithGracePeriod": 0, + "proposalCreated": 15640489, + "totalVotingSupply": "16000000000000000000000000", + "ipfsHash": "0x267dbabe4b9efc5aaa4b883462eb81b98ad717f7d3db4720420f2a58aa3b2cf9", + "startTimestamp": 1664472527, + "creationTimestamp": 1664472527, + "expirationTimestamp": 1664702927 } ] } \ No newline at end of file diff --git a/src/static-build/vote.ts b/src/static-build/vote.ts index 18efdac03a..e0033e8a68 100644 --- a/src/static-build/vote.ts +++ b/src/static-build/vote.ts @@ -1,9 +1,10 @@ -import { join } from 'path'; -import { LowSync, JSONFileSync } from 'lowdb'; import lodash from 'lodash'; +import { JSONFileSync, LowSync } from 'lowdb'; +import { join } from 'path'; + import { getVotes, VoteType } from '../modules/governance/utils/getVotes'; -import { getProvider } from '../utils/marketsAndNetworksConfig'; import { governanceConfig } from '../ui-config/governanceConfig'; +import { getProvider } from '../utils/marketsAndNetworksConfig'; class LowWithLodash extends LowSync { chain: lodash.ExpChain = lodash.chain(this).get('data'); diff --git a/src/ui-config/governanceConfig.ts b/src/ui-config/governanceConfig.ts index 85130cbf93..28deb75d63 100644 --- a/src/ui-config/governanceConfig.ts +++ b/src/ui-config/governanceConfig.ts @@ -20,6 +20,7 @@ export interface GovernanceConfig { AAVE_GOVERNANCE_V2_HELPER: string; }; ipfsGateway: string; + fallbackIpfsGateway: string; } export const governanceConfig: GovernanceConfig = { @@ -41,5 +42,6 @@ export const governanceConfig: GovernanceConfig = { AAVE_GOVERNANCE_V2_EXECUTOR_LONG: '0xEE56e2B3D491590B5b31738cC34d5232F378a8D5', AAVE_GOVERNANCE_V2_HELPER: '0x16ff7583ea21055bf5f929ec4b896d997ff35847', }, - ipfsGateway: 'https://gateway.pinata.cloud/ipfs', + ipfsGateway: 'https://cloudflare-ipfs.com/ipfs', + fallbackIpfsGateway: 'https://ipfs.io/ipfs', }; diff --git a/src/ui-config/menu-items/index.tsx b/src/ui-config/menu-items/index.tsx index 23c1394092..9de7037824 100644 --- a/src/ui-config/menu-items/index.tsx +++ b/src/ui-config/menu-items/index.tsx @@ -2,12 +2,12 @@ import { BookOpenIcon, QuestionMarkCircleIcon } from '@heroicons/react/outline'; import { t } from '@lingui/macro'; import { ReactNode } from 'react'; import { ROUTES } from 'src/components/primitives/Link'; +import { ENABLE_TESTNET } from 'src/utils/marketsAndNetworksConfig'; import DiscordIcon from '/public/icons/discord.svg'; import GithubIcon from '/public/icons/github.svg'; import { MarketDataType } from '../marketsConfig'; -import { ENABLE_TESTNET } from 'src/utils/marketsAndNetworksConfig'; interface Navigation { link: string; diff --git a/src/ui-config/reservePatches.ts b/src/ui-config/reservePatches.ts index 3ee22aa558..2dff830904 100644 --- a/src/ui-config/reservePatches.ts +++ b/src/ui-config/reservePatches.ts @@ -35,6 +35,7 @@ export const SYMBOL_MAP: { [key: string]: string } = { 'AAVE.e': 'AAVE', 'USDT.e': 'USDT', 'USDC.e': 'USDC', + 'BTC.b': 'BTC', // polygon miMATIC: 'MAI', }; @@ -44,16 +45,7 @@ export const SYMBOL_MAP: { [key: string]: string } = { * With the next version of uipooldataprovider https://github.com/aave/aave-v3-periphery/pull/89 this list can be greatly reduced/removed. */ export const SYMBOL_NAME_MAP: { [key: string]: string } = { - AMPL: 'Ampleforth', AVAX: 'Avalanche', - BAL: 'Balancer', - BAT: 'Basic Attention Token', - BUSD: 'Binance USD', - CRV: 'Curve DAO Token', - CVX: 'Convex Token', - DPI: 'DeFi Pulse Index', - ENJ: 'EnjinCoin', - ENS: 'Ethereum Name Service', ETH: 'Ethereum', EUROS: 'STASIS EURO', FAI: 'Fei USD', @@ -74,8 +66,6 @@ export const SYMBOL_NAME_MAP: { [key: string]: string } = { UNI: 'Uniswap', UNIDAIWETH: 'UNI DAI/WETH', UNIWBTCUSDC: 'UNI WBTC/USDC', - USDC: 'USD Coin', - USDP: 'Pax Dollar', USDT: 'Tether', WAVAX: 'Wrapped Avalanche', WBTC: 'Wrapped BTC', @@ -92,9 +82,11 @@ export const SYMBOL_NAME_MAP: { [key: string]: string } = { export function fetchIconSymbolAndName({ underlyingAsset, symbol, + name, }: { underlyingAsset: string; symbol: string; + name?: string; }) { // guni symbols are just broken (G-UNI for all tokens) if ( @@ -116,7 +108,7 @@ export function fetchIconSymbolAndName({ const unifiedSymbol = SYMBOL_MAP[symbol] || symbol; return { iconSymbol: unifiedSymbol, - name: SYMBOL_NAME_MAP[unifiedSymbol.toUpperCase()] || unifiedSymbol, + name: SYMBOL_NAME_MAP[unifiedSymbol.toUpperCase()] || name || unifiedSymbol, symbol, }; } diff --git a/src/utils/hfUtils.ts b/src/utils/hfUtils.ts index 246ad650ba..0c805f04ac 100644 --- a/src/utils/hfUtils.ts +++ b/src/utils/hfUtils.ts @@ -1,14 +1,14 @@ import { - ComputedUserReserve, + BigNumberValue, calculateHealthFactorFromBalancesBigUnits, + ComputedUserReserve, valueToBigNumber, - BigNumberValue, } from '@aave/math-utils'; +import BigNumber from 'bignumber.js'; import { ComputedReserveData, ExtendedFormattedUser, } from 'src/hooks/app-data-provider/useAppDataProvider'; -import BigNumber from 'bignumber.js'; interface CalculateHFAfterSwapProps { fromAmount: BigNumberValue; diff --git a/src/utils/marketsAndNetworksConfig.ts b/src/utils/marketsAndNetworksConfig.ts index 5ca4b11dd4..eab01466e2 100644 --- a/src/utils/marketsAndNetworksConfig.ts +++ b/src/utils/marketsAndNetworksConfig.ts @@ -28,7 +28,7 @@ const FORK_ENABLED = global?.window?.localStorage.getItem('forkEnabled') === 'tr // specifies which network was forked const FORK_BASE_CHAIN_ID = Number(global?.window?.localStorage.getItem('forkBaseChainId') || 1); // specifies on which chainId the fork is running -const FORK_CHAIN_ID = Number(global?.window?.localStorage.getItem('forkChainId') || 3030); +const FORK_CHAIN_ID = Number(global?.window?.localStorage.getItem('forkNetworkId') || 3030); const FORK_RPC_URL = global?.window?.localStorage.getItem('forkRPCUrl') || 'http://127.0.0.1:8545'; const FORK_WS_RPC_URL = global?.window?.localStorage.getItem('forkWsRPCUrl') || 'ws://127.0.0.1:8545'; @@ -187,8 +187,6 @@ export const getENSProvider = () => { }; const ammDisableProposal = 'https://app.aave.com/governance/proposal/?proposalId=44'; -const harmonyDisableSnapshot = - 'https://snapshot.org/#/aave.eth/proposal/0x81a78109941e5e0ac6cb5ebf82597c839c20ad6821a8c3ff063dba39032533d4'; export const frozenProposalMap: Record = { ['UST']: 'https://app.aave.com/governance/proposal/?proposalId=75', @@ -209,14 +207,6 @@ export const frozenProposalMap: Record = { ['UNIYFIWETH']: ammDisableProposal, ['BPTWBTCWETH']: ammDisableProposal, ['BPTBALWETH']: ammDisableProposal, - ['1DAI']: harmonyDisableSnapshot, - ['1USDC']: harmonyDisableSnapshot, - ['1USDT']: harmonyDisableSnapshot, - ['1AAVE']: harmonyDisableSnapshot, - ['1ETH']: harmonyDisableSnapshot, - ['LINK']: harmonyDisableSnapshot, - ['1WBTC']: harmonyDisableSnapshot, - ['WONE']: harmonyDisableSnapshot, }; // reexport so we can forbit config import diff --git a/src/utils/theme.tsx b/src/utils/theme.tsx index 85d287b934..2d0be530c0 100644 --- a/src/utils/theme.tsx +++ b/src/utils/theme.tsx @@ -1,12 +1,11 @@ import { CheckCircleIcon, + ChevronDownIcon, ExclamationCircleIcon, ExclamationIcon, InformationCircleIcon, - ChevronDownIcon, } from '@heroicons/react/outline'; import { SvgIcon, Theme, ThemeOptions } from '@mui/material'; - import { createTheme } from '@mui/material/styles'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -23,6 +22,10 @@ const FONT = 'Inter, Arial'; declare module '@mui/material/styles/createPalette' { interface PaletteColor extends ColorPartial {} + interface TypeText { + muted: string; + } + interface TypeBackground { default: string; paper: string; @@ -790,48 +793,6 @@ export function getThemedComponents(theme: Theme) { }, }, }, - MuiToggleButtonGroup: { - styleOverrides: { - root: { - backgroundColor: '#383D51', - border: '1px solid rgba(235, 235, 237, 0.12)', - padding: '4px', - }, - }, - }, - MuiToggleButton: { - styleOverrides: { - root: { - border: '0px', - flex: 1, - backgroundColor: '#383D51', - borderRadius: '4px', - - '&.Mui-selected, &.Mui-selected:hover': { - backgroundColor: '#FFFFFF', - borderRadius: '4px !important', - }, - - '&.Mui-selected, &.Mui-disabled': { - zIndex: 100, - height: '100%', - display: 'flex', - justifyContent: 'center', - - '.MuiTypography-subheader1': { - background: theme.palette.gradients.aaveGradient, - backgroundClip: 'text', - textFillColor: 'transparent', - }, - '.MuiTypography-secondary14': { - background: theme.palette.gradients.aaveGradient, - backgroundClip: 'text', - textFillColor: 'transparent', - }, - }, - }, - }, - }, MuiSelect: { defaultProps: { IconComponent: (props) => ( diff --git a/tsconfig.json b/tsconfig.json index 493dbcda8e..b439d669c8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,7 +38,6 @@ "**/*.test.ts", "**/*.test.tsx", "coverage", - "cypress" ], "include": ["custom.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx"] } diff --git a/yarn.lock b/yarn.lock index d456ee06ec..a5e2674ec0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1510,6 +1510,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.18.9": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.9.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" @@ -1868,6 +1875,17 @@ "@emotion/weak-memoize" "^0.2.5" stylis "4.0.13" +"@emotion/cache@^11.10.3": + version "11.10.3" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" + integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== + dependencies: + "@emotion/memoize" "^0.8.0" + "@emotion/sheet" "^1.2.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + stylis "4.0.13" + "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" @@ -1880,11 +1898,23 @@ dependencies: "@emotion/memoize" "^0.7.4" +"@emotion/is-prop-valid@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" + integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== + dependencies: + "@emotion/memoize" "^0.8.0" + "@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5": version "0.7.5" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz" integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== +"@emotion/memoize@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" + integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== + "@emotion/react@11.9.3": version "11.9.3" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.9.3.tgz#f4f4f34444f6654a2e550f5dab4f2d360c101df9" @@ -1935,6 +1965,11 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.1.tgz#015756e2a9a3c7c5f11d8ec22966a8dbfbfac787" integrity sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA== +"@emotion/sheet@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" + integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== + "@emotion/styled@11.9.3": version "11.9.3" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.9.3.tgz#47f0c71137fec7c57035bf3659b52fb536792340" @@ -1956,11 +1991,21 @@ resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz" integrity sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ== +"@emotion/utils@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" + integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== + "@emotion/weak-memoize@^0.2.5": version "0.2.5" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@emotion/weak-memoize@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" + integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== + "@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2", "@endemolshinegroup/cosmiconfig-typescript-loader@^3.0.2": version "3.0.2" resolved "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz" @@ -3063,6 +3108,13 @@ resolved "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz" integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== +"@ledgerhq/iframe-provider@0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/iframe-provider/-/iframe-provider-0.4.2.tgz#2b63892bb3ab9a0719d2b00488be18b176ad6b7e" + integrity sha512-RbdwvQow/ITLk0TLb6c3M7y8IyjopIGXhhuUEMjqTU6PZhAL9Gl7TzH8INit9x9cOeG2WCuV+ZbHQ2oWsLfJ+A== + dependencies: + eventemitter3 "^4.0.0" + "@lingui/babel-plugin-extract-messages@^3.13.2": version "3.13.2" resolved "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.13.2.tgz" @@ -3161,20 +3213,25 @@ resolved "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz" integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== -"@mui/base@5.0.0-alpha.90": - version "5.0.0-alpha.90" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.90.tgz#73700ba74e5c75096ee5d0bfe3ba4a3b3b81beef" - integrity sha512-hNKwzr+RkiuGsGrakz8Q2i5ezr4Dz4b4Qsdipt9SiMrhuFAra/i501VSaEIzwec9LC4G+vtW4fE7yJBB0XaAYw== +"@mui/base@5.0.0-alpha.97": + version "5.0.0-alpha.97" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.97.tgz#bd12db4ed3bb1dbc2879944bbdb68b559641b02a" + integrity sha512-gvo0hOg/tBzfJ3eDQOGAPBJJU+qTWd0e5zBEMFIkT1ekJqXx14JtIHvheOFU17y9iDciYE256Q8g+tj6a1dcBA== dependencies: - "@babel/runtime" "^7.17.2" - "@emotion/is-prop-valid" "^1.1.3" - "@mui/types" "^7.1.4" - "@mui/utils" "^5.9.1" - "@popperjs/core" "^2.11.5" + "@babel/runtime" "^7.18.9" + "@emotion/is-prop-valid" "^1.2.0" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.3" + "@popperjs/core" "^2.11.6" clsx "^1.2.1" prop-types "^15.8.1" react-is "^18.2.0" +"@mui/core-downloads-tracker@^5.10.5": + version "5.10.5" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.5.tgz#be060b633268b124d27474e772e506bb3f39ca51" + integrity sha512-sZYg85rQdlgDYU3V4WcT2Dl+k+y2wYqN04aUvVkFksRR0j81sj6KmfXx4842HJQcq5rjzcTvh4N+yv66XR/9fA== + "@mui/icons-material@^5.8.4": version "5.8.4" resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.8.4.tgz#3f2907c9f8f5ce4d754cb8fb4b68b5a1abf4d095" @@ -3182,67 +3239,68 @@ dependencies: "@babel/runtime" "^7.17.2" -"@mui/material@^5.9.1": - version "5.9.1" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.9.1.tgz#92990e6d4035792430dcf548b91db6f335aebdd3" - integrity sha512-c09SbaMm7Rl7Df9JRkXwPWNbnfrutmHERTJC46OJ9OMAM9+HGQihIbGln1k2Xj65jb3E+G498FZFAoSrrBDvwQ== - dependencies: - "@babel/runtime" "^7.17.2" - "@mui/base" "5.0.0-alpha.90" - "@mui/system" "^5.9.1" - "@mui/types" "^7.1.4" - "@mui/utils" "^5.9.1" +"@mui/material@^5.10.5": + version "5.10.5" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.10.5.tgz#390f95f346e5459d8df43f5e749b2310cefba96d" + integrity sha512-VFMadvfA6jqx5DCk2xoBl4bAGyzgmmubJIuB7fUWUZBwYIYL5Ea9SsoFpt5kawA6O2feuj69alDN2fhxPw1MeQ== + dependencies: + "@babel/runtime" "^7.18.9" + "@mui/base" "5.0.0-alpha.97" + "@mui/core-downloads-tracker" "^5.10.5" + "@mui/system" "^5.10.5" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.3" "@types/react-transition-group" "^4.4.5" clsx "^1.2.1" csstype "^3.1.0" prop-types "^15.8.1" react-is "^18.2.0" - react-transition-group "^4.4.2" + react-transition-group "^4.4.5" -"@mui/private-theming@^5.9.1": - version "5.9.1" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.9.1.tgz#4f714ed9ebd587373dc77b3fc69e9f3e720f0190" - integrity sha512-eIh2IZJInNTdgPLMo9cruzm8UDX5amBBxxsSoNre7lRj3wcsu3TG5OKjIbzkf4VxHHEhdPeNNQyt92k7L78u2A== +"@mui/private-theming@^5.10.3": + version "5.10.3" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.10.3.tgz#7325eef3e480caaaa2d866b9057943ec4fbcb8ce" + integrity sha512-LCYIKlkGz2BTSng2BFzzwSJBRZbChIUri2x2Nh8ryk2B1Ho7zpvE7ex6y39LlStG2Frf92NFC/V4YQbmMAjD5A== dependencies: - "@babel/runtime" "^7.17.2" - "@mui/utils" "^5.9.1" + "@babel/runtime" "^7.18.9" + "@mui/utils" "^5.10.3" prop-types "^15.8.1" -"@mui/styled-engine@^5.8.7": - version "5.8.7" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.8.7.tgz#63d0779c07677fe76d4705a02c7ae99f89b50780" - integrity sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg== +"@mui/styled-engine@^5.10.5": + version "5.10.5" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.10.5.tgz#8ce4197e887a69119aea056320aad5812025ceb4" + integrity sha512-6U6tTdf+H1OsjgcFoY12gYPR+qqZ1WHGGIahK5V7JhMkMUgH7ozyiNi8s1LzmwrUlAz1hAAhuO5nBYXku3wWvw== dependencies: - "@babel/runtime" "^7.17.2" - "@emotion/cache" "^11.9.3" + "@babel/runtime" "^7.18.9" + "@emotion/cache" "^11.10.3" csstype "^3.1.0" prop-types "^15.8.1" -"@mui/system@^5.9.1": - version "5.9.1" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.9.1.tgz#dadd1094b1582781cc524b112a0a126f60b23c25" - integrity sha512-ZixTmc2+sYp++avoYJ38eM70nfwwudN06vYCU4kfwa4nQPiH+bhLYZnfYkcXRKiDR/hfT0dptbOOfQGZqBYczQ== +"@mui/system@^5.10.5": + version "5.10.5" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.10.5.tgz#b0c8494aa9230fb3b8e33c4aee05207925519569" + integrity sha512-GUPiDVZTKp9yH3FVeLSIw3Bqsyl7qLxtAK1ZiZmC8e+zdH7bcnZZXvWK3vPIbx35ZyhQpvAOWQFpiF9TjdA77w== dependencies: - "@babel/runtime" "^7.17.2" - "@mui/private-theming" "^5.9.1" - "@mui/styled-engine" "^5.8.7" - "@mui/types" "^7.1.4" - "@mui/utils" "^5.9.1" + "@babel/runtime" "^7.18.9" + "@mui/private-theming" "^5.10.3" + "@mui/styled-engine" "^5.10.5" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.3" clsx "^1.2.1" csstype "^3.1.0" prop-types "^15.8.1" -"@mui/types@^7.1.4": - version "7.1.4" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.1.4.tgz#4185c05d6df63ec673cda15feab80440abadc764" - integrity sha512-uveM3byMbthO+6tXZ1n2zm0W3uJCQYtwt/v5zV5I77v2v18u0ITkb8xwhsDD2i3V2Kye7SaNR6FFJ6lMuY/WqQ== +"@mui/types@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.0.tgz#91380c2d42420f51f404120f7a9270eadd6f5c23" + integrity sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA== -"@mui/utils@^5.9.1": - version "5.9.1" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.9.1.tgz#2b2c9dadbf8ba6561e145b5688fb7df5ef15a934" - integrity sha512-8+4adOR3xusyJwvbnZxcjqcmbWvl7Og+260ZKIrSvwnFs0aLubL+8MhiceeDDGcmb0bTKxfUgRJ96j32Jb7P+A== +"@mui/utils@^5.10.3": + version "5.10.3" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.10.3.tgz#ce2a96f31de2a5e717f507b5383dbabbddbc4dfc" + integrity sha512-4jXMDPfx6bpMVuheLaOpKTjpzw39ogAZLeaLj5+RJec3E37/hAZMYjURfblLfTWMMoGoqkY03mNsZaEwNobBow== dependencies: - "@babel/runtime" "^7.17.2" + "@babel/runtime" "^7.18.9" "@types/prop-types" "^15.7.5" "@types/react-is" "^16.7.1 || ^17.0.0" prop-types "^15.8.1" @@ -3366,10 +3424,10 @@ resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@popperjs/core@^2.11.5": - version "2.11.5" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" - integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== +"@popperjs/core@^2.11.6": + version "2.11.6" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" + integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== "@rushstack/eslint-patch@^1.0.8": version "1.1.0" @@ -4540,7 +4598,7 @@ dependencies: "@walletconnect/window-getters" "^1.0.0" -"@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": +"@web3-react/abstract-connector@6", "@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/abstract-connector/-/abstract-connector-6.0.7.tgz#401b3c045f1e0fab04256311be49d5144e9badc6" integrity sha512-RhQasA4Ox8CxUC0OENc1AJJm8UTybu/oOCM61Zjg6y0iF7Z0sqv1Ai1VdhC33hrQpA8qSBgoXN9PaP8jKmtdqg== @@ -4586,7 +4644,7 @@ "@web3-react/abstract-connector" "^6.0.7" "@web3-react/types" "^6.0.7" -"@web3-react/types@^6.0.7": +"@web3-react/types@6", "@web3-react/types@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/types/-/types-6.0.7.tgz#34a6204224467eedc6123abaf55fbb6baeb2809f" integrity sha512-ofGmfDhxmNT1/P/MgVa8IKSkCStFiyvXe+U5tyZurKdrtTDFU+wJ/LxClPDtFerWpczNFPUSrKcuhfPX1sI6+A== @@ -5523,9 +5581,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001366: - version "1.0.30001399" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz" - integrity sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA== + version "1.0.30001375" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz" + integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw== capital-case@^1.0.4: version "1.0.4" @@ -6192,10 +6250,10 @@ cypress-wait-until@^1.7.2: resolved "https://registry.yarnpkg.com/cypress-wait-until/-/cypress-wait-until-1.7.2.tgz#7f534dd5a11c89b65359e7a0210f20d3dfc22107" integrity sha512-uZ+M8/MqRcpf+FII/UZrU7g1qYZ4aVlHcgyVopnladyoBrpoaMJ4PKZDrdOJ05H5RHbr7s9Tid635X3E+ZLU/Q== -cypress@10.7.0: - version "10.7.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.7.0.tgz#2d37f8b9751c6de33ee48639cb7e67a2ce593231" - integrity sha512-gTFvjrUoBnqPPOu9Vl5SBHuFlzx/Wxg/ZXIz2H4lzoOLFelKeF7mbwYUOzgzgF0oieU2WhJAestQdkgwJMMTvQ== +cypress@10.8.0: + version "10.8.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.8.0.tgz#12a681f2642b6f13d636bab65d5b71abdb1497a5" + integrity sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -6216,7 +6274,7 @@ cypress@10.7.0: dayjs "^1.10.4" debug "^4.3.2" enquirer "^2.3.6" - eventemitter2 "^6.4.3" + eventemitter2 "6.4.7" execa "4.1.0" executable "^4.1.1" extract-zip "2.0.1" @@ -7390,19 +7448,19 @@ event-target-shim@^5.0.0: resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter2@^6.4.3: - version "6.4.5" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655" - integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw== +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -eventemitter3@4.0.7: +eventemitter3@4.0.7, eventemitter3@^4.0.0: version "4.0.7" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== eventemitter3@^3.1.0: @@ -11397,10 +11455,10 @@ react-number-format@^4.9.1: dependencies: prop-types "^15.7.2" -react-transition-group@^4.4.2: - version "4.4.2" - resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz" - integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== dependencies: "@babel/runtime" "^7.5.5" dom-helpers "^5.0.1" @@ -12629,7 +12687,7 @@ through2@~0.4.1: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -tiny-invariant@^1.0.6: +tiny-invariant@1, tiny-invariant@^1.0.6: version "1.2.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== @@ -13327,6 +13385,16 @@ web3-eth-iban@1.7.0: bn.js "^4.11.9" web3-utils "1.7.0" +web3-ledgerhq-frame-connector@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/web3-ledgerhq-frame-connector/-/web3-ledgerhq-frame-connector-1.0.1.tgz#7554fb5e9d1da19e1ab24e434dbc4d0c012c0527" + integrity sha512-AnSISDK0csoi2V/dMAjcomK8ZbFAYk22KArSoG/chDKlvLgxBgXafWheQPgV7540Efd/wMbtcjo4NotY2M3nDA== + dependencies: + "@ledgerhq/iframe-provider" "0" + "@web3-react/abstract-connector" "6" + "@web3-react/types" "6" + tiny-invariant "1" + web3-providers-http@1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.7.0.tgz"