From b24bbb33762c6a320726212973213b0ca91540e6 Mon Sep 17 00:00:00 2001 From: Yusuf Seyrek Date: Tue, 16 May 2023 13:39:52 +0300 Subject: [PATCH] New architecture (#196) * feat: move app to pages features * feat: route changes * Use React Router, Remove SSR * Fix account menu * Remove app folder * remove old useParams * Moved pages back to pages and refactor names * add layout to route * clean up * create hooks for api fetching * fix refetch of all data on tx complete * formatting * fix: fixed the wallet-connector race condition * remove cosmjs/stargate (#202) * remove cosmjs/stargate * add Yusuf as code-orwner * Singleton client (#203) * remove cosmjs/stargate * add Yusuf as code-orwner * create signleton client and refactor vaults api * update client name, add apollo apr env * Setup validate-env and remove checking from apis * uncomment vaults * Resolve comments * fix: html templating, add checks for hydration&window object, reduce bundle size (#204) * fix: tests * Fix routing and wallet client (#205) * Add header to router (as layout) * Refactor Wallet component * Remove server fallback packages webpack * add missing dependency for useeffect --------- Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> Co-authored-by: Linkie Link --- .env.example | 2 + .github/CODEOWNERS | 2 +- .../Account/AccountDetails.test.tsx | 6 +- next.config.js | 38 +---- package-lock.json | 84 +++++++++++ package.json | 16 +- src/api/accounts/getAccount.ts | 18 +++ src/api/accounts/getAccountDebts.ts | 11 ++ src/api/accounts/getAccountDeposits.ts | 11 ++ src/api/accounts/getAccountVaults.ts | 11 ++ src/api/cosmwasm-client.ts | 19 +++ .../markets/getMarketBalances.ts} | 11 +- src/api/markets/getMarketBorrowings.ts | 29 ++++ .../markets/getMarketDebts.ts} | 16 +- .../markets/getMarketDeposits.ts} | 16 +- src/api/markets/getMarketLiquidity.ts | 30 ++++ .../index.ts => api/markets/getMarkets.ts} | 11 +- .../index.ts => api/prices/getPrices.ts} | 11 +- src/api/vaults/getVaultAprs.ts | 50 +++++++ src/api/vaults/getVaultConfigs.ts | 64 ++++++++ src/api/vaults/getVaults.ts | 29 ++++ src/api/wallets/getAccountIds.ts | 18 +++ src/api/wallets/getAccounts.ts | 26 ++++ src/api/wallets/getWalletBalances.ts | 14 ++ src/app/borrow/page.tsx | 5 - src/app/council/page.tsx | 5 - src/app/earn/farm/page.tsx | 5 - src/app/earn/lend/page.tsx | 5 - src/app/head.tsx | 30 ---- src/app/layout.tsx | 42 ------ src/app/page.tsx | 5 - src/app/portfolio/page.tsx | 5 - src/app/trade/page.tsx | 5 - .../accounts/[accountId]/borrow/page.tsx | 5 - .../accounts/[accountId]/council/page.tsx | 5 - .../accounts/[accountId]/earn/farm/page.tsx | 5 - .../accounts/[accountId]/earn/lend/page.tsx | 5 - .../[address]/accounts/[accountId]/page.tsx | 5 - .../accounts/[accountId]/portfolio/page.tsx | 5 - .../accounts/[accountId]/trade/page.tsx | 5 - src/app/wallets/[address]/borrow/page.tsx | 5 - src/app/wallets/[address]/council/page.tsx | 5 - src/app/wallets/[address]/earn/farm/page.tsx | 5 - src/app/wallets/[address]/earn/lend/page.tsx | 5 - src/app/wallets/[address]/layout.tsx | 3 - src/app/wallets/[address]/portfolio/page.tsx | 5 - src/app/wallets/[address]/trade/page.tsx | 5 - .../Account/AccountBalancesTable.tsx | 3 - src/components/Account/AccountComposition.tsx | 1 - src/components/Account/AccountDetails.tsx | 8 +- src/components/Account/AccountHealth.tsx | 1 - src/components/Account/AccountList.tsx | 32 ++-- src/components/Account/AccountMenu.tsx | 19 +-- src/components/Account/AccountMenuContent.tsx | 22 ++- src/components/Account/AccountStats.tsx | 1 - src/components/Account/AccountSummary.tsx | 2 - src/components/Account/CreateAccount.tsx | 2 - src/components/Account/FundAccount.tsx | 11 +- src/components/AccountDebtTable.tsx | 2 +- src/components/Background.tsx | 2 - src/components/Borrow/AssetExpanded.tsx | 2 - src/components/Borrow/BorrowSuspense.tsx | 0 src/components/Borrow/BorrowTable.tsx | 3 - src/components/Borrow/Borrowings.tsx | 25 ++-- src/components/Council/Overview.tsx | 10 +- src/components/DisplayCurrency.tsx | 2 - src/components/Earn/Tab.tsx | 17 ++- src/components/Earn/vault/AvailableVaults.tsx | 7 +- src/components/Earn/vault/FeaturedVaults.tsx | 7 +- src/components/Earn/vault/VaultCard.tsx | 2 - src/components/Earn/vault/VaultTable.tsx | 3 - src/components/FormattedNumber.tsx | 2 - src/components/Header/DesktopHeader.tsx | 27 ++-- src/components/Modal.tsx | 2 - src/components/Modals/BorrowModal.tsx | 2 - .../Modals/FundAndWithdrawModal.tsx | 2 - src/components/Modals/ModalsContainer.tsx | 2 - .../Navigation/DesktopNavigation.tsx | 19 +-- src/components/Navigation/NavLink.tsx | 14 +- src/components/NumberInput.tsx | 2 - src/components/Portfolio/AccountOverview.tsx | 14 +- src/components/Routes.tsx | 49 ++++++ src/components/Select/Select.tsx | 2 - src/components/Settings.tsx | 2 - src/components/Slider.tsx | 2 - src/components/Toaster.tsx | 7 +- src/components/TokenInput.tsx | 2 - src/components/TokenInputWithSlider.tsx | 2 - src/components/Trade/OrderBook.tsx | 11 +- src/components/Trade/Trade.tsx | 13 +- src/components/Trade/TradingView.tsx | 7 +- src/components/Wallet/ConnectButton.tsx | 2 - src/components/Wallet/ConnectedButton.tsx | 18 +-- src/components/Wallet/Wallet.tsx | 52 ++++--- .../Wallet/WalletConnectProvider.tsx | 9 +- src/components/pages/council.tsx | 9 -- src/components/pages/portfolio.tsx | 9 -- src/constants/env.ts | 69 ++++----- src/hooks/useAccountDebts.tsx | 10 ++ src/hooks/useAccounts.tsx | 10 ++ src/hooks/useCurrentAccount.tsx | 7 +- src/hooks/useMarketBorrowings.tsx | 9 ++ .../FetchPrices.tsx => hooks/usePrices.tsx} | 11 +- src/hooks/useVaults.tsx | 9 ++ src/hooks/useWalletBalances.tsx | 9 ++ src/middleware.ts | 12 -- .../pages/borrow.tsx => pages/BorrowPage.tsx} | 10 +- src/pages/CouncilPage.tsx | 5 + .../pages/farm.tsx => pages/FarmPage.tsx} | 6 +- .../pages/lend.tsx => pages/LendPage.tsx} | 4 +- src/pages/PortfolioPage.tsx | 5 + .../pages/trade.tsx => pages/TradePage.tsx} | 12 +- src/pages/_app.tsx | 49 ++++++ src/pages/_document.tsx | 13 ++ src/pages/_layout.tsx | 31 ++++ src/pages/api/accounts/[id]/debts.ts | 21 --- src/pages/api/accounts/[id]/deposits.ts | 21 --- src/pages/api/accounts/[id]/index.ts | 27 ---- src/pages/api/accounts/[id]/vaults.ts | 21 --- src/pages/api/markets/borrow.ts | 42 ------ src/pages/api/markets/liquidity.ts | 42 ------ src/pages/api/vaults/index.ts | 141 ------------------ .../api/wallets/[address]/accounts/ids.ts | 26 ---- .../api/wallets/[address]/accounts/index.ts | 35 ----- src/pages/api/wallets/[address]/balances.ts | 21 --- src/pages/index.tsx | 15 ++ src/store/slices/broadcast.ts | 4 +- src/types/interfaces/account.d.ts | 2 +- src/types/interfaces/pageParams.d.ts | 9 -- src/types/interfaces/route.d.ts | 2 +- src/types/interfaces/vaults.d.ts | 7 +- src/types/stargate.d.ts | 14 +- src/utils/api.ts | 73 --------- src/utils/route.ts | 81 ++-------- tailwind.config.js | 2 +- validate-env.js | 40 +++++ yarn.lock | 139 ++++++++++------- 137 files changed, 1039 insertions(+), 1211 deletions(-) create mode 100644 src/api/accounts/getAccount.ts create mode 100644 src/api/accounts/getAccountDebts.ts create mode 100644 src/api/accounts/getAccountDeposits.ts create mode 100644 src/api/accounts/getAccountVaults.ts create mode 100644 src/api/cosmwasm-client.ts rename src/{pages/api/markets/balances.ts => api/markets/getMarketBalances.ts} (54%) create mode 100644 src/api/markets/getMarketBorrowings.ts rename src/{pages/api/markets/debts.ts => api/markets/getMarketDebts.ts} (63%) rename src/{pages/api/markets/deposits.ts => api/markets/getMarketDeposits.ts} (64%) create mode 100644 src/api/markets/getMarketLiquidity.ts rename src/{pages/api/markets/index.ts => api/markets/getMarkets.ts} (69%) rename src/{pages/api/prices/index.ts => api/prices/getPrices.ts} (79%) create mode 100644 src/api/vaults/getVaultAprs.ts create mode 100644 src/api/vaults/getVaultConfigs.ts create mode 100644 src/api/vaults/getVaults.ts create mode 100644 src/api/wallets/getAccountIds.ts create mode 100644 src/api/wallets/getAccounts.ts create mode 100644 src/api/wallets/getWalletBalances.ts delete mode 100644 src/app/borrow/page.tsx delete mode 100644 src/app/council/page.tsx delete mode 100644 src/app/earn/farm/page.tsx delete mode 100644 src/app/earn/lend/page.tsx delete mode 100644 src/app/head.tsx delete mode 100644 src/app/layout.tsx delete mode 100644 src/app/page.tsx delete mode 100644 src/app/portfolio/page.tsx delete mode 100644 src/app/trade/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/borrow/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/council/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/earn/farm/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/earn/lend/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/portfolio/page.tsx delete mode 100644 src/app/wallets/[address]/accounts/[accountId]/trade/page.tsx delete mode 100644 src/app/wallets/[address]/borrow/page.tsx delete mode 100644 src/app/wallets/[address]/council/page.tsx delete mode 100644 src/app/wallets/[address]/earn/farm/page.tsx delete mode 100644 src/app/wallets/[address]/earn/lend/page.tsx delete mode 100644 src/app/wallets/[address]/layout.tsx delete mode 100644 src/app/wallets/[address]/portfolio/page.tsx delete mode 100644 src/app/wallets/[address]/trade/page.tsx create mode 100644 src/components/Borrow/BorrowSuspense.tsx create mode 100644 src/components/Routes.tsx delete mode 100644 src/components/pages/council.tsx delete mode 100644 src/components/pages/portfolio.tsx create mode 100644 src/hooks/useAccountDebts.tsx create mode 100644 src/hooks/useAccounts.tsx create mode 100644 src/hooks/useMarketBorrowings.tsx rename src/{components/FetchPrices.tsx => hooks/usePrices.tsx} (50%) create mode 100644 src/hooks/useVaults.tsx create mode 100644 src/hooks/useWalletBalances.tsx delete mode 100644 src/middleware.ts rename src/{components/pages/borrow.tsx => pages/BorrowPage.tsx} (51%) create mode 100644 src/pages/CouncilPage.tsx rename src/{components/pages/farm.tsx => pages/FarmPage.tsx} (70%) rename src/{components/pages/lend.tsx => pages/LendPage.tsx} (61%) create mode 100644 src/pages/PortfolioPage.tsx rename src/{components/pages/trade.tsx => pages/TradePage.tsx} (54%) create mode 100644 src/pages/_app.tsx create mode 100644 src/pages/_document.tsx create mode 100644 src/pages/_layout.tsx delete mode 100644 src/pages/api/accounts/[id]/debts.ts delete mode 100644 src/pages/api/accounts/[id]/deposits.ts delete mode 100644 src/pages/api/accounts/[id]/index.ts delete mode 100644 src/pages/api/accounts/[id]/vaults.ts delete mode 100644 src/pages/api/markets/borrow.ts delete mode 100644 src/pages/api/markets/liquidity.ts delete mode 100644 src/pages/api/vaults/index.ts delete mode 100644 src/pages/api/wallets/[address]/accounts/ids.ts delete mode 100644 src/pages/api/wallets/[address]/accounts/index.ts delete mode 100644 src/pages/api/wallets/[address]/balances.ts create mode 100644 src/pages/index.tsx delete mode 100644 src/types/interfaces/pageParams.d.ts delete mode 100644 src/utils/api.ts create mode 100644 validate-env.js diff --git a/.env.example b/.env.example index a7cc8667..a15282e4 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,7 @@ NEXT_PUBLIC_RPC=https://testnet-osmosis-node.marsprotocol.io/XF32UOOU55CX/osmosi NEXT_PUBLIC_GQL=https://testnet-osmosis-node.marsprotocol.io/XF32UOOU55CX/osmosis-hive-front/graphql NEXT_PUBLIC_REST=https://testnet-osmosis-node.marsprotocol.io/XF32UOOU55CX/osmosis-lcd-front/ NEXT_PUBLIC_SWAP=https://testnet.osmosis.zone +NEXT_PUBLIC_APOLLO_APR=https://api.apollo.farm/api/vault_infos/v2/osmo-test-5 NEXT_PUBLIC_WALLETS=keplr,cosmostation NEXT_PUBLIC_ACCOUNT_NFT=osmo1ye2rntzz9qmxgv7eg09supww6k6xs0y0sekcr3x5clp087fymn4q3y33s4 NEXT_PUBLIC_ORACLE=osmo1khe29uw3t85nmmp3mtr8dls7v2qwsfk3tndu5h4w5g2r5tzlz5qqarq2e2 @@ -21,6 +22,7 @@ NEXT_PUBLIC_API=http://localhost:3000/api # NEXT_PUBLIC_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql # NEXT_PUBLIC_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ # NEXT_PUBLIC_SWAP=https://app.osmosis.zone +# NEXT_PUBLIC_APOLLO_APR=https://api.apollo.farm/api/vault_infos/v2/osmosis-1 # NEXT_PUBLIC_WALLETS=keplr,xfi-cosmos,leap-cosmos,cosmostation,mobile-keplr,mobile-cosmostation # NEXT_PUBLIC_ACCOUNT_NFT=osmo1450hrg6dv2l58c0rvdwx8ec2a0r6dd50hn4frk370tpvqjhy8khqw7sw09 # NEXT_PUBLIC_ORACLE=osmo1mhznfr60vjdp2gejhyv2gax9nvyyzhd3z0qcwseyetkfustjauzqycsy2g diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 254e694c..51451618 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @bobthebuidlr @linkielink +* @bobthebuidlr @linkielink @yusufseyrek diff --git a/__tests__/components/Account/AccountDetails.test.tsx b/__tests__/components/Account/AccountDetails.test.tsx index 52b18249..4fcbaf83 100644 --- a/__tests__/components/Account/AccountDetails.test.tsx +++ b/__tests__/components/Account/AccountDetails.test.tsx @@ -1,10 +1,10 @@ import { render, screen } from '@testing-library/react' +import * as rrd from 'react-router-dom' -import * as useParams from 'utils/route' import AccountDetails from 'components/Account/AccountDetails' -jest.mock('utils/route') -const mockedUseParams = useParams.default as jest.Mock +jest.mock('react-router-dom') +const mockedUseParams = rrd.useParams as jest.Mock describe('', () => { afterAll(() => { diff --git a/next.config.js b/next.config.js index 5c19e1a5..a6bc0b47 100644 --- a/next.config.js +++ b/next.config.js @@ -1,46 +1,16 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - output: 'standalone', - experimental: { - appDir: true, - }, reactStrictMode: true, - async redirects() { + async rewrites() { return [ { - source: '/', - destination: '/trade', - permanent: true, - }, - { - source: '/wallets', - destination: '/trade', - permanent: true, - }, - { - source: '/wallets/:wallet', - destination: '/wallets/:wallet/trade', - permanent: true, - }, - { - source: '/wallets/:wallet/accounts', - destination: '/wallets/:wallet/accounts/trade', - permanent: true, + source: '/:any*', + destination: '/', }, ] }, - webpack(config, { isServer }) { - if (isServer) { - config.resolve.fallback = { - ...config.resolve.fallback, - 'utf-8-validate': false, - bufferutil: false, - './build/Release/ecdh': false, - eccrypto: false, - } - } - + webpack(config) { config.module.rules.push({ test: /\.svg$/i, issuer: /\.[jt]sx?$/, diff --git a/package-lock.json b/package-lock.json index 679e2491..9f1a2fb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19152,6 +19152,66 @@ "optional": true } } + }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz", + "integrity": "sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz", + "integrity": "sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-freebsd-x64": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz", + "integrity": "sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz", + "integrity": "sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } } }, "dependencies": { @@ -33215,6 +33275,30 @@ "requires": { "use-sync-external-store": "1.2.0" } + }, + "@next/swc-android-arm-eabi": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz", + "integrity": "sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz", + "integrity": "sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==", + "optional": true + }, + "@next/swc-freebsd-x64": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz", + "integrity": "sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz", + "integrity": "sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==", + "optional": true } } } diff --git a/package.json b/package.json index f22f8ef9..5cb8c7f2 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,18 @@ "version": "2.0.0", "private": true, "scripts": { - "build": "next build", + "build": "yarn validate-env && next build", "dev": "next dev", "test": "jest", "lint": "eslint ./src/ && yarn prettier-check", "format": "eslint ./src/ --fix && prettier --write ./src/", "prettier-check": "prettier --ignore-path .gitignore --check ./src/", - "start": "next start" + "start": "next start", + "validate-env": "node ./validate-env" }, "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.30.1", - "@cosmjs/stargate": "^0.30.1", - "@marsprotocol/wallet-connector": "^1.5.8", + "@marsprotocol/wallet-connector": "^1.5.9", "@sentry/nextjs": "^7.51.2", "@tanstack/react-table": "^8.9.1", "@tippyjs/react": "^4.2.6", @@ -27,6 +27,7 @@ "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", "react-draggable": "^4.4.5", + "react-router-dom": "^6.11.1", "react-spring": "^9.7.1", "react-toastify": "^9.1.2", "react-use-clipboard": "^1.0.9", @@ -37,15 +38,16 @@ }, "devDependencies": { "@svgr/webpack": "^8.0.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^14.0.0", "@types/node": "^20.1.1", "@types/react": "18.2.6", "@types/react-dom": "18.2.4", "autoprefixer": "^10.4.14", + "babel-jest": "^29.5.0", + "dotenv": "^16.0.3", "eslint": "8.40.0", "eslint-config-next": "^13.4.1", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^14.0.0", - "babel-jest": "^29.5.0", "eslint-plugin-import": "^2.27.5", "identity-obj-proxy": "^3.0.0", "jest": "^29.5.0", diff --git a/src/api/accounts/getAccount.ts b/src/api/accounts/getAccount.ts new file mode 100644 index 00000000..9adbba3f --- /dev/null +++ b/src/api/accounts/getAccount.ts @@ -0,0 +1,18 @@ +import { getClient } from 'api/cosmwasm-client' +import { ENV } from 'constants/env' + +export default async function getAccount(accountId: string): Promise { + const client = await getClient() + + const account: AccountResponse = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, { + positions: { + account_id: accountId, + }, + }) + + if (account) { + return account + } + + return new Promise((_, reject) => reject('No account found')) +} diff --git a/src/api/accounts/getAccountDebts.ts b/src/api/accounts/getAccountDebts.ts new file mode 100644 index 00000000..9e10a94e --- /dev/null +++ b/src/api/accounts/getAccountDebts.ts @@ -0,0 +1,11 @@ +import getAccount from 'api/accounts/getAccount' + +export default async function getAccountDebts(accountId: string): Promise { + const account = await getAccount(accountId) + + if (account) { + return account.debts + } + + return new Promise((_, reject) => reject('Account not found')) +} diff --git a/src/api/accounts/getAccountDeposits.ts b/src/api/accounts/getAccountDeposits.ts new file mode 100644 index 00000000..c15ba5ee --- /dev/null +++ b/src/api/accounts/getAccountDeposits.ts @@ -0,0 +1,11 @@ +import getAccount from 'api/accounts/getAccount' + +export default async function getAccountDeposits(accountId: string) { + const account = await getAccount(accountId) + + if (account) { + return account.deposits + } + + return new Promise((_, reject) => reject('Account not found')) +} diff --git a/src/api/accounts/getAccountVaults.ts b/src/api/accounts/getAccountVaults.ts new file mode 100644 index 00000000..da24f005 --- /dev/null +++ b/src/api/accounts/getAccountVaults.ts @@ -0,0 +1,11 @@ +import getAccount from 'api/accounts/getAccount' + +export default async function getAccountDeposits(accountId: string) { + const account = await getAccount(accountId) + + if (account) { + return account.vaults + } + + return new Promise((_, reject) => reject('Account not found')) +} diff --git a/src/api/cosmwasm-client.ts b/src/api/cosmwasm-client.ts new file mode 100644 index 00000000..40d0aa78 --- /dev/null +++ b/src/api/cosmwasm-client.ts @@ -0,0 +1,19 @@ +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' + +import { ENV } from 'constants/env' + +let _cosmWasmClient: CosmWasmClient + +const getClient = async () => { + try { + if (!_cosmWasmClient) { + _cosmWasmClient = await CosmWasmClient.connect(ENV.URL_RPC || '') + } + + return _cosmWasmClient + } catch (error) { + throw error + } +} + +export { getClient } diff --git a/src/pages/api/markets/balances.ts b/src/api/markets/getMarketBalances.ts similarity index 54% rename from src/pages/api/markets/balances.ts rename to src/api/markets/getMarketBalances.ts index fc0f90cb..78920b80 100644 --- a/src/pages/api/markets/balances.ts +++ b/src/api/markets/getMarketBalances.ts @@ -1,13 +1,8 @@ import { gql, request as gqlRequest } from 'graphql-request' -import { NextApiRequest, NextApiResponse } from 'next' -import { ENV, ENV_MISSING_MESSAGE } from 'constants/env' - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK) { - return res.status(404).json(ENV_MISSING_MESSAGE) - } +import { ENV } from 'constants/env' +export default async function getBalances() { const result = await gqlRequest( ENV.URL_GQL, gql` @@ -24,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) `, ) - return res.status(200).json(result.bank.balance) + return result.bank.balance } interface Result { diff --git a/src/api/markets/getMarketBorrowings.ts b/src/api/markets/getMarketBorrowings.ts new file mode 100644 index 00000000..88bfb279 --- /dev/null +++ b/src/api/markets/getMarketBorrowings.ts @@ -0,0 +1,29 @@ +import { BN } from 'utils/helpers' +import getPrices from 'api/prices/getPrices' +import getMarkets from 'api/markets/getMarkets' +import getMarketLiquidity from 'api/markets/getMarketLiquidity' + +export default async function getMarketBorrowings(): Promise { + const liquidity = await getMarketLiquidity() + const borrowEnabledMarkets = (await getMarkets()).filter((market: Market) => market.borrowEnabled) + const prices = await getPrices() + + const borrow: BorrowAsset[] = borrowEnabledMarkets.map((market) => { + const price = prices.find((coin) => coin.denom === market.denom)?.amount ?? '1' + const amount = liquidity.find((coin) => coin.denom === market.denom)?.amount ?? '0' + return { + denom: market.denom, + borrowRate: market.borrowRate ?? 0, + liquidity: { + amount: amount, + value: BN(amount).times(price).toString(), + }, + } + }) + + if (borrow) { + return borrow + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/pages/api/markets/debts.ts b/src/api/markets/getMarketDebts.ts similarity index 63% rename from src/pages/api/markets/debts.ts rename to src/api/markets/getMarketDebts.ts index 4d71e556..0bbaa250 100644 --- a/src/pages/api/markets/debts.ts +++ b/src/api/markets/getMarketDebts.ts @@ -1,15 +1,11 @@ import { gql, request as gqlRequest } from 'graphql-request' -import { NextApiRequest, NextApiResponse } from 'next' -import { ENV, ENV_MISSING_MESSAGE, VERCEL_BYPASS } from 'constants/env' +import { ENV } from 'constants/env' import { denomToKey, getContractQuery, keyToDenom } from 'utils/query' +import getMarkets from 'api/markets/getMarkets' -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!ENV.URL_API || !ENV.ADDRESS_RED_BANK || !ENV.URL_GQL) { - return res.status(404).json(ENV_MISSING_MESSAGE) - } - - const markets: Market[] = await (await fetch(`${ENV.URL_API}/markets${VERCEL_BYPASS}`)).json() +export default async function getMarketDebts(): Promise { + const markets: Market[] = await getMarkets() let query = '' @@ -45,10 +41,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) amount: result.debts[key], } }) - return res.status(200).json(debts) + return debts } - return res.status(404) + return new Promise((_, reject) => reject('No data')) } interface DebtsQuery { diff --git a/src/pages/api/markets/deposits.ts b/src/api/markets/getMarketDeposits.ts similarity index 64% rename from src/pages/api/markets/deposits.ts rename to src/api/markets/getMarketDeposits.ts index e8fd34e9..f4b4e337 100644 --- a/src/pages/api/markets/deposits.ts +++ b/src/api/markets/getMarketDeposits.ts @@ -1,15 +1,11 @@ import { gql, request as gqlRequest } from 'graphql-request' -import { NextApiRequest, NextApiResponse } from 'next' -import { ENV, ENV_MISSING_MESSAGE, VERCEL_BYPASS } from 'constants/env' +import { ENV } from 'constants/env' import { denomToKey, getContractQuery, keyToDenom } from 'utils/query' +import getMarkets from 'api/markets/getMarkets' -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!ENV.URL_RPC || !ENV.ADDRESS_RED_BANK || !ENV.URL_GQL || !ENV.URL_API) { - return res.status(404).json(ENV_MISSING_MESSAGE) - } - - const markets = await (await fetch(`${ENV.URL_API}/markets${VERCEL_BYPASS}`)).json() +export default async function getMarketDeposits(): Promise { + const markets = await getMarkets() let query = '' @@ -45,10 +41,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) amount: result.deposits[key], } }) - return res.status(200).json(deposits) + return deposits } - return res.status(404) + return new Promise((_, reject) => reject('No data')) } interface DepositsQuery { diff --git a/src/api/markets/getMarketLiquidity.ts b/src/api/markets/getMarketLiquidity.ts new file mode 100644 index 00000000..b2ed1fcd --- /dev/null +++ b/src/api/markets/getMarketLiquidity.ts @@ -0,0 +1,30 @@ +import { BN } from 'utils/helpers' +import getMarketDeposits from 'api/markets/getMarketDeposits' +import getMarketDebts from 'api/markets/getMarketDebts' + +export default async function getMarketLiquidity(): Promise { + const deposits = await getMarketDeposits() + const debts = await getMarketDebts() + + const liquidity: Coin[] = deposits.map((deposit) => { + const debt = debts.find((debt) => debt.denom === deposit.denom) + + if (debt) { + return { + denom: deposit.denom, + amount: BN(deposit.amount).minus(debt.amount).toString(), + } + } + + return { + denom: deposit.denom, + amount: '0', + } + }) + + if (liquidity) { + return liquidity + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/pages/api/markets/index.ts b/src/api/markets/getMarkets.ts similarity index 69% rename from src/pages/api/markets/index.ts rename to src/api/markets/getMarkets.ts index d7638c8d..e7bccb2c 100644 --- a/src/pages/api/markets/index.ts +++ b/src/api/markets/getMarkets.ts @@ -1,16 +1,11 @@ import { gql, request as gqlRequest } from 'graphql-request' -import { NextApiRequest, NextApiResponse } from 'next' -import { ENV, ENV_MISSING_MESSAGE } from 'constants/env' +import { ENV } from 'constants/env' import { getMarketAssets } from 'utils/assets' import { denomToKey } from 'utils/query' import { resolveMarketResponses } from 'utils/resolvers' -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK || !ENV.ADDRESS_INCENTIVES) { - return res.status(404).json(ENV_MISSING_MESSAGE) - } - +export default async function getMarkets(): Promise { const marketAssets = getMarketAssets() const marketQueries = marketAssets.map( @@ -36,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const market = result.rbwasmkey[`${denomToKey(asset.denom)}`] return market }) - return res.status(200).json(resolveMarketResponses(markets)) + return resolveMarketResponses(markets) } interface RedBankData { diff --git a/src/pages/api/prices/index.ts b/src/api/prices/getPrices.ts similarity index 79% rename from src/pages/api/prices/index.ts rename to src/api/prices/getPrices.ts index 6ce5ab23..f188e72f 100644 --- a/src/pages/api/prices/index.ts +++ b/src/api/prices/getPrices.ts @@ -1,16 +1,11 @@ import { gql, request as gqlRequest } from 'graphql-request' -import { NextApiRequest, NextApiResponse } from 'next' import { ASSETS } from 'constants/assets' -import { ENV, ENV_MISSING_MESSAGE } from 'constants/env' +import { ENV } from 'constants/env' import { getMarketAssets } from 'utils/assets' import { BN } from 'utils/helpers' -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!ENV.URL_GQL || !ENV.ADDRESS_ORACLE) { - return res.status(404).json(ENV_MISSING_MESSAGE) - } - +export default async function getPrices(): Promise { const marketAssets = getMarketAssets() const baseCurrency = ASSETS[0] @@ -51,7 +46,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ] as Coin[] }, []) - return res.status(200).json(data) + return data } interface TokenPricesResult { diff --git a/src/api/vaults/getVaultAprs.ts b/src/api/vaults/getVaultAprs.ts new file mode 100644 index 00000000..8879876d --- /dev/null +++ b/src/api/vaults/getVaultAprs.ts @@ -0,0 +1,50 @@ +import { ENV } from 'constants/env' + +export default async function getAprs() { + try { + const response = await fetch(ENV.URL_APOLLO_APR) + + if (response.ok) { + const data: FlatApr[] | NestedApr[] = await response.json() + + const newAprs = data.map((aprData) => { + try { + const apr = aprData as FlatApr + const aprTotal = apr.apr.reduce((prev, curr) => Number(curr.value) + prev, 0) + const feeTotal = apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0) + + const finalApr = aprTotal + feeTotal + + return { address: aprData.contract_address, apr: finalApr } + } catch { + const apr = aprData as NestedApr + const aprTotal = apr.apr.aprs.reduce((prev, curr) => Number(curr.value) + prev, 0) + const feeTotal = apr.apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0) + + const finalApr = aprTotal + feeTotal + return { address: aprData.contract_address, apr: finalApr } + } + }) + + return newAprs + } + + return [] + } catch { + return [] + } +} + +interface FlatApr { + contract_address: string + apr: { type: string; value: number | string }[] + fees: { type: string; value: number | string }[] +} + +interface NestedApr { + contract_address: string + apr: { + aprs: { type: string; value: number | string }[] + fees: { type: string; value: number | string }[] + } +} diff --git a/src/api/vaults/getVaultConfigs.ts b/src/api/vaults/getVaultConfigs.ts new file mode 100644 index 00000000..d3e9d384 --- /dev/null +++ b/src/api/vaults/getVaultConfigs.ts @@ -0,0 +1,64 @@ +import { getClient } from 'api/cosmwasm-client' +import { ENV, IS_TESTNET } from 'constants/env' +import { TESTNET_VAULTS, VAULTS } from 'constants/vaults' +import { + ArrayOfVaultInfoResponse, + VaultBaseForString, +} from 'types/generated/mars-credit-manager/MarsCreditManager.types' + +export default async function getVaultConfigs(): Promise { + const vaultInfos: VaultInfo[] = await getVaultInfos([]) + const vaults = IS_TESTNET ? TESTNET_VAULTS : VAULTS + + return vaults.map((vaultMetaData) => { + const vaultConfig = vaultInfos.find((vaultInfo) => vaultInfo.address === vaultMetaData.address) + + return { + ...vaultMetaData, + ...vaultConfig, + } as VaultConfig + }) +} + +const getVaultInfos = async ( + vaultInfos: VaultInfo[], + startAfter?: VaultBaseForString, +): Promise => { + if (!ENV.ADDRESS_CREDIT_MANAGER) return [] + const client = await getClient() + try { + const batch: ArrayOfVaultInfoResponse = await client.queryContractSmart( + ENV.ADDRESS_CREDIT_MANAGER, + { + vaults_info: { limit: 4, start_after: startAfter }, + }, + ) + + const batchProcessed = batch?.map((vaultInfo) => { + return { + address: vaultInfo.vault.address, + cap: { + denom: vaultInfo.config.deposit_cap.denom, + used: Number(vaultInfo.utilization.amount), + max: Number(vaultInfo.config.deposit_cap.amount), + }, + ltv: { + max: Number(vaultInfo.config.max_ltv), + liq: Number(vaultInfo.config.liquidation_threshold), + }, + } as VaultConfig + }) + + vaultInfos.push(...batchProcessed) + + if (batch.length === 4) { + return await getVaultInfos(vaultInfos, { + address: batchProcessed[batchProcessed.length - 1].address, + } as VaultBaseForString) + } + + return vaultInfos + } catch { + return vaultInfos + } +} diff --git a/src/api/vaults/getVaults.ts b/src/api/vaults/getVaults.ts new file mode 100644 index 00000000..60805b16 --- /dev/null +++ b/src/api/vaults/getVaults.ts @@ -0,0 +1,29 @@ +import { convertAprToApy } from 'utils/parsers' +import getVaultConfigs from 'api/vaults/getVaultConfigs' +import getAprs from 'api/vaults/getVaultAprs' + +export default async function getVaults(): Promise { + const $vaultConfigs = getVaultConfigs() + const $aprs = getAprs() + const vaults: Vault[] = await Promise.all([$vaultConfigs, $aprs]).then(([vaultConfigs, aprs]) => { + return vaultConfigs.map((vaultConfig) => { + const apr = aprs.find((apr) => apr.address === vaultConfig.address) + if (apr) { + return { + ...vaultConfig, + apy: convertAprToApy(apr.apr, 365), + } + } + return { + ...vaultConfig, + apy: null, + } + }) + }) + + if (vaults) { + return vaults + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/api/wallets/getAccountIds.ts b/src/api/wallets/getAccountIds.ts new file mode 100644 index 00000000..8b4e6be9 --- /dev/null +++ b/src/api/wallets/getAccountIds.ts @@ -0,0 +1,18 @@ +import { getClient } from 'api/cosmwasm-client' +import { ENV } from 'constants/env' + +export default async function getAccountIds(address: string) { + const client = await getClient() + + const data = await client.queryContractSmart(ENV.ADDRESS_ACCOUNT_NFT, { + tokens: { + owner: address, + }, + }) + + if (data.tokens) { + return data.tokens + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/api/wallets/getAccounts.ts b/src/api/wallets/getAccounts.ts new file mode 100644 index 00000000..fb1a21de --- /dev/null +++ b/src/api/wallets/getAccounts.ts @@ -0,0 +1,26 @@ +import { ENV } from 'constants/env' +import { resolvePositionResponses } from 'utils/resolvers' +import getWalletAccountIds from 'api/wallets/getAccountIds' +import { getClient } from 'api/cosmwasm-client' + +export default async function getAccounts(address: string): Promise { + const accountIds: string[] = await getWalletAccountIds(address) + + const client = await getClient() + + const $accounts: Promise[] = accountIds.map((accountId) => + client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER!, { + positions: { + account_id: `${accountId}`, + }, + }), + ) + + const accounts = await Promise.all($accounts).then((accounts) => accounts) + + if (accounts) { + return resolvePositionResponses(accounts) + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/api/wallets/getWalletBalances.ts b/src/api/wallets/getWalletBalances.ts new file mode 100644 index 00000000..798409a9 --- /dev/null +++ b/src/api/wallets/getWalletBalances.ts @@ -0,0 +1,14 @@ +import { ENV } from 'constants/env' + +export default async function getWalletBalances(address: string): Promise { + const uri = '/cosmos/bank/v1beta1/balances/' + + const response = await fetch(`${ENV.URL_REST}${uri}${address}`) + + if (response.ok) { + const data = await response.json() + return data.balances + } + + return new Promise((_, reject) => reject('No data')) +} diff --git a/src/app/borrow/page.tsx b/src/app/borrow/page.tsx deleted file mode 100644 index fe8775e7..00000000 --- a/src/app/borrow/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import BorrowPage from 'components/pages/borrow' - -export default async function page({ params }: PageProps) { - return -} diff --git a/src/app/council/page.tsx b/src/app/council/page.tsx deleted file mode 100644 index a24798ba..00000000 --- a/src/app/council/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import CouncilPage from 'components/pages/council' - -export default async function page({ params }: PageProps) { - return -} diff --git a/src/app/earn/farm/page.tsx b/src/app/earn/farm/page.tsx deleted file mode 100644 index 2928f19a..00000000 --- a/src/app/earn/farm/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import FarmPage from 'components/pages/farm' - -export default async function page({ params }: { params: PageParams }) { - return -} diff --git a/src/app/earn/lend/page.tsx b/src/app/earn/lend/page.tsx deleted file mode 100644 index 602bd297..00000000 --- a/src/app/earn/lend/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import LendPage from 'components/pages/lend' - -export default function page({ params }: { params: PageParams }) { - return -} diff --git a/src/app/head.tsx b/src/app/head.tsx deleted file mode 100644 index b632a7f4..00000000 --- a/src/app/head.tsx +++ /dev/null @@ -1,30 +0,0 @@ -export default function Head() { - return ( - <> - Mars Protocol V2 - - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx deleted file mode 100644 index b20592e2..00000000 --- a/src/app/layout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import classNames from 'classnames' -import { headers } from 'next/headers' - -import AccountDetails from 'components/Account/AccountDetails' -import Background from 'components/Background' -import FetchPrices from 'components/FetchPrices' -import Footer from 'components/Footer' -import DesktopHeader from 'components/Header/DesktopHeader' -import ModalsContainer from 'components/Modals/ModalsContainer' -import Toaster from 'components/Toaster' -import 'react-toastify/dist/ReactToastify.min.css' -import 'styles/globals.css' -import { getRouteParams } from 'utils/route' - -export default function RootLayout(props: { children: React.ReactNode }) { - const href = headers().get('x-url') || '' - const params = getRouteParams(href) - return ( - - - - - - -
-
- {props.children} -
- -
-