diff --git a/README.md b/README.md index ad563fae7..ec890b143 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ This repository is organized as follows: - `/apps/lend`: Lend [React](https://react.dev/) application. - `/tests`: DApp tests - `/packages/curve-ui-kit`: Shared UI kit created using Material UI, mapped as `@ui-kit` +- `/packages/prices-api`: Package for consuming the Prices API, mapped as `@curvefi/prices-api`. Soon to be to separated its own NPM package. ## Development Guide diff --git a/apps/dao/src/file-types.d.ts b/apps/dao/src/file-types.d.ts new file mode 100644 index 000000000..270473c03 --- /dev/null +++ b/apps/dao/src/file-types.d.ts @@ -0,0 +1,9 @@ +/// + +declare module '*.svg' { + const ReactComponent: React.FC> + const content: string + + export { ReactComponent } + export default content +} diff --git a/apps/lend/src/file-types.d.ts b/apps/lend/src/file-types.d.ts new file mode 100644 index 000000000..270473c03 --- /dev/null +++ b/apps/lend/src/file-types.d.ts @@ -0,0 +1,9 @@ +/// + +declare module '*.svg' { + const ReactComponent: React.FC> + const content: string + + export { ReactComponent } + export default content +} diff --git a/apps/loan/src/file-types.d.ts b/apps/loan/src/file-types.d.ts new file mode 100644 index 000000000..270473c03 --- /dev/null +++ b/apps/loan/src/file-types.d.ts @@ -0,0 +1,9 @@ +/// + +declare module '*.svg' { + const ReactComponent: React.FC> + const content: string + + export { ReactComponent } + export default content +} diff --git a/apps/main/sitemap.js b/apps/main/sitemap.js index daea2f761..6ee257de8 100644 --- a/apps/main/sitemap.js +++ b/apps/main/sitemap.js @@ -1,7 +1,7 @@ -import { MAIN_ROUTE as MAIN_ROUTES } from '@main/constants' -import { MAIN_ROUTE as LOAN_ROUTES } from '@loan/constants' -import { MAIN_ROUTE as LEND_ROUTES } from '@lend/constants' -import { MAIN_ROUTE as DAO_ROUTES } from '@dao/constants' +import { MAIN_ROUTE as MAIN_ROUTES } from '@/dex/constants' +import { MAIN_ROUTE as LOAN_ROUTES } from '@/loan/constants' +import { MAIN_ROUTE as LEND_ROUTES } from '@/lend/constants' +import { MAIN_ROUTE as DAO_ROUTES } from '@/dao/constants' import { getAppRoot } from 'curve-ui-kit/src/shared/routes' const ROUTES = { diff --git a/apps/main/src/dao/components/ConnectWallet.tsx b/apps/main/src/dao/components/ConnectWallet.tsx deleted file mode 100644 index 451e25ed2..000000000 --- a/apps/main/src/dao/components/ConnectWallet.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { isLoading } from '@ui/utils' -import useStore from '@/dao/store/useStore' -import { useUserProfileStore } from '@ui-kit/features/user-profile' - -import ConnectWalletPrompt from '@ui/ConnectWalletPrompt' - -type ConnectWalletProps = { - description: string - connectText: string - loadingText: string -} - -const ConnectWallet: React.FC = ({ description, connectText, loadingText }) => { - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - const connectState = useStore((state) => state.connectState) - const theme = useUserProfileStore((state) => state.theme) - - const loading = isLoading(connectState) - - return ( - - ) -} - -export default ConnectWallet diff --git a/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx b/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx index 8988d20bc..8a9961e98 100644 --- a/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx +++ b/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx @@ -1,16 +1,15 @@ import { useEffect } from 'react' import styled from 'styled-components' - import useStore from '@/dao/store/useStore' import { formatNumber } from '@ui/utils' import { t } from '@lingui/macro' - import Box from '@ui/Box' import MetricsComp, { MetricsColumnData } from '@/dao/components/MetricsComp' import Tooltip from '@ui/Tooltip' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const CrvStats: React.FC = () => { - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const { veCrvData, getVeCrvData, veCrvFees, veCrvHolders } = useStore((state) => state.analytics) const { loading: usdRatesLoading, usdRatesMapper } = useStore((state) => state.usdRates) const crv = usdRatesMapper.crv diff --git a/apps/main/src/dao/components/PageGauges/GaugeVoting/index.tsx b/apps/main/src/dao/components/PageGauges/GaugeVoting/index.tsx index 174fe04d1..6cdaa1d06 100644 --- a/apps/main/src/dao/components/PageGauges/GaugeVoting/index.tsx +++ b/apps/main/src/dao/components/PageGauges/GaugeVoting/index.tsx @@ -1,15 +1,17 @@ import styled from 'styled-components' import { useEffect } from 'react' - import useStore from '@/dao/store/useStore' - import CurrentVotes from './CurrentVotes' -import ConnectWallet from '@/dao/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { isLoading } from '@ui/utils' const GaugeVoting = ({ userAddress }: { userAddress: string | undefined }) => { const { getUserGaugeVoteWeights, userGaugeVoteWeightsMapper } = useStore((state) => state.user) const curve = useStore((state) => state.curve) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) useEffect(() => { if (userAddress && curve && userGaugeVoteWeightsMapper[userAddress.toLowerCase()] === undefined) { @@ -19,10 +21,12 @@ const GaugeVoting = ({ userAddress }: { userAddress: string | undefined }) => { return !provider ? ( - connectWallet()} + isLoading={isLoading(connectState)} /> ) : ( diff --git a/apps/main/src/dao/components/PageGauges/index.tsx b/apps/main/src/dao/components/PageGauges/index.tsx index cd6672262..4ed70cd48 100644 --- a/apps/main/src/dao/components/PageGauges/index.tsx +++ b/apps/main/src/dao/components/PageGauges/index.tsx @@ -16,7 +16,7 @@ import SubNav from '@/dao/components/SubNav' const Gauges = () => { const { isMdUp } = useStore((state) => state.layout) - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const userAddress = wallet?.accounts[0].address const [navSelection, setNavSelection] = useState('gaugeList') diff --git a/apps/main/src/dao/components/PageProposal/index.tsx b/apps/main/src/dao/components/PageProposal/index.tsx index 8d0226ef2..8cc127d2d 100644 --- a/apps/main/src/dao/components/PageProposal/index.tsx +++ b/apps/main/src/dao/components/PageProposal/index.tsx @@ -25,6 +25,7 @@ import BackButton from '../BackButton' import ProposalHeader from './ProposalHeader' import ProposalInformation from './ProposalInformation' import { ProposalType } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type ProposalProps = { routerParams: { @@ -34,7 +35,7 @@ type ProposalProps = { const Proposal: React.FC = ({ routerParams: { rProposalId } }) => { const [voteId, voteType] = rProposalId.split('-') as [string, ProposalType] - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const { proposalsLoadingState, getProposal, proposalLoadingState, getUserProposalVote } = useStore( (state) => state.proposals, ) diff --git a/apps/main/src/dao/components/PageUser/index.tsx b/apps/main/src/dao/components/PageUser/index.tsx index 7426a757f..e438ed39e 100644 --- a/apps/main/src/dao/components/PageUser/index.tsx +++ b/apps/main/src/dao/components/PageUser/index.tsx @@ -12,6 +12,7 @@ import UserLocksTable from './UserLocksTable' import UserGaugeVotesTable from './UserGaugeVotesTable' import SubNav from '@/dao/components/SubNav' import { VeCrvHolder } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type UserPageProps = { routerParams: { @@ -25,7 +26,7 @@ const UserPage: React.FC = ({ routerParams: { rUserAddress } }) = getVeCrvHolders, } = useStore((state) => state.analytics) const { getUserEns, userMapper } = useStore((state) => state.user) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const [activeNavKey, setNavKey] = useState('proposals') const navItems = [ diff --git a/apps/main/src/dao/components/PageVeCrv/components/FormActions.tsx b/apps/main/src/dao/components/PageVeCrv/components/FormActions.tsx index d1043ad41..32e07233d 100644 --- a/apps/main/src/dao/components/PageVeCrv/components/FormActions.tsx +++ b/apps/main/src/dao/components/PageVeCrv/components/FormActions.tsx @@ -1,10 +1,8 @@ import React from 'react' import { t } from '@lingui/macro' - -import useStore from '@/dao/store/useStore' - import Button from '@ui/Button' import Spinner from '@ui/Spinner' +import useStore from '@/dao/store/useStore' const FormActions = ({ haveSigner, @@ -14,12 +12,11 @@ const FormActions = ({ haveSigner: boolean loading: boolean }>) => { - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - + const connectWallet = useStore((s) => s.updateConnectState) return ( <> {!haveSigner && !loading ? ( - ) : loading ? ( diff --git a/apps/main/src/dao/components/PageVeCrv/components/FormLockCreate.tsx b/apps/main/src/dao/components/PageVeCrv/components/FormLockCreate.tsx index 44b97359f..2a6dfb67f 100644 --- a/apps/main/src/dao/components/PageVeCrv/components/FormLockCreate.tsx +++ b/apps/main/src/dao/components/PageVeCrv/components/FormLockCreate.tsx @@ -24,6 +24,7 @@ import FieldLockedAmt from '@/dao/components/PageVeCrv/components/FieldLockedAmt import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockCreate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -36,7 +37,7 @@ const FormLockCreate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => const formValues = useStore((state) => state.lockedCrv.formValues) const fetchStepApprove = useStore((state) => state.lockedCrv.fetchStepApprove) const fetchStepCreate = useStore((state) => state.lockedCrv.fetchStepCreate) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) const [steps, setSteps] = useState([]) diff --git a/apps/main/src/dao/components/PageVeCrv/components/FormLockCrv.tsx b/apps/main/src/dao/components/PageVeCrv/components/FormLockCrv.tsx index 30bcea611..31052b8c3 100644 --- a/apps/main/src/dao/components/PageVeCrv/components/FormLockCrv.tsx +++ b/apps/main/src/dao/components/PageVeCrv/components/FormLockCrv.tsx @@ -19,6 +19,7 @@ import FieldLockedAmt from '@/dao/components/PageVeCrv/components/FieldLockedAmt import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockCrv = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -29,7 +30,7 @@ const FormLockCrv = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const formEstGas = useStore((state) => state.lockedCrv.formEstGas[activeKey] ?? DEFAULT_FORM_EST_GAS) const formStatus = useStore((state) => state.lockedCrv.formStatus) const formValues = useStore((state) => state.lockedCrv.formValues) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepApprove = useStore((state) => state.lockedCrv.fetchStepApprove) const fetchStepIncreaseCrv = useStore((state) => state.lockedCrv.fetchStepIncreaseCrv) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) diff --git a/apps/main/src/dao/components/PageVeCrv/components/FormLockDate.tsx b/apps/main/src/dao/components/PageVeCrv/components/FormLockDate.tsx index 6f945c83b..4141fcb40 100644 --- a/apps/main/src/dao/components/PageVeCrv/components/FormLockDate.tsx +++ b/apps/main/src/dao/components/PageVeCrv/components/FormLockDate.tsx @@ -23,6 +23,7 @@ import FieldDatePicker from '@/dao/components/PageVeCrv/components/FieldDatePick import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockDate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -33,7 +34,7 @@ const FormLockDate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const formEstGas = useStore((state) => state.lockedCrv.formEstGas[activeKey] ?? DEFAULT_FORM_EST_GAS) const formStatus = useStore((state) => state.lockedCrv.formStatus) const formValues = useStore((state) => state.lockedCrv.formValues) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepIncreaseTime = useStore((state) => state.lockedCrv.fetchStepIncreaseTime) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) diff --git a/apps/main/src/dao/components/UserBox/index.tsx b/apps/main/src/dao/components/UserBox/index.tsx index ea607b6c9..b1f7b73dd 100644 --- a/apps/main/src/dao/components/UserBox/index.tsx +++ b/apps/main/src/dao/components/UserBox/index.tsx @@ -5,7 +5,7 @@ import { t } from '@lingui/macro' import Box from '@ui/Box' import Button from '@ui/Button' import UserInformation from './UserInformation' -import { SnapshotVotingPower, ActiveProposal } from '@/dao/types/dao.types' +import { ActiveProposal, SnapshotVotingPower } from '@/dao/types/dao.types' type Props = { children?: React.ReactNode @@ -17,12 +17,7 @@ type Props = { } const UserBox = ({ className, children, votingPower, snapshotVotingPower, activeProposal, row }: Props) => { - const [{ wallet }, connect] = useConnectWallet() - - const handleConnectWallet = async () => { - await connect() - } - + const { wallet, connect } = useConnectWallet() return ( {wallet ? ( @@ -37,7 +32,7 @@ const UserBox = ({ className, children, votingPower, snapshotVotingPower, active ) : (

{t`Please connect a wallet to see user information.`}

- + connect()}> {t`Connect Wallet`}
diff --git a/apps/main/src/dao/constants.ts b/apps/main/src/dao/constants.ts index e7e352113..0eede5b81 100644 --- a/apps/main/src/dao/constants.ts +++ b/apps/main/src/dao/constants.ts @@ -1,4 +1,5 @@ import { DAO_ROUTES } from '@ui-kit/shared/routes' +export { CONNECT_STAGE } from '@ui/utils/utilsConnectState' export const NETWORK_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' @@ -10,13 +11,6 @@ export const ROUTE = { PAGE_404: '/404', } as const -export const CONNECT_STAGE = { - CONNECT_API: 'api', - CONNECT_WALLET: 'connect-wallet', - DISCONNECT_WALLET: 'disconnect-wallet', - SWITCH_NETWORK: 'switch-network', -} as const - export const REFRESH_INTERVAL = { '3s': 3000, '1m': 60000, diff --git a/apps/main/src/dao/hooks/usePageOnMount.ts b/apps/main/src/dao/hooks/usePageOnMount.ts index 5e7d80a7c..941ee435a 100644 --- a/apps/main/src/dao/hooks/usePageOnMount.ts +++ b/apps/main/src/dao/hooks/usePageOnMount.ts @@ -1,35 +1,38 @@ import type { Location, NavigateFunction, Params } from 'react-router' import type { ConnectState } from '@ui/utils' - +import { isFailure, isLoading, isSuccess } from '@ui/utils' import { ethers } from 'ethers' import { useCallback, useEffect } from 'react' -import { useConnectWallet, useSetChain, useSetLocale } from '@ui-kit/features/connect-wallet' - +import { + getWalletChainId, + getWalletSignerAddress, + useConnectWallet, + useSetChain, + useSetLocale, +} from '@ui-kit/features/connect-wallet' import { CONNECT_STAGE, REFRESH_INTERVAL } from '@/dao/constants' import { dynamicActivate, updateAppLocale } from '@ui-kit/lib/i18n' -import { getStorageValue, setStorageValue } from '@/dao/utils/utilsStorage' import { getNetworkFromUrl, parseParams } from '@/dao/utils/utilsRouter' -import { getWalletChainId, getWalletSignerAddress } from '@/dao/store/createWalletSlice' import { helpers } from '@/dao/lib/curvejs' -import { isFailure, isLoading, isSuccess } from '@ui/utils' import networks from '@/dao/networks' import useStore from '@/dao/store/useStore' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { ChainId, PageProps, Wallet } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' function usePageOnMount(params: Params, location: Location, navigate: NavigateFunction, chainIdNotRequired?: boolean) { - const [{ wallet }, connect, disconnect] = useConnectWallet() + const { wallet, connect, disconnect, walletName, setWalletName } = useConnectWallet() const [_, setChain] = useSetChain() const updateWalletLocale = useSetLocale() const curve = useStore((state) => state.curve) - const connectState = useStore((state) => state.connectState) + const connectState = useStore((s) => s.connectState) + const chooseWallet = useWalletStore((s) => s.chooseWallet) const updateConnectState = useStore((state) => state.updateConnectState) const updateCurveJs = useStore((state) => state.updateCurveJs) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) const setLocale = useUserProfileStore((state) => state.setLocale) - const walletChainId = getWalletChainId(wallet) const walletSignerAddress = getWalletSignerAddress(wallet) const parsedParams = parseParams(params, chainIdNotRequired) @@ -58,7 +61,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu async (options: ConnectState['options']) => { if (options) { const [walletName] = options - let walletState: Wallet | null = null + let walletState: Wallet | null if (walletName) { // If found label in localstorage, after 30s if not connected, reconnect with modal @@ -83,7 +86,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu walletState = walletStates[0] } catch (error) { // if failed to get walletState due to timeout, show connect modal. - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) ;[walletState] = await connect() } } else { @@ -92,7 +95,8 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu try { if (!walletState) throw new Error('unable to connect') - setStorageValue('APP_CACHE', { walletName: walletState.label, timestamp: Date.now().toString() }) + chooseWallet(walletState) + setWalletName(walletState.label) const walletChainId = getWalletChainId(walletState) if (walletChainId && walletChainId !== parsedParams.rChainId) { const success = await setChain({ chainId: ethers.toQuantity(parsedParams.rChainId) }) @@ -112,24 +116,24 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } catch (error) { updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) } } }, - [connect, navigate, parsedParams, setChain, updateConnectState], + [connect, chooseWallet, navigate, parsedParams, setChain, updateConnectState, setWalletName], ) const handleDisconnectWallet = useCallback( async (wallet: Wallet) => { try { await disconnect(wallet) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) } catch (error) { console.error(error) } }, - [disconnect, parsedParams.rChainId, updateConnectState], + [disconnect, parsedParams.rChainId, setWalletName, updateConnectState], ) const handleNetworkSwitch = useCallback( @@ -164,7 +168,6 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu useEffect(() => { if (connectState.status === '' && connectState.stage === '') { updateGlobalStoreByKey('routerProps', { params, location, navigate }) - const walletName = getStorageValue('APP_CACHE')?.walletName ?? '' if (walletName) { updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [walletName]) } else { diff --git a/apps/main/src/dao/layout/Header.tsx b/apps/main/src/dao/layout/Header.tsx index 294bf38b4..c95a68394 100644 --- a/apps/main/src/dao/layout/Header.tsx +++ b/apps/main/src/dao/layout/Header.tsx @@ -18,7 +18,7 @@ import { ChainId } from '@/dao/types/dao.types' type HeaderProps = { sections: NavigationSection[]; BannerProps: GlobalBannerProps } export const Header = ({ sections, BannerProps }: HeaderProps) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const mainNavRef = useRef(null) const navigate = useNavigate() useLayoutHeight(mainNavRef, 'mainNav') diff --git a/apps/main/src/dao/layout/index.tsx b/apps/main/src/dao/layout/index.tsx index 7969bf0c3..d1b35181b 100644 --- a/apps/main/src/dao/layout/index.tsx +++ b/apps/main/src/dao/layout/index.tsx @@ -5,9 +5,8 @@ import styled from 'styled-components' import { t } from '@lingui/macro' import { CONNECT_STAGE, isFailure, isLoading } from '@ui/utils' -import { getWalletChainId } from '@/dao/store/createWalletSlice' +import { getWalletChainId, useConnectWallet } from '@ui-kit/features/connect-wallet' import { getNetworkFromUrl } from '@/dao/utils/utilsRouter' -import { useConnectWallet } from '@ui-kit/features/connect-wallet' import { useHeightResizeObserver } from '@ui/hooks' import useStore from '@/dao/store/useStore' @@ -16,7 +15,7 @@ import { Footer } from '@ui-kit/widgets/Footer' import { useUserProfileStore } from '@ui-kit/features/user-profile' const BaseLayout = ({ children }: { children: React.ReactNode }) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const globalAlertRef = useRef(null) const globalAlertHeight = useHeightResizeObserver(globalAlertRef) diff --git a/apps/main/src/dao/store/createAnalyticsSlice.ts b/apps/main/src/dao/store/createAnalyticsSlice.ts index 3eb413d6e..ba8a53ca3 100644 --- a/apps/main/src/dao/store/createAnalyticsSlice.ts +++ b/apps/main/src/dao/store/createAnalyticsSlice.ts @@ -18,6 +18,7 @@ import { TopHoldersSortBy, AllHoldersSortBy, } from '@/dao/types/dao.types' +import type { ContractRunner } from 'ethers/lib.commonjs/providers' type StateKey = keyof typeof DEFAULT_STATE @@ -67,7 +68,7 @@ export type AnalyticsSlice = { getVeCrvHolders(): Promise setTopHoldersSortBy(sortBy: TopHoldersSortBy): void setAllHoldersSortBy(sortBy: AllHoldersSortBy): void - getVeCrvData(provider: any): Promise + getVeCrvData(provider: ContractRunner): Promise // helpers setStateByActiveKey(key: StateKey, activeKey: string, value: T): void setStateByKey(key: StateKey, value: T): void @@ -308,7 +309,7 @@ const createAnalyticsSlice = (set: SetState, get: GetState): Analy ) } }, - getVeCrvData: async (provider: any) => { + getVeCrvData: async (provider) => { get()[sliceKey].setStateByKey('veCrvData', { totalVeCrv: 0, totalLockedCrv: 0, diff --git a/apps/main/src/dao/store/createAppSlice.ts b/apps/main/src/dao/store/createAppSlice.ts index f626db455..f82b9fb6b 100644 --- a/apps/main/src/dao/store/createAppSlice.ts +++ b/apps/main/src/dao/store/createAppSlice.ts @@ -1,10 +1,8 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/dao/store/useStore' -import type { ConnectState } from '@ui/utils' - +import { CONNECT_STAGE, ConnectState } from '@ui/utils' import isEqual from 'lodash/isEqual' import produce from 'immer' - import { log } from '@ui-kit/lib' import { CurveApi, RouterProps, Wallet } from '@/dao/types/dao.types' @@ -19,8 +17,8 @@ export type LayoutHeight = { } type SliceState = { - connectState: ConnectState curve: CurveApi | null + connectState: ConnectState isLoadingApi: boolean isLoadingCurve: boolean isMobile: boolean @@ -33,7 +31,7 @@ type SliceState = { // prettier-ignore export interface AppSlice extends SliceState { - updateConnectState(status: ConnectState['status'], stage: ConnectState['stage'], options?: ConnectState['options']): void + updateConnectState(status?: ConnectState['status'], stage?: ConnectState['stage'], options?: ConnectState['options']): void updateCurveJs(curveApi: CurveApi, prevCurveApi: CurveApi | null, wallet: Wallet | null): Promise updateLayoutHeight: (key: keyof LayoutHeight, value: number | null) => void updateShowScrollButton(scrollY: number): void @@ -46,14 +44,12 @@ export interface AppSlice extends SliceState { } const DEFAULT_STATE = { - connectState: { status: '', stage: '' } as ConnectState, curve: null, isMobile: false, isLoadingApi: false, isLoadingCurve: true, isPageVisible: true, loaded: false, - pageWidth: null, layoutHeight: { globalAlert: 0, mainNav: 0, @@ -62,18 +58,14 @@ const DEFAULT_STATE = { }, routerProps: null, showScrollButton: false, -} + connectState: { status: '', stage: '' }, +} satisfies SliceState const createAppSlice = (set: SetState, get: GetState): AppSlice => ({ ...DEFAULT_STATE, - updateConnectState: ( - status: ConnectState['status'], - stage: ConnectState['stage'], - options?: ConnectState['options'], - ) => { - const value = options ? { status, stage, options } : { status, stage } - get().updateGlobalStoreByKey('connectState', value) + updateConnectState: (status = 'loading', stage = CONNECT_STAGE.CONNECT_WALLET, options = ['']) => { + set({ connectState: { status, stage, ...(options && { options }) } }) }, updateCurveJs: async (curveApi: CurveApi, prevCurveApi: CurveApi | null, wallet: Wallet | null) => { const isNetworkSwitched = !!prevCurveApi?.chainId && prevCurveApi.chainId !== curveApi.chainId diff --git a/apps/main/src/dao/store/createGasSlice.ts b/apps/main/src/dao/store/createGasSlice.ts index c37fd47ca..bb770229b 100644 --- a/apps/main/src/dao/store/createGasSlice.ts +++ b/apps/main/src/dao/store/createGasSlice.ts @@ -8,6 +8,7 @@ import { httpFetcher } from '@/dao/utils' import { log } from '@ui-kit/lib' import networks from '@/dao/networks' import { CurveApi, Provider, GasInfo } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -69,7 +70,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => if (parsedGasInfo) { get()[sliceKey].setStateByKeys(parsedGasInfo) } else { - const provider = get().wallet.getProvider('') + const { provider } = useWalletStore.getState() if (provider && chainId) { const parsedGasInfo = await parseGasInfo(curve, provider) if (parsedGasInfo) { diff --git a/apps/main/src/dao/store/createGaugesSlice.ts b/apps/main/src/dao/store/createGaugesSlice.ts index fa68a79a2..63ed37651 100644 --- a/apps/main/src/dao/store/createGaugesSlice.ts +++ b/apps/main/src/dao/store/createGaugesSlice.ts @@ -7,20 +7,21 @@ import { t } from '@lingui/macro' import { shortenTokenAddress } from '@ui/utils' import { CurveGaugeResponse, - PricesGaugeOverviewResponse, + FetchingState, + GaugeCurveApiDataMapper, GaugeFormattedData, GaugeMapper, - GaugeCurveApiDataMapper, - GaugeVotesResponse, GaugeVotesMapper, + GaugeVotesResponse, + GaugeVotesSortBy, GaugeWeightHistoryData, - FetchingState, - TransactionState, - SortByFilterGaugesKeys, + PricesGaugeOverviewResponse, SortByFilterGauges, + SortByFilterGaugesKeys, SortDirection, - GaugeVotesSortBy, + TransactionState, } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -366,18 +367,14 @@ const createGaugesSlice = (set: SetState, get: GetState): GaugesSl castVote: async (userAddress: string, gaugeAddress: string, voteWeight: number) => { const curve = get().curve - const provider = get().wallet.getProvider('') + const { provider } = useWalletStore.getState() const { getUserGaugeVoteWeights } = get().user if (!curve) return const address = gaugeAddress.toLowerCase() - const notifyNotification = get().wallet.notifyNotification - let dismissNotificationHandler - - const notifyPendingMessage = t`Please confirm cast vote.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') - dismissNotificationHandler = dismissConfirm + const { notify } = useWalletStore.getState() + const { dismiss: dismissConfirm } = notify(t`Please confirm cast vote.`, 'pending') set( produce(get(), (state) => { @@ -404,8 +401,7 @@ const createGaugesSlice = (set: SetState, get: GetState): GaugesSl dismissConfirm() const loadingNotificationMessage = t`Casting vote...` - const { dismiss: dismissLoading } = notifyNotification(loadingNotificationMessage, 'pending') - dismissNotificationHandler = dismissLoading + const { dismiss: dismissLoading } = notify(loadingNotificationMessage, 'pending') await provider!.waitForTransaction(res) @@ -420,7 +416,7 @@ const createGaugesSlice = (set: SetState, get: GetState): GaugesSl ) dismissLoading() const successNotificationMessage = t`Succesfully cast vote!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) await getUserGaugeVoteWeights(userAddress, true) diff --git a/apps/main/src/dao/store/createLockedCrvSlice.ts b/apps/main/src/dao/store/createLockedCrvSlice.ts index 7bd5720e2..f7af1daa5 100644 --- a/apps/main/src/dao/store/createLockedCrvSlice.ts +++ b/apps/main/src/dao/store/createLockedCrvSlice.ts @@ -21,6 +21,7 @@ import { FnStepApproveResponse, FnStepResponse, } from '@/dao/types/dao.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -162,43 +163,43 @@ const createLockedCrvSlice = (set: SetState, get: GetState): Locke return resp }, fetchStepApprove: async (activeKey, curve, rFormType, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider) { - let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) - cFormStatus.formProcessing = true - cFormStatus.step = 'APPROVAL' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const { chainId } = curve - const approveFn = networks[chainId].api.lockCrv.lockCrvApprove - const resp = await approveFn(activeKey, provider, curve, formValues.lockedAmt) - - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus.formProcessing = false - cFormStatus.step = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.isApproved = true - cFormStatus.formTypeCompleted = 'APPROVE' - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - - // fetch est gas and approval - await get()[sliceKey].fetchEstGasApproval(activeKey, curve, rFormType, formValues) - } - - return resp + let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) + cFormStatus.formProcessing = true + cFormStatus.step = 'APPROVAL' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + + await get().gas.fetchGasInfo(curve) + const { chainId } = curve + const approveFn = networks[chainId].api.lockCrv.lockCrvApprove + const resp = await approveFn(activeKey, provider, curve, formValues.lockedAmt) + + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus.formProcessing = false + cFormStatus.step = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.isApproved = true + cFormStatus.formTypeCompleted = 'APPROVE' + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + + // fetch est gas and approval + await get()[sliceKey].fetchEstGasApproval(activeKey, curve, rFormType, formValues) } + + return resp } }, fetchStepCreate: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider && formValues.lockedAmt && formValues.utcDate && formValues.days) { + if (formValues.lockedAmt && formValues.utcDate && formValues.days) { let cFormStatus = cloneDeep(get()[sliceKey].formStatus) cFormStatus.formProcessing = true cFormStatus.step = 'CREATE_LOCK' @@ -228,7 +229,7 @@ const createLockedCrvSlice = (set: SetState, get: GetState): Locke // re-fetch data const fetchedVecrvInfo = await get()[sliceKey].fetchVecrvInfo(curve) - const wallet = get().wallet.onboard?.state.get().wallets?.[0] + const { wallet } = useWalletStore.getState() if (wallet) { get().user.updateUserData(curve, wallet) } @@ -241,87 +242,85 @@ const createLockedCrvSlice = (set: SetState, get: GetState): Locke } }, fetchStepIncreaseCrv: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = true - cFormStatus.step = 'INCREASE_CRV' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const { chainId } = curve - const fn = networks[chainId].api.lockCrv.increaseAmount - const resp = await fn(activeKey, curve, provider, formValues.lockedAmt) - - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'INCREASE_CRV' - get()[sliceKey].setStateByKeys({ - formValues: cloneDeep(DEFAULT_FORM_VALUES), - formStatus: cloneDeep(cFormStatus), - }) + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = true + cFormStatus.step = 'INCREASE_CRV' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + + await get().gas.fetchGasInfo(curve) + const { chainId } = curve + const fn = networks[chainId].api.lockCrv.increaseAmount + const resp = await fn(activeKey, curve, provider, formValues.lockedAmt) + + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'INCREASE_CRV' + get()[sliceKey].setStateByKeys({ + formValues: cloneDeep(DEFAULT_FORM_VALUES), + formStatus: cloneDeep(cFormStatus), + }) - // re-fetch data - get()[sliceKey].fetchVecrvInfo(curve) - const wallet = get().wallet.onboard?.state.get().wallets?.[0] - if (wallet) { - get().user.updateUserData(curve, wallet) - } + // re-fetch data + get()[sliceKey].fetchVecrvInfo(curve) + const { wallet } = useWalletStore.getState() + if (wallet) { + get().user.updateUserData(curve, wallet) } - - return resp } + + return resp } }, fetchStepIncreaseTime: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = true - cFormStatus.step = 'INCREASE_TIME' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const { chainId } = curve - const fn = networks[chainId].api.lockCrv.increaseUnlockTime - const resp = await fn(activeKey, provider, curve, formValues.days) - - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'INCREASE_TIME' - get()[sliceKey].setStateByKeys({ - formValues: cloneDeep(DEFAULT_FORM_VALUES), - formStatus: cloneDeep(cFormStatus), - }) + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = true + cFormStatus.step = 'INCREASE_TIME' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + + await get().gas.fetchGasInfo(curve) + const { chainId } = curve + const fn = networks[chainId].api.lockCrv.increaseUnlockTime + const resp = await fn(activeKey, provider, curve, formValues.days) + + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'INCREASE_TIME' + get()[sliceKey].setStateByKeys({ + formValues: cloneDeep(DEFAULT_FORM_VALUES), + formStatus: cloneDeep(cFormStatus), + }) - // re-fetch data - get()[sliceKey].fetchVecrvInfo(curve) - const wallet = get().wallet.onboard?.state.get().wallets?.[0] - if (wallet) { - get().user.updateUserData(curve, wallet) - } + // re-fetch data + get()[sliceKey].fetchVecrvInfo(curve) + const { wallet } = useWalletStore.getState() + if (wallet) { + get().user.updateUserData(curve, wallet) } - - return resp } + + return resp } }, diff --git a/apps/main/src/dao/store/createProposalsSlice.ts b/apps/main/src/dao/store/createProposalsSlice.ts index a84bd15d0..4a031eb0d 100644 --- a/apps/main/src/dao/store/createProposalsSlice.ts +++ b/apps/main/src/dao/store/createProposalsSlice.ts @@ -10,19 +10,20 @@ import orderBy from 'lodash/orderBy' import produce from 'immer' import { t } from '@lingui/macro' import { - ProposalType, + FetchingState, + PricesProposalResponse, PricesProposalResponseData, - ProposalData, PricesProposalsResponse, - PricesProposalResponse, - ProposalMapper, - UserProposalVoteResData, - FetchingState, - TransactionState, + ProposalData, ProposalListFilter, + ProposalMapper, + ProposalType, SortByFilterProposals, SortDirection, + TransactionState, + UserProposalVoteResData, } from '@/dao/types/dao.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -336,16 +337,13 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo castVote: async (voteId: number, voteType: ProposalType, support: boolean) => { const voteIdKey = `${voteId}-${voteType}` const { curve } = get() - const provider = get().wallet.getProvider('') - const notifyNotification = get().wallet.notifyNotification - const fetchGasInfo = get().gas.fetchGasInfo + const { provider, notify } = useWalletStore.getState() - let dismissNotificationHandler + const fetchGasInfo = get().gas.fetchGasInfo if (!curve || !provider) return - const notifyPendingMessage = t`Please confirm to cast vote.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') + const { dismiss: dismissConfirm } = notify(t`Please confirm to cast vote.`, 'pending') get()[sliceKey].setStateByKey('voteTxMapper', { ...get()[sliceKey].voteTxMapper, [voteIdKey]: { @@ -356,7 +354,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo }, }) - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = dismissConfirm try { await fetchGasInfo(curve) @@ -380,7 +378,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo dismissConfirm() const deployingNotificationMessage = t`Casting vote...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') + const { dismiss: dismissDeploying } = notify(deployingNotificationMessage, 'pending') dismissNotificationHandler = dismissDeploying get()[sliceKey].setStateByKey('voteTxMapper', { @@ -405,7 +403,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo dismissDeploying() const successNotificationMessage = t`Vote casted successfully!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) // get new user votes list from api const userAddress = get().user.userAddress @@ -436,16 +434,12 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo const { curve } = get() const voteIdKey = `${voteId}-${voteType}` - const provider = get().wallet.getProvider('') - const notifyNotification = get().wallet.notifyNotification + const { provider, notify } = useWalletStore.getState() const fetchGasInfo = get().gas.fetchGasInfo - let dismissNotificationHandler - if (!curve || !provider) return - const notifyPendingMessage = t`Please confirm to execute proposal.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') + const { dismiss: dismissConfirm } = notify(t`Please confirm to execute proposal.`, 'pending') get()[sliceKey].setStateByKey('executeTxMapper', { ...get()[sliceKey].executeTxMapper, [voteIdKey]: { @@ -456,7 +450,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo }, }) - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = dismissConfirm try { await fetchGasInfo(curve) @@ -480,7 +474,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo dismissConfirm() const deployingNotificationMessage = t`Executing proposal...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') + const { dismiss: dismissDeploying } = notify(deployingNotificationMessage, 'pending') dismissNotificationHandler = dismissDeploying get()[sliceKey].setStateByKey('executeTxMapper', { @@ -497,7 +491,7 @@ const createProposalsSlice = (set: SetState, get: GetState): Propo dismissDeploying() const successNotificationMessage = t`Proposal executed successfully!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) // update proposal executed status, forcing api to update by providing a transaction hash await get()[sliceKey].getProposal(voteId, voteType, true, transactionHash) diff --git a/apps/main/src/dao/store/createUserSlice.ts b/apps/main/src/dao/store/createUserSlice.ts index 228a9b25d..25c57f021 100644 --- a/apps/main/src/dao/store/createUserSlice.ts +++ b/apps/main/src/dao/store/createUserSlice.ts @@ -6,27 +6,27 @@ import { Contract } from 'ethers' import produce from 'immer' import { SEVEN_DAYS } from '@/dao/constants' -import { getWalletSignerAddress, getWalletSignerEns } from '@/dao/store/createWalletSlice' +import { getWalletSignerAddress, getWalletSignerEns, useWalletStore } from '@ui-kit/features/connect-wallet' import { contractVeCRV } from '@/dao/store/contracts' import { abiVeCrv } from '@/dao/store/abis' import { CurveApi, - UserMapper, - UserVoteData, + FetchingState, SnapshotVotingPower, - UserLockRes, - UserLock, - UserProposalVoteData, - UserProposalVotesRes, + SortDirection, UserGaugeVote, UserGaugeVotesRes, + UserGaugeVotesSortBy, UserGaugeVoteWeightsMapper, - FetchingState, - SortDirection, + UserGaugeVoteWeightSortBy, + UserLock, + UserLockRes, UserLocksSortBy, - UserGaugeVotesSortBy, + UserMapper, + UserProposalVoteData, + UserProposalVotesRes, UserProposalVotesSortBy, - UserGaugeVoteWeightSortBy, + UserVoteData, } from '@/dao/types/dao.types' type StateKey = keyof typeof DEFAULT_STATE @@ -150,7 +150,7 @@ const createUserSlice = (set: SetState, get: GetState): UserSlice ...DEFAULT_STATE, updateUserData: async (curve: CurveApi, wallet: WalletState) => { const getUserProposalVotes = get()[sliceKey].getUserProposalVotes - const userAddress = getWalletSignerAddress(wallet) + const userAddress = getWalletSignerAddress(wallet)! try { const veCRV = await curve.dao.userVeCrv(userAddress) @@ -169,7 +169,7 @@ const createUserSlice = (set: SetState, get: GetState): UserSlice }) }, getUserEns: async (userAddress: string) => { - const provider = get().wallet.getProvider('') + const { provider } = useWalletStore.getState() if (!provider) { console.error("Can't fetch ens, no provider available") diff --git a/apps/main/src/dao/store/createWalletSlice.ts b/apps/main/src/dao/store/createWalletSlice.ts deleted file mode 100644 index 8f83ea934..000000000 --- a/apps/main/src/dao/store/createWalletSlice.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { CustomNotification, NotificationType } from '@web3-onboard/core/dist/types' -import type { GetState, SetState } from 'zustand' -import type { OnboardAPI, UpdateNotification } from '@web3-onboard/core' -import type { State } from '@/dao/store/useStore' - -import { BrowserProvider, ethers } from 'ethers' -import cloneDeep from 'lodash/cloneDeep' - -import { CONNECT_STAGE } from '@/dao/constants' -import { Provider, Wallet } from '@/dao/types/dao.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - onboard: OnboardAPI | null -} - -const sliceKey = 'wallet' - -// prettier-ignore -export type WalletSlice = { - [sliceKey]: SliceState & { - notifyNotification(message: string, type: NotificationType, autoDismiss?: number): ({ dismiss: () => void; update: UpdateNotification | undefined }) - updateConnectWalletStateKeys(): void - getProvider(sliceKey: 'lockedCrv' | ''): Provider | null - - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - onboard: null, -} - -const createWalletSlice = (set: SetState, get: GetState): WalletSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - notifyNotification: (message, type = 'pending', autoDismiss) => { - const onboard = get().wallet.onboard - - if (onboard) { - // see https://onboard.blocknative.com/docs/packages/core#options for all options - const customNotification: CustomNotification = { - type, - message, - } - - if (typeof autoDismiss !== 'undefined') { - customNotification.autoDismiss = autoDismiss - } - - return onboard.state.actions.customNotification(customNotification) - } else { - return { dismiss: () => {}, update: undefined } - } - }, - updateConnectWalletStateKeys: () => { - get().updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, ['']) - }, - getProvider: (sliceKey) => { - let provider = null - - // get provider from onboard - try { - const onboard = get().wallet.onboard - - if (onboard) { - const wallet = onboard.state.get().wallets?.[0] - provider = wallet ? new BrowserProvider(wallet.provider) : null - } - } catch (error) { - console.error('error getting wallet') - } - - // update form error if provider is not found - if (!provider && sliceKey) { - const storedFormStatus = get()[sliceKey]?.formStatus - if ( - storedFormStatus && - typeof storedFormStatus === 'object' && - 'step' in storedFormStatus && - 'formProcessing' in storedFormStatus && - 'error' in storedFormStatus - ) { - get()[sliceKey].setStateByKey('formStatus', { - ...storedFormStatus, - step: '', - formProcessing: false, - error: 'error-invalid-provider', - }) - } - } - return provider - }, - - // slice helpers - setStateByActiveKey: (key, activeKey, value) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key, value) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createWalletSlice - -export function getProvider(wallet: Wallet) { - return new ethers.BrowserProvider(wallet.provider) -} - -export function getWalletChainId(wallet: Wallet | undefined | null) { - if (!wallet) return null - const chainId = wallet.chains[0].id - return Number(BigInt(chainId).toString()) -} - -export function getWalletSignerAddress(wallet: Wallet | undefined | null) { - if (!wallet) return '' - return wallet.accounts[0]?.address -} - -export function getWalletSignerEns(wallet: Wallet | undefined | null) { - if (!wallet) return '' - return wallet.accounts[0]?.ens?.name -} diff --git a/apps/main/src/dao/store/useStore.ts b/apps/main/src/dao/store/useStore.ts index 818e268d2..3056aeffa 100644 --- a/apps/main/src/dao/store/useStore.ts +++ b/apps/main/src/dao/store/useStore.ts @@ -8,7 +8,6 @@ import merge from 'lodash/merge' import createAppSlice, { AppSlice } from '@/dao/store/createAppSlice' import createCacheSlice, { CacheSlice } from '@/dao/store/createCacheSlice' import createGasSlice, { GasSlice } from '@/dao/store/createGasSlice' -import createWalletSlice, { WalletSlice } from '@/dao/store/createWalletSlice' import createProposalsSlice, { ProposalsSlice } from '@/dao/store/createProposalsSlice' import createUserSlice, { UserSlice } from '@/dao/store/createUserSlice' import createGaugesSlice, { GaugesSlice } from '@/dao/store/createGaugesSlice' @@ -21,7 +20,6 @@ import createLayoutSlice, { AppLayoutSlice } from './createLayoutSlice' export type State = AppSlice & CacheSlice & GasSlice & - WalletSlice & ProposalsSlice & UserSlice & GaugesSlice & @@ -35,7 +33,6 @@ const store = (set: SetState, get: GetState): State => ({ ...createAppSlice(set, get), ...createGasSlice(set, get), ...createCacheSlice(set, get), - ...createWalletSlice(set, get), ...createProposalsSlice(set, get), ...createGaugesSlice(set, get), ...createUserSlice(set, get), diff --git a/apps/main/src/dao/utils/index.ts b/apps/main/src/dao/utils/index.ts index 519aaa424..5fbcfad10 100644 --- a/apps/main/src/dao/utils/index.ts +++ b/apps/main/src/dao/utils/index.ts @@ -1,6 +1,5 @@ import { AlertFormErrorKey } from '@/dao/types/dao.types' -export * from './utilsStorage' export * from './utilsRouter' export * from './utilsDates' diff --git a/apps/main/src/dao/utils/utilsStorage.ts b/apps/main/src/dao/utils/utilsStorage.ts deleted file mode 100644 index 67edd38b1..000000000 --- a/apps/main/src/dao/utils/utilsStorage.ts +++ /dev/null @@ -1,40 +0,0 @@ -import merge from 'lodash/merge' - -import dayjs from '@ui-kit/lib/dayjs' - -export const APP_STORAGE = { - APP_CACHE: 'dao-app-cache', -} - -type Key = keyof typeof APP_STORAGE - -export function getStorageValue(key: Key) { - const storedValue = window.localStorage.getItem(APP_STORAGE[key]) - let parsedStoredValue: { [key: string]: string } = {} - - if (storedValue) { - try { - parsedStoredValue = JSON.parse(storedValue) ?? {} - } catch (error) { - console.error(error) - } - } - - if (key === 'APP_CACHE') { - return { - timestamp: parsedStoredValue.timestamp ?? '', - walletName: getWalletName(parsedStoredValue.walletName, parsedStoredValue.timestamp), - } - } -} - -function getWalletName(walletName: string | undefined, timestamp: string | undefined) { - const isStaled = walletName && timestamp && dayjs().diff(+timestamp, 'days') > 5 - return isStaled || !walletName ? '' : walletName -} - -export function setStorageValue(key: Key, updatedValue: T) { - const storedValue = getStorageValue(key) - const mergedStoredValue = merge(storedValue, updatedValue) - window.localStorage.setItem(APP_STORAGE[key], JSON.stringify(mergedStoredValue)) -} diff --git a/apps/main/src/dex/components/ConnectWallet.tsx b/apps/main/src/dex/components/ConnectWallet.tsx deleted file mode 100644 index 34315ac08..000000000 --- a/apps/main/src/dex/components/ConnectWallet.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { isLoading } from '@ui/utils' -import useStore from '@/dex/store/useStore' - -import ConnectWalletPrompt from '@ui/ConnectWalletPrompt' -import { useUserProfileStore } from '@ui-kit/features/user-profile' - -type ConnectWalletProps = { - description: string - connectText: string - loadingText: string -} - -const ConnectWallet: React.FC = ({ description, connectText, loadingText }) => { - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - const connectState = useStore((state) => state.connectState) - - const theme = useUserProfileStore((state) => state.theme) - - const loading = isLoading(connectState) - - return ( - - ) -} - -export default ConnectWallet diff --git a/apps/main/src/dex/components/FormConnectWallet.tsx b/apps/main/src/dex/components/FormConnectWallet.tsx index 9026209cb..fb077951c 100644 --- a/apps/main/src/dex/components/FormConnectWallet.tsx +++ b/apps/main/src/dex/components/FormConnectWallet.tsx @@ -2,11 +2,11 @@ import { t } from '@lingui/macro' import React from 'react' import { isLoading } from '@ui/utils' -import useStore from '@/dex/store/useStore' import Button from '@ui/Button' import Spinner from '@ui/Spinner' import { useCurve } from '@/dex/entities/curve' +import useStore from '@/dex/store/useStore' const FormConnectWallet = ({ loading, @@ -16,12 +16,11 @@ const FormConnectWallet = ({ }>) => { const { data: curve } = useCurve() const connectState = useStore((state) => state.connectState) - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - + const connectWallet = useStore((s) => s.updateConnectState) return ( <> {!isLoading(connectState) && !loading && !curve?.signerAddress ? ( - ) : isLoading(connectState) || loading ? ( diff --git a/apps/main/src/dex/components/PageCompensation/Page.tsx b/apps/main/src/dex/components/PageCompensation/Page.tsx index 9c0af9a21..826f2338b 100644 --- a/apps/main/src/dex/components/PageCompensation/Page.tsx +++ b/apps/main/src/dex/components/PageCompensation/Page.tsx @@ -1,16 +1,12 @@ import type { NextPage } from 'next' import type { EtherContract } from '@/dex/components/PageCompensation/types' - import { Contract, Interface } from 'ethers' import { t } from '@lingui/macro' import { useLocation, useNavigate, useParams } from 'react-router-dom' import React, { useCallback, useEffect, useState } from 'react' import styled from 'styled-components' - import { scrollToTop } from '@/dex/utils' import usePageOnMount from '@/dex/hooks/usePageOnMount' -import useStore from '@/dex/store/useStore' - import Box, { BoxHeader } from '@ui/Box' import Button from '@ui/Button' import DocumentHead from '@/dex/layout/default/DocumentHead' @@ -20,6 +16,8 @@ import IconButton from '@ui/IconButton' import Settings from '@/dex/layout/default/Settings' import Spinner, { SpinnerWrapper } from '@ui/Spinner' import { Provider } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import useStore from '@/dex/store/useStore' const Page: NextPage = () => { const params = useParams() @@ -27,11 +25,8 @@ const Page: NextPage = () => { const navigate = useNavigate() const { pageLoaded, routerParams, curve } = usePageOnMount(params, location, navigate) const { rChainId } = routerParams - - const getProvider = useStore((state) => state.wallet.getProvider) - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - - const [provider, setProvider] = useState() + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) const [contracts, setContracts] = useState([]) const fetchData = useCallback(async (provider: Provider) => { @@ -54,14 +49,10 @@ const Page: NextPage = () => { // get initial data useEffect(() => { if (!pageLoaded) return - - const provider = getProvider('') - if (provider) { - setProvider(provider) fetchData(provider) } - }, [fetchData, getProvider, pageLoaded]) + }, [fetchData, pageLoaded, provider]) return ( <> @@ -81,13 +72,7 @@ const Page: NextPage = () => { ) : !provider ? ( <> Please connect your wallet to view compensation - diff --git a/apps/main/src/dex/components/PageCompensation/components/Compensation.tsx b/apps/main/src/dex/components/PageCompensation/components/Compensation.tsx index 996ca834e..1d9e3ffd5 100644 --- a/apps/main/src/dex/components/PageCompensation/components/Compensation.tsx +++ b/apps/main/src/dex/components/PageCompensation/components/Compensation.tsx @@ -15,6 +15,7 @@ import ExternalLink from '@ui/Link/ExternalLink' import Icon from '@ui/Icon' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, ChainId, Provider } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const Compensation = ({ rChainId, @@ -39,7 +40,7 @@ const Compensation = ({ token: string vestedTotal: number }) => { - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchGasInfo = useStore((state) => state.gas.fetchGasInfo) const networks = useStore((state) => state.networks.networks) diff --git a/apps/main/src/dex/components/PageCreatePool/ConfirmModal/CreatePoolButton.tsx b/apps/main/src/dex/components/PageCreatePool/ConfirmModal/CreatePoolButton.tsx index 7cd3c50eb..5f4721c95 100644 --- a/apps/main/src/dex/components/PageCreatePool/ConfirmModal/CreatePoolButton.tsx +++ b/apps/main/src/dex/components/PageCreatePool/ConfirmModal/CreatePoolButton.tsx @@ -18,17 +18,12 @@ interface Props { const CreatePoolButton = ({ disabled, curve }: Props) => { const networks = useStore((state) => state.networks.networks) const { haveSigner } = curveProps(curve, networks) - const deployPool = useStore((state) => state.createPool.deployPool) const { txStatus, txSuccess, txLink, poolId, errorMessage } = useStore((state) => state.createPool.transactionState) - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - - const handleClick = async () => { - deployPool(curve) - } + const connectWallet = useStore((s) => s.updateConnectState) return !haveSigner ? ( - + connectWallet()}> {t`Connect Wallet`} ) : ( @@ -40,7 +35,7 @@ const CreatePoolButton = ({ disabled, curve }: Props) => { )} {(txStatus === '' || txStatus === 'ERROR') && ( - handleClick()}> + deployPool(curve)}> {t`Create Pool`} )} diff --git a/apps/main/src/dex/components/PageCreatePool/Page.tsx b/apps/main/src/dex/components/PageCreatePool/Page.tsx index 80e142dca..eb5a5c7b4 100644 --- a/apps/main/src/dex/components/PageCreatePool/Page.tsx +++ b/apps/main/src/dex/components/PageCreatePool/Page.tsx @@ -1,27 +1,28 @@ import type { NextPage } from 'next' - import { t } from '@lingui/macro' import { useEffect } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' import styled from 'styled-components' - import { breakpoints } from '@ui/utils/responsive' import { scrollToTop } from '@/dex/utils' import usePageOnMount from '@/dex/hooks/usePageOnMount' -import useStore from '@/dex/store/useStore' - import DocumentHead from '@/dex/layout/default/DocumentHead' import PoolCreation from '@/dex/components/PageCreatePool/index' import Box from '@ui/Box' -import ConnectWallet from '@/dex/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import useStore from '@/dex/store/useStore' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() const location = useLocation() const navigate = useNavigate() const { curve } = usePageOnMount(params, location, navigate) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) useEffect(() => { scrollToTop() @@ -33,10 +34,12 @@ const Page: NextPage = () => { {!provider ? ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/dex/components/PageCrvLocker/components/FormActions.tsx b/apps/main/src/dex/components/PageCrvLocker/components/FormActions.tsx index 88083cc23..f81d1d098 100644 --- a/apps/main/src/dex/components/PageCrvLocker/components/FormActions.tsx +++ b/apps/main/src/dex/components/PageCrvLocker/components/FormActions.tsx @@ -1,25 +1,23 @@ -import React from 'react' +import { ReactNode } from 'react' import { t } from '@lingui/macro' - -import useStore from '@/dex/store/useStore' - import Button from '@ui/Button' import Spinner from '@ui/Spinner' +import useStore from '@/dex/store/useStore' const FormActions = ({ haveSigner, loading, children, -}: React.PropsWithChildren<{ +}: { haveSigner: boolean loading: boolean -}>) => { - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) - + children?: ReactNode | undefined +}) => { + const connectWallet = useStore((s) => s.updateConnectState) return ( <> {!haveSigner && !loading ? ( - ) : loading ? ( diff --git a/apps/main/src/dex/components/PageCrvLocker/components/FormLockCreate.tsx b/apps/main/src/dex/components/PageCrvLocker/components/FormLockCreate.tsx index 2be2a69e1..28003ea34 100644 --- a/apps/main/src/dex/components/PageCrvLocker/components/FormLockCreate.tsx +++ b/apps/main/src/dex/components/PageCrvLocker/components/FormLockCreate.tsx @@ -21,6 +21,7 @@ import FieldLockedAmt from '@/dex/components/PageCrvLocker/components/FieldLocke import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockCreate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -33,7 +34,7 @@ const FormLockCreate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => const formValues = useStore((state) => state.lockedCrv.formValues) const fetchStepApprove = useStore((state) => state.lockedCrv.fetchStepApprove) const fetchStepCreate = useStore((state) => state.lockedCrv.fetchStepCreate) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) const network = useStore((state) => state.networks.networks[rChainId]) diff --git a/apps/main/src/dex/components/PageCrvLocker/components/FormLockCrv.tsx b/apps/main/src/dex/components/PageCrvLocker/components/FormLockCrv.tsx index c38881721..0fa59aed8 100644 --- a/apps/main/src/dex/components/PageCrvLocker/components/FormLockCrv.tsx +++ b/apps/main/src/dex/components/PageCrvLocker/components/FormLockCrv.tsx @@ -15,6 +15,7 @@ import FieldLockedAmt from '@/dex/components/PageCrvLocker/components/FieldLocke import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockCrv = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -25,7 +26,7 @@ const FormLockCrv = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const formEstGas = useStore((state) => state.lockedCrv.formEstGas[activeKey] ?? DEFAULT_FORM_EST_GAS) const formStatus = useStore((state) => state.lockedCrv.formStatus) const formValues = useStore((state) => state.lockedCrv.formValues) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepApprove = useStore((state) => state.lockedCrv.fetchStepApprove) const fetchStepIncreaseCrv = useStore((state) => state.lockedCrv.fetchStepIncreaseCrv) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) diff --git a/apps/main/src/dex/components/PageCrvLocker/components/FormLockDate.tsx b/apps/main/src/dex/components/PageCrvLocker/components/FormLockDate.tsx index 2f27ebabd..9709b0c95 100644 --- a/apps/main/src/dex/components/PageCrvLocker/components/FormLockDate.tsx +++ b/apps/main/src/dex/components/PageCrvLocker/components/FormLockDate.tsx @@ -20,6 +20,7 @@ import FieldDatePicker from '@/dex/components/PageCrvLocker/components/FieldDate import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormLockDate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const isSubscribed = useRef(false) @@ -30,7 +31,7 @@ const FormLockDate = ({ curve, rChainId, rFormType, vecrvInfo }: PageVecrv) => { const formEstGas = useStore((state) => state.lockedCrv.formEstGas[activeKey] ?? DEFAULT_FORM_EST_GAS) const formStatus = useStore((state) => state.lockedCrv.formStatus) const formValues = useStore((state) => state.lockedCrv.formValues) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepIncreaseTime = useStore((state) => state.lockedCrv.fetchStepIncreaseTime) const setFormValues = useStore((state) => state.lockedCrv.setFormValues) const network = useStore((state) => state.networks.networks[rChainId]) diff --git a/apps/main/src/dex/components/PageDashboard/Page.tsx b/apps/main/src/dex/components/PageDashboard/Page.tsx index 9941e6c8b..7dd2c38f3 100644 --- a/apps/main/src/dex/components/PageDashboard/Page.tsx +++ b/apps/main/src/dex/components/PageDashboard/Page.tsx @@ -8,14 +8,16 @@ import styled from 'styled-components' import { breakpoints } from '@ui/utils/responsive' import { scrollToTop } from '@/dex/utils' import usePageOnMount from '@/dex/hooks/usePageOnMount' -import useStore from '@/dex/store/useStore' import Dashboard from '@/dex/components/PageDashboard/index' import DocumentHead from '@/dex/layout/default/DocumentHead' import Settings from '@/dex/layout/default/Settings' import Spinner, { SpinnerWrapper } from '@ui/Spinner' -import ConnectWallet from '@/dex/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import Box from '@ui/Box' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import useStore from '@/dex/store/useStore' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -23,7 +25,9 @@ const Page: NextPage = () => { const navigate = useNavigate() const { curve, routerParams } = usePageOnMount(params, location, navigate) const { rChainId } = routerParams - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) useEffect(() => { scrollToTop() @@ -35,10 +39,12 @@ const Page: NextPage = () => { {!provider ? ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/dex/components/PageDashboard/components/FormClaimFeesButtons.tsx b/apps/main/src/dex/components/PageDashboard/components/FormClaimFeesButtons.tsx index ff3cbf31a..c2ca9b05d 100644 --- a/apps/main/src/dex/components/PageDashboard/components/FormClaimFeesButtons.tsx +++ b/apps/main/src/dex/components/PageDashboard/components/FormClaimFeesButtons.tsx @@ -9,6 +9,7 @@ import useStore from '@/dex/store/useStore' import Button from '@ui/Button' import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormClaimFeesButtons = ({ activeKey, @@ -29,7 +30,7 @@ const FormClaimFeesButtons = ({ const claimFeesAmounts = useStore((state) => state.dashboard.claimableFees[activeKey]) const formProcessing = useStore((state) => state.dashboard.formStatus.formProcessing) const fetchStepClaimFees = useStore((state) => state.dashboard.fetchStepClaimFees) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormStatus = useStore((state) => state.dashboard.setFormStatusClaimFees) const { chainId, signerAddress } = curve || {} diff --git a/apps/main/src/dex/components/PageDashboard/components/FormVecrv.tsx b/apps/main/src/dex/components/PageDashboard/components/FormVecrv.tsx index bfc163bb3..dff38a10c 100644 --- a/apps/main/src/dex/components/PageDashboard/components/FormVecrv.tsx +++ b/apps/main/src/dex/components/PageDashboard/components/FormVecrv.tsx @@ -23,6 +23,7 @@ import Button from '@ui/Button' import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' // TODO uncomment locker link code once it is ready const FormVecrv = () => { @@ -40,7 +41,7 @@ const FormVecrv = () => { const dashboardVecrvInfo = useStore((state) => state.dashboard.vecrvInfo[activeKey]) const formStatus = useStore((state) => state.dashboard.formStatus) const setFormStatusVecrv = useStore((state) => state.dashboard.setFormStatusVecrv) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepWithdraw = useStore((state) => state.dashboard.fetchStepWithdrawVecrv) const network = useStore((state) => curve && state.networks.networks[curve.chainId]) diff --git a/apps/main/src/dex/components/PageDeployGauge/components/DeployGaugeButton.tsx b/apps/main/src/dex/components/PageDeployGauge/components/DeployGaugeButton.tsx index 03a169901..b4cc566c8 100644 --- a/apps/main/src/dex/components/PageDeployGauge/components/DeployGaugeButton.tsx +++ b/apps/main/src/dex/components/PageDeployGauge/components/DeployGaugeButton.tsx @@ -7,17 +7,17 @@ import { curveProps } from '@/dex/lib/utils' import { useNetworkFromUrl } from '@/dex/utils/utilsRouter' import { shortenTokenAddress } from '@/dex/utils' import { - TWOCOINCRYPTOSWAP, - TWOCOINCRYPTOSWAPNG, - THREECOINCRYPTOSWAP, STABLESWAP, STABLESWAPOLD, + THREECOINCRYPTOSWAP, + TWOCOINCRYPTOSWAP, + TWOCOINCRYPTOSWAPNG, } from '@/dex/components/PageDeployGauge/constants' import Button from '@ui/Button' import Spinner, { SpinnerWrapper } from '@ui/Spinner' import AlertBox from '@ui/AlertBox' import InfoLinkBar from '@/dex/components/PageCreatePool/ConfirmModal/CreateInfoLinkBar' -import { CurveApi, ChainId } from '@/dex/types/main.types' +import { ChainId, CurveApi } from '@/dex/types/main.types' interface Props { disabled: boolean @@ -35,7 +35,7 @@ const DeployGaugeButton = ({ disabled, chainId, curve }: Props) => { const { lpTokenAddress, currentPoolType, sidechainGauge, sidechainNav, deploymentStatus, deployGauge } = useStore( (state) => state.deployGauge, ) - const updateConnectWalletStateKeys = useStore((state) => state.wallet.updateConnectWalletStateKeys) + const connectWallet = useStore((s) => s.updateConnectState) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) const updateConnectState = useStore((state) => state.updateConnectState) const isLoadingApi = useStore((state) => state.isLoadingApi) @@ -88,7 +88,7 @@ const DeployGaugeButton = ({ disabled, chainId, curve }: Props) => { ) // no signer ) : !haveSigner ? ( - + connectWallet()}> {t`Connect Wallet`} ) : ( @@ -169,7 +169,7 @@ const DeployGaugeButton = ({ disabled, chainId, curve }: Props) => { ) ) : !haveSigner ? ( - + connectWallet()}> {t`Connect Wallet`} ) : ( diff --git a/apps/main/src/dex/components/PageIntegrations/index.tsx b/apps/main/src/dex/components/PageIntegrations/index.tsx index 3547f1400..bb6c7268b 100644 --- a/apps/main/src/dex/components/PageIntegrations/index.tsx +++ b/apps/main/src/dex/components/PageIntegrations/index.tsx @@ -1,19 +1,16 @@ import type { FormValues } from '@/dex/components/PageIntegrations/types' import type { IntegrationsTags } from '@ui/Integration/types' import type { NavigateFunction, Params } from 'react-router' - import { useFocusRing } from '@react-aria/focus' import { Trans } from '@lingui/macro' import Image from 'next/image' import styled from 'styled-components' import React, { useCallback, useEffect, useMemo } from 'react' - import { ROUTE } from '@/dex/constants' import { breakpoints } from '@ui/utils' import { getPath } from '@/dex/utils/utilsRouter' import { parseSearchParams } from '@/dex/components/PageIntegrations/utils' import useStore from '@/dex/store/useStore' - import Box from '@ui/Box' import IntegrationAppComp from '@ui/Integration/IntegrationApp' import SearchInput from '@ui/SearchInput' diff --git a/apps/main/src/dex/components/PagePool/Deposit/components/FormDeposit.tsx b/apps/main/src/dex/components/PagePool/Deposit/components/FormDeposit.tsx index 4039d942d..f21e936c6 100644 --- a/apps/main/src/dex/components/PagePool/Deposit/components/FormDeposit.tsx +++ b/apps/main/src/dex/components/PagePool/Deposit/components/FormDeposit.tsx @@ -21,6 +21,7 @@ import Stepper from '@ui/Stepper' import TransferActions from '@/dex/components/PagePool/components/TransferActions' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormDeposit = ({ chainIdPoolId, @@ -49,7 +50,7 @@ const FormDeposit = ({ const slippage = useStore((state) => state.poolDeposit.slippage[activeKey] ?? DEFAULT_SLIPPAGE) const fetchStepApprove = useStore((state) => state.poolDeposit.fetchStepApprove) const fetchStepDeposit = useStore((state) => state.poolDeposit.fetchStepDeposit) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolDeposit.setFormValues) const resetState = useStore((state) => state.poolDeposit.resetState) const network = useStore((state) => (chainId ? state.networks.networks[chainId] : null)) diff --git a/apps/main/src/dex/components/PagePool/Deposit/components/FormDepositStake.tsx b/apps/main/src/dex/components/PagePool/Deposit/components/FormDepositStake.tsx index 73c0ba88b..3823dec05 100644 --- a/apps/main/src/dex/components/PagePool/Deposit/components/FormDepositStake.tsx +++ b/apps/main/src/dex/components/PagePool/Deposit/components/FormDepositStake.tsx @@ -23,6 +23,7 @@ import Stepper from '@ui/Stepper' import TransferActions from '@/dex/components/PagePool/components/TransferActions' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, Pool, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormDepositStake = ({ chainIdPoolId, @@ -52,7 +53,7 @@ const FormDepositStake = ({ const slippage = useStore((state) => state.poolDeposit.slippage[activeKey] ?? DEFAULT_SLIPPAGE) const fetchStepApprove = useStore((state) => state.poolDeposit.fetchStepApprove) const fetchStepDepositStake = useStore((state) => state.poolDeposit.fetchStepDepositStake) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolDeposit.setFormValues) const resetState = useStore((state) => state.poolDeposit.resetState) const network = useStore((state) => state.networks.networks[rChainId]) diff --git a/apps/main/src/dex/components/PagePool/Deposit/components/FormStake.tsx b/apps/main/src/dex/components/PagePool/Deposit/components/FormStake.tsx index a07da8fdf..677fa1879 100644 --- a/apps/main/src/dex/components/PagePool/Deposit/components/FormStake.tsx +++ b/apps/main/src/dex/components/PagePool/Deposit/components/FormStake.tsx @@ -19,6 +19,7 @@ import Stepper from '@ui/Stepper' import TransferActions from '@/dex/components/PagePool/components/TransferActions' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, Pool, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormStake = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, userPoolBalances }: TransferProps) => { const isSubscribed = useRef(false) @@ -33,7 +34,7 @@ const FormStake = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, us const rewardsApy = useStore((state) => state.pools.rewardsApyMapper[rChainId]?.[poolDataCacheOrApi.pool.id]) const fetchStepApprove = useStore((state) => state.poolDeposit.fetchStepStakeApprove) const fetchStepStake = useStore((state) => state.poolDeposit.fetchStepStake) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolDeposit.setFormValues) const resetState = useStore((state) => state.poolDeposit.resetState) const network = useStore((state) => (chainId ? state.networks.networks[chainId] : null)) diff --git a/apps/main/src/dex/components/PagePool/Page.tsx b/apps/main/src/dex/components/PagePool/Page.tsx index 4dc8d4628..e4d2f3cdd 100644 --- a/apps/main/src/dex/components/PagePool/Page.tsx +++ b/apps/main/src/dex/components/PagePool/Page.tsx @@ -10,8 +10,9 @@ import useStore from '@/dex/store/useStore' import { scrollToTop } from '@/dex/utils' import DocumentHead from '@/dex/layout/default/DocumentHead' import Transfer from '@/dex/components/PagePool/index' -import ConnectWallet from '@/dex/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import Box from '@ui/Box' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -27,8 +28,10 @@ const Page: NextPage = () => { const fetchNewPool = useStore((state) => state.pools.fetchNewPool) const poolDataCache = useStore((state) => state.storeCache.poolsMapper[rChainId]?.[parsedRPoolId]) const poolData = useStore((state) => state.pools.poolsMapper[rChainId]?.[parsedRPoolId]) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const network = useStore((state) => state.networks.networks[rChainId]) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) const { hasDepositAndStake } = getNetworkConfigFromApi(rChainId) @@ -62,10 +65,12 @@ const Page: NextPage = () => { {!provider ? ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/dex/components/PagePool/Swap/index.tsx b/apps/main/src/dex/components/PagePool/Swap/index.tsx index 68f550de2..413a12b6d 100644 --- a/apps/main/src/dex/components/PagePool/Swap/index.tsx +++ b/apps/main/src/dex/components/PagePool/Swap/index.tsx @@ -35,6 +35,7 @@ import TransferActions from '@/dex/components/PagePool/components/TransferAction import TxInfoBar from '@ui/TxInfoBar' import WarningModal from '@/dex/components/PagePool/components/WarningModal' import { Balances, CurveApi, TokensMapper, PoolData, PoolAlert } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const Swap = ({ chainIdPoolId, @@ -76,7 +77,7 @@ const Swap = ({ const fetchUsdRateByTokens = useStore((state) => state.usdRates.fetchUsdRateByTokens) const fetchStepApprove = useStore((state) => state.poolSwap.fetchStepApprove) const fetchStepSwap = useStore((state) => state.poolSwap.fetchStepSwap) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const resetState = useStore((state) => state.poolSwap.resetState) const setFormValues = useStore((state) => state.poolSwap.setFormValues) const setPoolIsWrapped = useStore((state) => state.pools.setPoolIsWrapped) diff --git a/apps/main/src/dex/components/PagePool/Withdraw/components/FormClaim.tsx b/apps/main/src/dex/components/PagePool/Withdraw/components/FormClaim.tsx index f9fb34f38..30465bd59 100644 --- a/apps/main/src/dex/components/PagePool/Withdraw/components/FormClaim.tsx +++ b/apps/main/src/dex/components/PagePool/Withdraw/components/FormClaim.tsx @@ -18,6 +18,7 @@ import Stepper from '@ui/Stepper' import TransferActions from '@/dex/components/PagePool/components/TransferActions' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormClaim = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, userPoolBalances }: TransferProps) => { const isSubscribed = useRef(false) @@ -29,7 +30,7 @@ const FormClaim = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, us const fetchClaimable = useStore((state) => state.poolWithdraw.fetchClaimable) const fetchStepClaim = useStore((state) => state.poolWithdraw.fetchStepClaim) const setStateByKey = useStore((state) => state.poolWithdraw.setStateByKey) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolWithdraw.setFormValues) const resetState = useStore((state) => state.poolWithdraw.resetState) const network = useStore((state) => (chainId ? state.networks.networks[chainId] : null)) diff --git a/apps/main/src/dex/components/PagePool/Withdraw/components/FormUnstake.tsx b/apps/main/src/dex/components/PagePool/Withdraw/components/FormUnstake.tsx index e45bdabe2..a2cdb2f2e 100644 --- a/apps/main/src/dex/components/PagePool/Withdraw/components/FormUnstake.tsx +++ b/apps/main/src/dex/components/PagePool/Withdraw/components/FormUnstake.tsx @@ -14,6 +14,7 @@ import TransferActions from '@/dex/components/PagePool/components/TransferAction import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { CurveApi, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormUnstake = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, userPoolBalances }: TransferProps) => { const isSubscribed = useRef(false) @@ -25,7 +26,7 @@ const FormUnstake = ({ curve, poolData, poolDataCacheOrApi, routerParams, seed, const formStatus = useStore((state) => state.poolWithdraw.formStatus) const formValues = useStore((state) => state.poolWithdraw.formValues) const fetchStepUnstake = useStore((state) => state.poolWithdraw.fetchStepUnstake) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolWithdraw.setFormValues) const resetState = useStore((state) => state.poolWithdraw.resetState) const network = useStore((state) => (chainId ? state.networks.networks[chainId] : null)) diff --git a/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx b/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx index ab4a775e7..eb510a7fb 100644 --- a/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx +++ b/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx @@ -32,6 +32,7 @@ import TransferActions from '@/dex/components/PagePool/components/TransferAction import TxInfoBar from '@ui/TxInfoBar' import WarningModal from '@/dex/components/PagePool/components/WarningModal' import { CurveApi, Pool, PoolData } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const FormWithdraw = ({ chainIdPoolId, @@ -57,7 +58,7 @@ const FormWithdraw = ({ const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) const fetchStepApprove = useStore((state) => state.poolWithdraw.fetchStepApprove) const fetchStepWithdraw = useStore((state) => state.poolWithdraw.fetchStepWithdraw) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.poolWithdraw.setFormValues) const setPoolIsWrapped = useStore((state) => state.pools.setPoolIsWrapped) const resetState = useStore((state) => state.poolWithdraw.resetState) diff --git a/apps/main/src/dex/components/PagePoolList/index.tsx b/apps/main/src/dex/components/PagePoolList/index.tsx index 7f96d670c..3e784e8ea 100644 --- a/apps/main/src/dex/components/PagePoolList/index.tsx +++ b/apps/main/src/dex/components/PagePoolList/index.tsx @@ -1,9 +1,6 @@ import type { ColumnKeys, PagePoolList, SearchParams } from '@/dex/components/PagePoolList/types' - -import { t } from '@lingui/macro' import React, { useCallback, useEffect, useMemo, useState } from 'react' import styled from 'styled-components' - import { COLUMN_KEYS } from '@/dex/components/PagePoolList/utils' import { DEFAULT_FORM_STATUS, getPoolListActiveKey } from '@/dex/store/createPoolListSlice' import { REFRESH_INTERVAL } from '@/dex/constants' @@ -11,7 +8,6 @@ import usePageVisibleInterval from '@/dex/hooks/usePageVisibleInterval' import useStore from '@/dex/store/useStore' import { getUserActiveKey } from '@/dex/store/createUserSlice' import useCampaignRewardsMapper from '@/dex/hooks/useCampaignRewardsMapper' - import Spinner, { SpinnerWrapper } from '@ui/Spinner' import Table, { Tbody } from '@ui/Table' import TableHead from '@/dex/components/PagePoolList/components/TableHead' @@ -19,7 +15,6 @@ import TableHeadMobile from '@/dex/components/PagePoolList/components/TableHeadM import TableSettings from '@/dex/components/PagePoolList/components/TableSettings/TableSettings' import TableRowNoResult from '@/dex/components/PagePoolList/components/TableRowNoResult' import { PoolRow } from '@/dex/components/PagePoolList/components/PoolRow' -import ConnectWallet from '@/dex/components/ConnectWallet' const PoolList = ({ rChainId, diff --git a/apps/main/src/dex/components/PageRouterSwap/Page.tsx b/apps/main/src/dex/components/PageRouterSwap/Page.tsx index b0c054f62..4d8af51ef 100644 --- a/apps/main/src/dex/components/PageRouterSwap/Page.tsx +++ b/apps/main/src/dex/components/PageRouterSwap/Page.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react' import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom' import styled from 'styled-components' import { ROUTE } from '@/dex/constants' -import { breakpoints } from '@ui/utils' +import { breakpoints, isLoading } from '@ui/utils' import { getPath } from '@/dex/utils/utilsRouter' import { scrollToTop } from '@/dex/utils' import usePageOnMount from '@/dex/hooks/usePageOnMount' @@ -15,7 +15,7 @@ import Box, { BoxHeader } from '@ui/Box' import DocumentHead from '@/dex/layout/default/DocumentHead' import IconButton from '@ui/IconButton' import QuickSwap from '@/dex/components/PageRouterSwap/index' -import ConnectWallet from '@/dex/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import { useUserProfileStore } from '@ui-kit/features/user-profile' const Page: NextPage = () => { @@ -29,9 +29,11 @@ const Page: NextPage = () => { const getNetworkConfigFromApi = useStore((state) => state.getNetworkConfigFromApi) const isLoadingCurve = useStore((state) => state.isLoadingCurve) const routerCached = useStore((state) => state.storeCache.routerFormValues[rChainId]) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const nativeToken = useStore((state) => state.networks.nativeToken[rChainId]) const network = useStore((state) => state.networks.networks[rChainId]) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) const { tokensMapper, tokensMapperStr } = useTokensMapper(rChainId) const maxSlippage = useUserProfileStore((state) => state.maxSlippage.global) @@ -117,7 +119,13 @@ const Page: NextPage = () => { {!provider ? ( - + connectWallet()} + isLoading={isLoading(connectState)} + /> ) : ( diff --git a/apps/main/src/dex/components/PageRouterSwap/index.tsx b/apps/main/src/dex/components/PageRouterSwap/index.tsx index 499d74710..05655cefa 100644 --- a/apps/main/src/dex/components/PageRouterSwap/index.tsx +++ b/apps/main/src/dex/components/PageRouterSwap/index.tsx @@ -35,6 +35,7 @@ import TxInfoBar from '@ui/TxInfoBar' import WarningModal from '@/dex/components/PagePool/components/WarningModal' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { CurveApi, ChainId, Token, TokensMapper } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const QuickSwap = ({ pageLoaded, @@ -66,7 +67,7 @@ const QuickSwap = ({ const formValues = useStore((state) => state.quickSwap.formValues) const isLoadingApi = useStore((state) => state.isLoadingApi) const isPageVisible = useStore((state) => state.isPageVisible) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const routesAndOutput = useStore((state) => state.quickSwap.routesAndOutput[activeKey]) const isHideSmallPools = useStore((state) => state.poolList.formValues.hideSmallPools) const isMaxLoading = useStore((state) => state.quickSwap.isMaxLoading) diff --git a/apps/main/src/dex/constants.ts b/apps/main/src/dex/constants.ts index eecb2f5d0..42db5cbf5 100644 --- a/apps/main/src/dex/constants.ts +++ b/apps/main/src/dex/constants.ts @@ -1,4 +1,5 @@ import { DEX_ROUTES } from '@ui-kit/shared/routes' +export { CONNECT_STAGE } from '@ui/utils/utilsConnectState' export const NETWORK_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' export const INVALID_ADDRESS = '0x0000000000000000000000000000000000000000' @@ -44,13 +45,6 @@ export const TIME_FRAMES = { MONTH: 30 * 24 * 60 * 60, } as const -export const CONNECT_STAGE = { - CONNECT_API: 'api', - CONNECT_WALLET: 'connect-wallet', - DISCONNECT_WALLET: 'disconnect-wallet', - SWITCH_NETWORK: 'switch-network', -} as const - export const DEFAULT_NETWORK_CONFIG = { useApi: true, // default to true when calling fetchPools excludeTokensBalancesMapper: {}, // tokens that cause issues when getting wallet balances diff --git a/apps/main/src/dex/entities/gauge/lib/reward-actions.ts b/apps/main/src/dex/entities/gauge/lib/reward-actions.ts index ec6d00f8b..51ce36535 100644 --- a/apps/main/src/dex/entities/gauge/lib/reward-actions.ts +++ b/apps/main/src/dex/entities/gauge/lib/reward-actions.ts @@ -24,13 +24,13 @@ import type { import { queryClient } from '@ui-kit/lib/api/query-client' import { GaugeParams } from '@ui-kit/lib/model/query' import useTokensMapper from '@/dex/hooks/useTokensMapper' -import useStore from '@/dex/store/useStore' +import { useWalletStore } from '@ui-kit/features/connect-wallet' export const useAddRewardToken = ({ chainId, poolId, }: GaugeParams): UseMutationResult => { - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const { tokensMapper } = useTokensMapper(chainId) return useMutation({ @@ -71,7 +71,7 @@ export const useDepositRewardApprove = ({ chainId, poolId, }: GaugeParams): UseMutationResult => { - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const { tokensMapper } = useTokensMapper(chainId) return useMutation({ @@ -109,7 +109,7 @@ export const useDepositReward = ({ chainId, poolId, }: GaugeParams): UseMutationResult => { - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const { tokensMapper } = useTokensMapper(chainId) return useMutation({ diff --git a/apps/main/src/dex/entities/signer/lib.ts b/apps/main/src/dex/entities/signer/lib.ts index 786b66c58..3d28913ee 100644 --- a/apps/main/src/dex/entities/signer/lib.ts +++ b/apps/main/src/dex/entities/signer/lib.ts @@ -1,10 +1,11 @@ import { useMemo } from 'react' import type { Address } from 'viem' import useStore from '@/dex/store/useStore' +import { useWalletStore } from '@ui-kit/features/connect-wallet' export const useSignerAddress = (): { data: Address | undefined } => { - const onboardInstance = useStore((state) => state.wallet.onboard) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address + const { wallet } = useWalletStore.getState() + const signerAddress = wallet?.accounts?.[0]?.address return { data: signerAddress } } diff --git a/apps/main/src/dex/hooks/usePageOnMount.ts b/apps/main/src/dex/hooks/usePageOnMount.ts index dbefbe90c..0cd00abea 100644 --- a/apps/main/src/dex/hooks/usePageOnMount.ts +++ b/apps/main/src/dex/hooks/usePageOnMount.ts @@ -9,21 +9,22 @@ import { getWalletSignerAddress, useConnectWallet, useSetChain, useSetLocale } f import { CONNECT_STAGE, REFRESH_INTERVAL, ROUTE } from '@/dex/constants' import { dynamicActivate, updateAppLocale } from '@ui-kit/lib/i18n' -import { getStorageValue, setStorageValue } from '@/dex/utils/storage' import { useNetworkFromUrl, useParsedParams } from '@/dex/utils/utilsRouter' -import { getWalletChainId } from '@/dex/store/createWalletSlice' +import { getWalletChainId } from '@ui-kit/features/connect-wallet' import { initCurveJs } from '@/dex/utils/utilsCurvejs' import useStore from '@/dex/store/useStore' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { ChainId, PageProps, Wallet } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' function usePageOnMount(params: Params, location: Location, navigate: NavigateFunction, chainIdNotRequired?: boolean) { - const [{ wallet }, connect, disconnect] = useConnectWallet() + const { wallet, connect, disconnect, walletName, setWalletName } = useConnectWallet() const [_, setChain] = useSetChain() const updateWalletLocale = useSetLocale() const curve = useStore((state) => state.curve) const connectState = useStore((state) => state.connectState) + const chooseWallet = useWalletStore((s) => s.chooseWallet) const setNetworkConfigs = useStore((state) => state.networks.setNetworkConfigs) const updateConnectState = useStore((state) => state.updateConnectState) const updateCurveJs = useStore((state) => state.updateCurveJs) @@ -93,7 +94,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu walletState = walletStates[0] } catch (error) { // if failed to get walletState due to timeout, show connect modal. - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) ;[walletState] = await connect() } } else { @@ -102,7 +103,8 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu try { if (!walletState) throw new Error('unable to connect') - setStorageValue('APP_CACHE', { walletName: walletState.label, timestamp: Date.now().toString() }) + chooseWallet(walletState) + setWalletName(walletState.label) const walletChainId = getWalletChainId(walletState) if (walletChainId && walletChainId !== parsedParams.rChainId) { const success = await setChain({ chainId: ethers.toQuantity(parsedParams.rChainId) }) @@ -122,12 +124,13 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } catch (error) { updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) } } }, [ connect, + chooseWallet, navigate, networks, parsedParams.rChainId, @@ -135,6 +138,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu parsedParams.restFullPathname, setChain, updateConnectState, + setWalletName, ], ) @@ -142,13 +146,13 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu async (wallet: Wallet) => { try { await disconnect(wallet) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) } catch (error) { console.error(error) } }, - [disconnect, parsedParams.rChainId, updateConnectState], + [disconnect, parsedParams.rChainId, updateConnectState, setWalletName], ) const handleNetworkSwitch = useCallback( @@ -199,7 +203,6 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu navigate(`${parsedParams.rLocalePathname}/ethereum${ROUTE.PAGE_SWAP}`) } else { updateGlobalStoreByKey('routerProps', { params, location, navigate }) - const walletName = getStorageValue('APP_CACHE')?.walletName ?? '' if (walletName) { updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [walletName]) } else { diff --git a/apps/main/src/dex/hooks/usePoolTotalStaked.tsx b/apps/main/src/dex/hooks/usePoolTotalStaked.tsx index 1b6bbcfb0..306a3d450 100644 --- a/apps/main/src/dex/hooks/usePoolTotalStaked.tsx +++ b/apps/main/src/dex/hooks/usePoolTotalStaked.tsx @@ -3,13 +3,13 @@ import { useCallback, useEffect } from 'react' import { isValidAddress } from '@/dex/utils' import useStore from '@/dex/store/useStore' import dayjs from '@ui-kit/lib/dayjs' -import { Provider, PoolDataCacheOrApi } from '@/dex/types/main.types' +import { PoolDataCacheOrApi, Provider } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const usePoolTotalStaked = (poolDataCacheOrApi: PoolDataCacheOrApi) => { const { address, lpToken, gauge } = poolDataCacheOrApi?.pool ?? {} - const curve = useStore((state) => state.curve) - const getProvider = useStore((state) => state.wallet.getProvider) + const walletProvider = useWalletStore((s) => s.provider) const staked = useStore((state) => state.pools.stakedMapper[address]) const setStateByActiveKey = useStore((state) => state.pools.setStateByActiveKey) const { rpcUrl } = useStore((state) => curve && state.networks.networks[curve.chainId]) ?? {} @@ -59,7 +59,7 @@ const usePoolTotalStaked = (poolDataCacheOrApi: PoolDataCacheOrApi) => { if (address && rpcUrl && shouldCallApi) { ;(async () => { - const provider = getProvider('') || new JsonRpcProvider(rpcUrl) + const provider = walletProvider || new JsonRpcProvider(rpcUrl) const gaugeContract = isValidAddress(gauge.address) ? await getContract('gaugeTotalSupply', gauge.address, provider) : null @@ -77,7 +77,7 @@ const usePoolTotalStaked = (poolDataCacheOrApi: PoolDataCacheOrApi) => { })() } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [curve?.signerAddress, curve?.chainId, address, rpcUrl]) + }, [curve?.signerAddress, curve?.chainId, address, rpcUrl, walletProvider]) return staked } diff --git a/apps/main/src/dex/layout/default/Header.tsx b/apps/main/src/dex/layout/default/Header.tsx index abe17e314..a1504c00e 100644 --- a/apps/main/src/dex/layout/default/Header.tsx +++ b/apps/main/src/dex/layout/default/Header.tsx @@ -17,7 +17,7 @@ type HeaderProps = { sections: NavigationSection[]; BannerProps: GlobalBannerPro const QuickSwap = () => t`Quickswap` export const Header = ({ sections, BannerProps }: HeaderProps) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const mainNavRef = useRef(null) const navigate = useNavigate() useLayoutHeight(mainNavRef, 'mainNav') diff --git a/apps/main/src/dex/layout/default/index.tsx b/apps/main/src/dex/layout/default/index.tsx index ec8088d74..9a813bb6f 100644 --- a/apps/main/src/dex/layout/default/index.tsx +++ b/apps/main/src/dex/layout/default/index.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components' import { CONNECT_STAGE, ROUTE } from '@/dex/constants' import { useNetworkFromUrl } from '@/dex/utils/utilsRouter' -import { getWalletChainId } from '@/dex/store/createWalletSlice' +import { getWalletChainId } from '@ui-kit/features/connect-wallet' import { isFailure, isLoading } from '@ui/utils' import { useConnectWallet } from '@ui-kit/features/connect-wallet' import useLayoutHeight from '@/dex/hooks/useLayoutHeight' @@ -17,7 +17,7 @@ import { layoutHeightKeys } from '@/dex/store/createGlobalSlice' import { useUserProfileStore } from '@ui-kit/features/user-profile' const BaseLayout = ({ children }: { children: React.ReactNode }) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const globalAlertRef = useRef(null) useLayoutHeight(globalAlertRef, 'globalAlert') diff --git a/apps/main/src/dex/store/createCreatePoolSlice.ts b/apps/main/src/dex/store/createCreatePoolSlice.ts index 1883461bb..5fb535203 100644 --- a/apps/main/src/dex/store/createCreatePoolSlice.ts +++ b/apps/main/src/dex/store/createCreatePoolSlice.ts @@ -30,6 +30,7 @@ import { } from '@/dex/components/PageCreatePool/constants' import { isTricrypto } from '@/dex/components/PageCreatePool/utils' import { CurveApi, ChainId } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type SliceState = { navigationIndex: number @@ -767,7 +768,6 @@ const createCreatePoolSlice = (set: SetState, get: GetState) => ({ const { pools: { fetchNewPool, basePools }, gas: { fetchGasInfo }, - wallet: { notifyNotification }, createPool: { poolSymbol, swapType, @@ -793,6 +793,7 @@ const createCreatePoolSlice = (set: SetState, get: GetState) => ({ }, networks: { networks }, } = get() + const { notify: notifyNotification } = useWalletStore.getState() let dismissNotificationHandler diff --git a/apps/main/src/dex/store/createDashboardSlice.ts b/apps/main/src/dex/store/createDashboardSlice.ts index 431fadad0..048314d15 100644 --- a/apps/main/src/dex/store/createDashboardSlice.ts +++ b/apps/main/src/dex/store/createDashboardSlice.ts @@ -1,20 +1,20 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/dex/store/useStore' import type { Address } from 'viem' +import { isAddress } from 'viem' import type { VecrvInfo } from '@/dex/components/PageCrvLocker/types' import type { IProfit } from '@curvefi/api/lib/interfaces' import type { + DashboardDataMapper, + DashboardDatasMapper, FormStatus, FormValues, Order, SortId, WalletPoolData, - DashboardDatasMapper, - DashboardDataMapper, } from '@/dex/components/PageDashboard/types' import { PromisePool } from '@supercharge/promise-pool' -import { isAddress } from 'viem' import orderBy from 'lodash/orderBy' import { DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES, SORT_ID } from '@/dex/components/PageDashboard/utils' @@ -22,7 +22,9 @@ import { claimButtonsKey } from '@/dex/components/PageDashboard/components/FormC import { fulfilledValue, getErrorMessage, getStorageValue, setStorageValue, sleep } from '@/dex/utils' import { shortenAccount } from '@ui/utils' import curvejsApi from '@/dex/lib/curvejs' -import { CurveApi, ChainId, PoolDataMapper, FnStepResponse } from '@/dex/types/main.types' +import { ChainId, CurveApi, FnStepResponse, PoolDataMapper } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -329,11 +331,10 @@ const createDashboardSlice = (set: SetState, get: GetState): Dashb // steps fetchStepClaimFees: async (activeKey, curve, walletAddress, key) => { - const { pools, gas, wallet } = get() + const { pools, gas } = get() const { claimableFees, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) const { chainId } = curve @@ -373,12 +374,10 @@ const createDashboardSlice = (set: SetState, get: GetState): Dashb fetchStepWithdrawVecrv: async (activeKey, curve, walletAddress) => { const { gas, - wallet, [sliceKey]: { formValues, ...sliceState }, } = get() - const provider = wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update form let cFormStatus: FormStatus = { diff --git a/apps/main/src/dex/store/createDeployGaugeSlice.ts b/apps/main/src/dex/store/createDeployGaugeSlice.ts index 7c928af86..4b612e101 100644 --- a/apps/main/src/dex/store/createDeployGaugeSlice.ts +++ b/apps/main/src/dex/store/createDeployGaugeSlice.ts @@ -7,6 +7,7 @@ import produce from 'immer' import { t } from '@lingui/macro' import { shortenTokenAddress } from '@/dex/utils' import { CurveApi, ChainId } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type NetworkWithFactory = { chainId: ChainId @@ -181,7 +182,7 @@ const createDeployGaugeSlice = (set: SetState, get: GetState) => ( const { poolAddress, lpTokenAddress, sidechainGauge, currentSidechain } = get().deployGauge const chainId = curve.chainId const fetchGasInfo = get().gas.fetchGasInfo - const notifyNotification = get().wallet.notifyNotification + const { notify: notifyNotification } = useWalletStore.getState() const tokenAddress = sidechainGauge ? lpTokenAddress : poolAddress const shortenAddress = shortenTokenAddress(tokenAddress) diff --git a/apps/main/src/dex/store/createGasSlice.ts b/apps/main/src/dex/store/createGasSlice.ts index 8b77e6bfd..b043272d1 100644 --- a/apps/main/src/dex/store/createGasSlice.ts +++ b/apps/main/src/dex/store/createGasSlice.ts @@ -7,6 +7,7 @@ import { httpFetcher } from '@/dex/lib/utils' import { log } from '@ui-kit/lib/logging' import api from '@/dex/lib/curvejs' import { CurveApi, NetworkConfig, Provider, GasInfo } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -46,10 +47,9 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => const { chainId } = curve const { - wallet, networks: { networks }, } = get() - const provider = wallet.getProvider('') + const { provider } = useWalletStore.getState() log('fetchGasInfo', chainId) try { @@ -115,8 +115,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => curve.setCustomFeeData(customFeeData) } else if (chainId === 252 || chainId === 8453) { // TODO: remove this hardcode value once it api is fixed - const provider = get().wallet.getProvider('') - + const { provider } = useWalletStore.getState() if (provider) { parsedGasInfo = await parseGasInfo(curve, provider, networks) @@ -129,8 +128,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => } } else if (chainId === 10) { // TODO: remove this hardcode value once it api is fixed - const provider = get().wallet.getProvider('') - + const { provider } = useWalletStore.getState() if (provider) { parsedGasInfo = await parseGasInfo(curve, provider, networks) @@ -146,7 +144,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => if (parsedGasInfo) { get()[sliceKey].setStateByKeys(parsedGasInfo) } else { - const provider = get().wallet.getProvider('') + const { provider } = useWalletStore.getState() if (provider && chainId) { const parsedGasInfo = await parseGasInfo(curve, provider, networks) if (parsedGasInfo) { diff --git a/apps/main/src/dex/store/createGlobalSlice.ts b/apps/main/src/dex/store/createGlobalSlice.ts index 5cc35c2c2..177641ab7 100644 --- a/apps/main/src/dex/store/createGlobalSlice.ts +++ b/apps/main/src/dex/store/createGlobalSlice.ts @@ -1,6 +1,6 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/dex/store/useStore' -import { ConnectState, getPageWidthClassName } from '@ui/utils' +import { CONNECT_STAGE, ConnectState, getPageWidthClassName } from '@ui/utils' import isEqual from 'lodash/isEqual' import produce from 'immer' import { log } from '@ui-kit/lib/logging' @@ -27,8 +27,8 @@ export type LayoutHeight = { export const layoutHeightKeys = ['globalAlert', 'mainNav', 'secondaryNav', 'footer'] as const type GlobalState = { - connectState: ConnectState curve: CurveApi + connectState: ConnectState hasDepositAndStake: { [chainId: string]: boolean | null } hasRouter: { [chainId: string]: boolean | null } isLoadingApi: boolean @@ -54,13 +54,12 @@ export interface GlobalSlice extends GlobalState { setNetworkConfigFromApi(curve: CurveApi): void setPageWidth: (pageWidth: number) => void updateConnectState( - status: ConnectState['status'], - stage: ConnectState['stage'], + status?: ConnectState['status'], + stage?: ConnectState['stage'], options?: ConnectState['options'], ): void updateCurveJs(curveApi: CurveApi, prevCurveApi: CurveApi | null, wallet: Wallet | null): Promise updateLayoutHeight: (key: keyof LayoutHeight, value: number) => void - updateMaxSlippage(key: string, value: string | null): void updateShowScrollButton(scrollY: number): void updateGlobalStoreByKey: (key: DefaultStateKeys, value: T) => void @@ -72,9 +71,10 @@ export interface GlobalSlice extends GlobalState { const DEFAULT_STATE = { connectState: { status: '' as const, stage: '' }, - curve: null, + curve: null!, hasDepositAndStake: {}, hasRouter: {}, + pageWidthPx: null, isMobile: false, isLoadingApi: false, isLoadingCurve: true, @@ -95,9 +95,9 @@ const DEFAULT_STATE = { }, routerProps: null, showScrollButton: false, -} +} satisfies GlobalState -const createGlobalSlice = (set: SetState, get: GetState) => ({ +const createGlobalSlice = (set: SetState, get: GetState): GlobalSlice => ({ ...DEFAULT_STATE, getNetworkConfigFromApi: (chainId: ChainId | '') => { @@ -145,13 +145,8 @@ const createGlobalSlice = (set: SetState, get: GetState) => ({ }), ) }, - updateConnectState: ( - status: ConnectState['status'], - stage: ConnectState['stage'], - options?: ConnectState['options'], - ) => { - const value = options ? { status, stage, options } : { status, stage } - get().updateGlobalStoreByKey('connectState', value) + updateConnectState: (status = 'loading', stage = CONNECT_STAGE.CONNECT_WALLET, options = ['']) => { + set({ connectState: { status, stage, ...(options && { options }) } }) }, updateCurveJs: async (curveApi: CurveApi, prevCurveApi: CurveApi | null, wallet: Wallet | null) => { const state = get() diff --git a/apps/main/src/dex/store/createLockedCrvSlice.ts b/apps/main/src/dex/store/createLockedCrvSlice.ts index 21f3d3d9e..e23d96098 100644 --- a/apps/main/src/dex/store/createLockedCrvSlice.ts +++ b/apps/main/src/dex/store/createLockedCrvSlice.ts @@ -1,26 +1,28 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/dex/store/useStore' -import type { FormType, FormEstGas, FormStatus, FormValues, VecrvInfo } from '@/dex/components/PageCrvLocker/types' +import type { FormEstGas, FormStatus, FormType, FormValues, VecrvInfo } from '@/dex/components/PageCrvLocker/types' import cloneDeep from 'lodash/cloneDeep' import { + DEFAULT_FORM_EST_GAS, + DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES, DEFAULT_USER_LOCKED_CRV_INFO, - DEFAULT_FORM_STATUS, - DEFAULT_FORM_EST_GAS, } from '@/dex/components/PageCrvLocker/utils' import { formatNumber, shortenAccount } from '@ui/utils' import curvejsApi from '@/dex/lib/curvejs' import dayjs from '@ui-kit/lib/dayjs' import { - CurveApi, ChainId, - FnStepEstGasApprovalResponse, + CurveApi, FnStepApproveResponse, + FnStepEstGasApprovalResponse, FnStepResponse, } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -166,41 +168,41 @@ const createLockedCrvSlice = (set: SetState, get: GetState): Locke return resp }, fetchStepApprove: async (activeKey, curve, rFormType, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) - cFormStatus.formProcessing = true - cFormStatus.step = 'APPROVAL' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const resp = await curvejsApi.lockCrv.lockCrvApprove(activeKey, provider, curve, formValues.lockedAmt) - - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus.formProcessing = false - cFormStatus.step = '' + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.isApproved = true - cFormStatus.formTypeCompleted = 'APPROVE' - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - - // fetch est gas and approval - await get()[sliceKey].fetchEstGasApproval(activeKey, curve, rFormType, formValues) - } - - return resp + let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) + cFormStatus.formProcessing = true + cFormStatus.step = 'APPROVAL' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + + await get().gas.fetchGasInfo(curve) + const resp = await curvejsApi.lockCrv.lockCrvApprove(activeKey, provider, curve, formValues.lockedAmt) + + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus.formProcessing = false + cFormStatus.step = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.isApproved = true + cFormStatus.formTypeCompleted = 'APPROVE' + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + + // fetch est gas and approval + await get()[sliceKey].fetchEstGasApproval(activeKey, curve, rFormType, formValues) } + + return resp } }, fetchStepCreate: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider && formValues.lockedAmt && formValues.utcDate && formValues.days) { + if (formValues.lockedAmt && formValues.utcDate && formValues.days) { let cFormStatus = cloneDeep(get()[sliceKey].formStatus) cFormStatus.formProcessing = true cFormStatus.step = 'CREATE_LOCK' @@ -244,75 +246,71 @@ const createLockedCrvSlice = (set: SetState, get: GetState): Locke } }, fetchStepIncreaseCrv: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = true - cFormStatus.step = 'INCREASE_CRV' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const resp = await curvejsApi.lockCrv.increaseAmount(activeKey, curve, provider, formValues.lockedAmt) - - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'INCREASE_CRV' - get()[sliceKey].setStateByKeys({ - formValues: cloneDeep(DEFAULT_FORM_VALUES), - formStatus: cloneDeep(cFormStatus), - }) - - // re-fetch data - get()[sliceKey].fetchVecrvInfo(curve) - } + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = true + cFormStatus.step = 'INCREASE_CRV' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + await get().gas.fetchGasInfo(curve) + const resp = await curvejsApi.lockCrv.increaseAmount(activeKey, curve, provider, formValues.lockedAmt) + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'INCREASE_CRV' + get()[sliceKey].setStateByKeys({ + formValues: cloneDeep(DEFAULT_FORM_VALUES), + formStatus: cloneDeep(cFormStatus), + }) - return resp + // re-fetch data + get()[sliceKey].fetchVecrvInfo(curve) } + + return resp } }, fetchStepIncreaseTime: async (activeKey, curve, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = true - cFormStatus.step = 'INCREASE_TIME' - get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) - - await get().gas.fetchGasInfo(curve) - const resp = await curvejsApi.lockCrv.increaseUnlockTime(activeKey, provider, curve, formValues.days) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.activeKey === get()[sliceKey].activeKey) { - cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'INCREASE_TIME' - get()[sliceKey].setStateByKeys({ - formValues: cloneDeep(DEFAULT_FORM_VALUES), - formStatus: cloneDeep(cFormStatus), - }) - - // re-fetch data - get()[sliceKey].fetchVecrvInfo(curve) - } + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = true + cFormStatus.step = 'INCREASE_TIME' + get()[sliceKey].setStateByKey('formStatus', cloneDeep(cFormStatus)) + + await get().gas.fetchGasInfo(curve) + const resp = await curvejsApi.lockCrv.increaseUnlockTime(activeKey, provider, curve, formValues.days) + + if (resp.activeKey === get()[sliceKey].activeKey) { + cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'INCREASE_TIME' + get()[sliceKey].setStateByKeys({ + formValues: cloneDeep(DEFAULT_FORM_VALUES), + formStatus: cloneDeep(cFormStatus), + }) - return resp + // re-fetch data + get()[sliceKey].fetchVecrvInfo(curve) } + + return resp } }, diff --git a/apps/main/src/dex/store/createPoolDepositSlice.ts b/apps/main/src/dex/store/createPoolDepositSlice.ts index c2daee1e0..a5ffbd9cf 100644 --- a/apps/main/src/dex/store/createPoolDepositSlice.ts +++ b/apps/main/src/dex/store/createPoolDepositSlice.ts @@ -30,6 +30,8 @@ import { FnStepApproveResponse, FnStepResponse, } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -364,221 +366,197 @@ const createPoolDepositSlice = (set: SetState, get: GetState): Poo return resp }, fetchStepApprove: async (activeKey, curve, formType, pool, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'APPROVAL', - }) - - await get().gas.fetchGasInfo(curve) - const { chainId } = curve - const { amounts, isWrapped } = formValues - - const approveFn = - formType === 'DEPOSIT' ? curvejsApi.poolDeposit.depositApprove : curvejsApi.poolDeposit.depositAndStakeApprove - - const resp = await approveFn(activeKey, provider, pool, isWrapped, parseAmountsForAPI(amounts)) - - if (resp.activeKey === get()[sliceKey].activeKey) { - const cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'APPROVE' - cFormStatus.isApproved = true - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - - // fetch est gas and approval - await get()[sliceKey].fetchEstGasApproval(activeKey, chainId, formType, pool) - } + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const { chainId } = curve + const { amounts, isWrapped } = formValues + const approveFn = + formType === 'DEPOSIT' ? curvejsApi.poolDeposit.depositApprove : curvejsApi.poolDeposit.depositAndStakeApprove + const resp = await approveFn(activeKey, provider, pool, isWrapped, parseAmountsForAPI(amounts)) + if (resp.activeKey === get()[sliceKey].activeKey) { + const cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'APPROVE' + cFormStatus.isApproved = true + get()[sliceKey].setStateByKey('formStatus', cFormStatus) - return resp + // fetch est gas and approval + await get()[sliceKey].fetchEstGasApproval(activeKey, chainId, formType, pool) } + + return resp } }, fetchStepDeposit: async (activeKey, curve, poolData, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'DEPOSIT', - }) - - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - const { amounts, isWrapped } = formValues - const resp = await curvejsApi.poolDeposit.deposit( - activeKey, - provider, - pool, - isWrapped, - parseAmountsForAPI(amounts), - maxSlippage, - ) - - if (resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'DEPOSIT' - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(formValues), - }) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'DEPOSIT', + }) - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + const { amounts, isWrapped } = formValues + const resp = await curvejsApi.poolDeposit.deposit( + activeKey, + provider, + pool, + isWrapped, + parseAmountsForAPI(amounts), + maxSlippage, + ) + + if (resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'DEPOSIT' + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(formValues), + }) - return resp + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } + + return resp } }, fetchStepDepositStake: async (activeKey, curve, poolData, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'DEPOSIT_STAKE', - }) - - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - const { amounts, isWrapped } = formValues - const resp = await curvejsApi.poolDeposit.depositAndStake( - activeKey, - provider, - pool, - isWrapped, - parseAmountsForAPI(amounts), - maxSlippage, - ) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'DEPOSIT_STAKE' - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(formValues), - }) - - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'DEPOSIT_STAKE', + }) + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + const { amounts, isWrapped } = formValues + const resp = await curvejsApi.poolDeposit.depositAndStake( + activeKey, + provider, + pool, + isWrapped, + parseAmountsForAPI(amounts), + maxSlippage, + ) + if (resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'DEPOSIT_STAKE' + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(formValues), + }) - return resp + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } + + return resp } }, fetchStepStakeApprove: async (activeKey, curve, formType, pool, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'APPROVAL', - }) - - await get().gas.fetchGasInfo(curve) - const { chainId } = curve - const { lpToken } = formValues - const resp = await curvejsApi.poolDeposit.stakeApprove(activeKey, provider, pool, lpToken) - - if (resp.activeKey === get()[sliceKey].activeKey) { - const cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'APPROVE' - cFormStatus.isApproved = true - get()[sliceKey].setStateByKey('formStatus', cFormStatus) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - // fetch est gas and approval - await get()[sliceKey].fetchEstGasApproval(activeKey, chainId, formType, pool) - } + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const { chainId } = curve + const { lpToken } = formValues + const resp = await curvejsApi.poolDeposit.stakeApprove(activeKey, provider, pool, lpToken) + if (resp.activeKey === get()[sliceKey].activeKey) { + const cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'APPROVE' + cFormStatus.isApproved = true + get()[sliceKey].setStateByKey('formStatus', cFormStatus) - return resp + // fetch est gas and approval + await get()[sliceKey].fetchEstGasApproval(activeKey, chainId, formType, pool) } + + return resp } }, fetchStepStake: async (activeKey, curve, poolData, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'STAKE', - }) - - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - const { lpToken } = formValues - const resp = await curvejsApi.poolDeposit.stake(activeKey, provider, pool, lpToken) - - if (resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'STAKE' - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(formValues), - }) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'STAKE', + }) + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + const { lpToken } = formValues + const resp = await curvejsApi.poolDeposit.stake(activeKey, provider, pool, lpToken) + if (resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'STAKE' + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(formValues), + }) - return resp + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } + + return resp } }, diff --git a/apps/main/src/dex/store/createPoolSwapSlice.ts b/apps/main/src/dex/store/createPoolSwapSlice.ts index 2eed67c23..13846eb63 100644 --- a/apps/main/src/dex/store/createPoolSwapSlice.ts +++ b/apps/main/src/dex/store/createPoolSwapSlice.ts @@ -1,7 +1,7 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/dex/store/useStore' import type { EstimatedGas as FormEstGas } from '@/dex/components/PagePool/types' -import type { ExchangeOutput, RouterSwapOutput, FormStatus, FormValues } from '@/dex/components/PagePool/Swap/types' +import type { ExchangeOutput, FormStatus, FormValues, RouterSwapOutput } from '@/dex/components/PagePool/Swap/types' import type { RoutesAndOutput, RoutesAndOutputModal } from '@/dex/components/PageRouterSwap/types' import cloneDeep from 'lodash/cloneDeep' import { Contract, Interface, JsonRpcProvider } from 'ethers' @@ -17,15 +17,17 @@ import { getSwapActionModalType } from '@/dex/utils/utilsSwap' import curvejsApi from '@/dex/lib/curvejs' import { Balances, - CurveApi, ChainId, CurrencyReserves, - Pool, - PoolData, - FnStepEstGasApprovalResponse, + CurveApi, FnStepApproveResponse, + FnStepEstGasApprovalResponse, FnStepResponse, + Pool, + PoolData, } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -92,9 +94,7 @@ const createPoolSwapSlice = (set: SetState, get: GetState): PoolSw if (typeof storedIgnoreExchangeRateCheck !== 'undefined') { return storedIgnoreExchangeRateCheck } else { - const provider = state.wallet.getProvider('') || new JsonRpcProvider(networks[chainId].rpcUrl) - - if (!provider) return false + const provider = useWalletStore.getState().provider || new JsonRpcProvider(networks[chainId].rpcUrl) try { const json = await import('@/dex/components/PagePool/abis/stored_rates.json').then((module) => module.default) @@ -346,114 +346,100 @@ const createPoolSwapSlice = (set: SetState, get: GetState): PoolSw return resp }, fetchStepApprove: async (activeKey, curve, pool, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - const storedActiveKey = get()[sliceKey].activeKey - - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'APPROVAL', - }) - - await get().gas.fetchGasInfo(curve) - const { fromAddress, fromAmount, isWrapped } = formValues - const resp = await curvejsApi.poolSwap.swapApprove( - activeKey, - provider, - pool, - isWrapped, - fromAddress, - fromAmount, - ) - - if (resp.activeKey === get()[sliceKey].activeKey) { - const cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'APPROVE' - cFormStatus.isApproved = true - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - - // fetch est gas, approval and exchange - get()[sliceKey].fetchEstGasApproval(activeKey, curve.chainId, pool, formValues, maxSlippage) - await get()[sliceKey].fetchExchangeOutput(activeKey, storedActiveKey, curve, pool, formValues, maxSlippage) - } + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - return resp + const storedActiveKey = get()[sliceKey].activeKey + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const { fromAddress, fromAmount, isWrapped } = formValues + const resp = await curvejsApi.poolSwap.swapApprove(activeKey, provider, pool, isWrapped, fromAddress, fromAmount) + if (resp.activeKey === get()[sliceKey].activeKey) { + const cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'APPROVE' + cFormStatus.isApproved = true + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + + // fetch est gas, approval and exchange + get()[sliceKey].fetchEstGasApproval(activeKey, curve.chainId, pool, formValues, maxSlippage) + await get()[sliceKey].fetchExchangeOutput(activeKey, storedActiveKey, curve, pool, formValues, maxSlippage) } + + return resp } }, fetchStepSwap: async (activeKey, curve, poolData, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'SWAP', - }) - - await get().gas.fetchGasInfo(curve) - const { fromAddress, fromToken, fromAmount, toAddress, toToken, isWrapped } = formValues - const resp = await curvejsApi.poolSwap.swap( - activeKey, - provider, - poolData.pool, - isWrapped, - fromAddress, - toAddress, - fromAmount, - maxSlippage, - ) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.activeKey === get()[sliceKey].activeKey) { - const cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - const cFormValues = cloneDeep(formValues) - cFormValues.fromAmount = '' - cFormValues.toAmount = '' - - cFormStatus.formTypeCompleted = 'SWAP' - - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - activeKey: getActiveKey(cFormValues, maxSlippage), - exchangeOutput: {}, - formEstGas: {}, - formValues: cFormValues, - }) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'SWAP', + }) + await get().gas.fetchGasInfo(curve) + const { fromAddress, fromToken, fromAmount, toAddress, toToken, isWrapped } = formValues + const resp = await curvejsApi.poolSwap.swap( + activeKey, + provider, + poolData.pool, + isWrapped, + fromAddress, + toAddress, + fromAmount, + maxSlippage, + ) + if (resp.activeKey === get()[sliceKey].activeKey) { + const cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' + + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + const cFormValues = cloneDeep(formValues) + cFormValues.fromAmount = '' + cFormValues.toAmount = '' + + cFormStatus.formTypeCompleted = 'SWAP' + + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + activeKey: getActiveKey(cFormValues, maxSlippage), + exchangeOutput: {}, + formEstGas: {}, + formValues: cFormValues, + }) - // cache swapped tokens - get().storeCache.setStateByActiveKey('routerFormValues', curve.chainId.toString(), { - fromAddress, - fromToken, - toAddress, - toToken, - }) + // cache swapped tokens + get().storeCache.setStateByActiveKey('routerFormValues', curve.chainId.toString(), { + fromAddress, + fromToken, + toAddress, + toToken, + }) - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, poolData.pool.id, true), - get().pools.fetchPoolStats(curve, poolData), - ]) - } - return resp + // re-fetch data + await Promise.all([ + get().user.fetchUserPoolInfo(curve, poolData.pool.id, true), + get().pools.fetchPoolStats(curve, poolData), + ]) } + return resp } }, diff --git a/apps/main/src/dex/store/createPoolWithdrawSlice.ts b/apps/main/src/dex/store/createPoolWithdrawSlice.ts index 9035ca648..c2af0e919 100644 --- a/apps/main/src/dex/store/createPoolWithdrawSlice.ts +++ b/apps/main/src/dex/store/createPoolWithdrawSlice.ts @@ -21,6 +21,8 @@ import { FnStepApproveResponse, FnStepResponse, } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -417,198 +419,175 @@ const createPoolWithdrawSlice = (set: SetState, get: GetState): Po return resp }, fetchStepApprove: async (activeKey, curve, formType, pool, formValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'APPROVAL', - }) - - await get().gas.fetchGasInfo(curve) - const { amounts, lpToken, selected } = formValues - let resp - - if (selected === 'token' || selected === 'lpToken') { - const fn = - selected === 'token' - ? curvejsApi.poolWithdraw.withdrawOneCoinApprove - : curvejsApi.poolWithdraw.withdrawApprove - resp = await fn(activeKey, provider, pool, lpToken) - } else if (formValues.selected === 'imbalance') { - const fn = curvejsApi.poolWithdraw.withdrawImbalanceApprove - resp = await fn(activeKey, provider, pool, parseAmountsForAPI(amounts)) - } - - if (resp && resp.activeKey === get()[sliceKey].activeKey) { - const cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'APPROVE' - cFormStatus.isApproved = true - get()[sliceKey].setStateByKey('formStatus', cFormStatus) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const { amounts, lpToken, selected } = formValues + let resp + if (selected === 'token' || selected === 'lpToken') { + const fn = + selected === 'token' + ? curvejsApi.poolWithdraw.withdrawOneCoinApprove + : curvejsApi.poolWithdraw.withdrawApprove + resp = await fn(activeKey, provider, pool, lpToken) + } else if (formValues.selected === 'imbalance') { + const fn = curvejsApi.poolWithdraw.withdrawImbalanceApprove + resp = await fn(activeKey, provider, pool, parseAmountsForAPI(amounts)) + } + if (resp && resp.activeKey === get()[sliceKey].activeKey) { + const cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.step = '' + cFormStatus.error = '' - // fetch est gas and approval - await get()[sliceKey].fetchEstGasApproval(activeKey, curve, formType, pool, formValues) - } + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'APPROVE' + cFormStatus.isApproved = true + get()[sliceKey].setStateByKey('formStatus', cFormStatus) - return resp + // fetch est gas and approval + await get()[sliceKey].fetchEstGasApproval(activeKey, curve, formType, pool, formValues) } + + return resp } }, fetchStepWithdraw: async (activeKey, curve, poolData, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'WITHDRAW', - }) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'WITHDRAW', + }) + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + let resp + if (formValues.selected === 'token') { + const fn = curvejsApi.poolWithdraw.withdrawOneCoin + resp = await fn( + activeKey, + provider, + poolData.pool, + formValues.isWrapped, + formValues.lpToken, + formValues.selectedTokenAddress, + maxSlippage, + ) + } else if (formValues.selected === 'lpToken') { + const fn = curvejsApi.poolWithdraw.withdraw + resp = await fn(activeKey, provider, pool, formValues.isWrapped, formValues.lpToken, maxSlippage) + } else if (formValues.selected === 'imbalance') { + const amounts = parseAmountsForAPI(formValues.amounts) + const fn = curvejsApi.poolWithdraw.withdrawImbalance + resp = await fn(activeKey, provider, pool, formValues.isWrapped, amounts, maxSlippage) + } + if (resp && resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - let resp + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'WITHDRAW' + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(formValues), + }) - if (formValues.selected === 'token') { - const fn = curvejsApi.poolWithdraw.withdrawOneCoin - resp = await fn( - activeKey, - provider, - poolData.pool, - formValues.isWrapped, - formValues.lpToken, - formValues.selectedTokenAddress, - maxSlippage, - ) - } else if (formValues.selected === 'lpToken') { - const fn = curvejsApi.poolWithdraw.withdraw - resp = await fn(activeKey, provider, pool, formValues.isWrapped, formValues.lpToken, maxSlippage) - } else if (formValues.selected === 'imbalance') { - const amounts = parseAmountsForAPI(formValues.amounts) - const fn = curvejsApi.poolWithdraw.withdrawImbalance - resp = await fn(activeKey, provider, pool, formValues.isWrapped, amounts, maxSlippage) + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } - if (resp && resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'WITHDRAW' - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(formValues), - }) - - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } - - return resp - } + return resp } }, fetchStepUnstake: async (activeKey, curve, poolData, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'UNSTAKE', - }) - - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - const resp = await curvejsApi.poolWithdraw.unstake(activeKey, provider, pool, formValues.stakedLpToken) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.error = '' - - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = 'UNSTAKE' - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(formValues), - }) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'UNSTAKE', + }) + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + const resp = await curvejsApi.poolWithdraw.unstake(activeKey, provider, pool, formValues.stakedLpToken) + if (resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.error = '' - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = 'UNSTAKE' + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(formValues), + }) - return resp + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } + + return resp } }, fetchStepClaim: async (activeKey, curve, poolData) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - formProcessing: true, - step: 'CLAIM', - }) - - await get().gas.fetchGasInfo(curve) - const { pool } = poolData - const { isClaimCrv } = get()[sliceKey].formStatus - const resp = isClaimCrv - ? await curvejsApi.poolWithdraw.claimCrv(activeKey, provider, pool) - : await curvejsApi.poolWithdraw.claimRewards(activeKey, provider, pool) - - if (resp.activeKey === get()[sliceKey].activeKey) { - let cFormStatus = cloneDeep(get()[sliceKey].formStatus) - cFormStatus.formProcessing = false - cFormStatus.step = '' - cFormStatus.isClaimCrv = false - cFormStatus.isClaimRewards = false - cFormStatus.error = '' + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - if (resp.error) { - cFormStatus.error = resp.error - get()[sliceKey].setStateByKey('formStatus', cFormStatus) - } else { - cFormStatus.formTypeCompleted = isClaimCrv ? 'CLAIM_CRV' : 'CLAIM_REWARDS' - const storedFormValues = get()[sliceKey].formValues - get()[sliceKey].setStateByKeys({ - formStatus: cFormStatus, - formValues: resetFormValues(storedFormValues), - }) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + formProcessing: true, + step: 'CLAIM', + }) + await get().gas.fetchGasInfo(curve) + const { pool } = poolData + const { isClaimCrv } = get()[sliceKey].formStatus + const resp = isClaimCrv + ? await curvejsApi.poolWithdraw.claimCrv(activeKey, provider, pool) + : await curvejsApi.poolWithdraw.claimRewards(activeKey, provider, pool) + if (resp.activeKey === get()[sliceKey].activeKey) { + let cFormStatus = cloneDeep(get()[sliceKey].formStatus) + cFormStatus.formProcessing = false + cFormStatus.step = '' + cFormStatus.isClaimCrv = false + cFormStatus.isClaimRewards = false + cFormStatus.error = '' - // re-fetch data - await Promise.all([ - get().user.fetchUserPoolInfo(curve, pool.id), - get().pools.fetchPoolStats(curve, poolData), - ]) - } + if (resp.error) { + cFormStatus.error = resp.error + get()[sliceKey].setStateByKey('formStatus', cFormStatus) + } else { + cFormStatus.formTypeCompleted = isClaimCrv ? 'CLAIM_CRV' : 'CLAIM_REWARDS' + const storedFormValues = get()[sliceKey].formValues + get()[sliceKey].setStateByKeys({ + formStatus: cFormStatus, + formValues: resetFormValues(storedFormValues), + }) - return resp + // re-fetch data + await Promise.all([get().user.fetchUserPoolInfo(curve, pool.id), get().pools.fetchPoolStats(curve, poolData)]) } + + return resp } }, diff --git a/apps/main/src/dex/store/createQuickSwapSlice.ts b/apps/main/src/dex/store/createQuickSwapSlice.ts index f49be71df..09f6fdcd8 100644 --- a/apps/main/src/dex/store/createQuickSwapSlice.ts +++ b/apps/main/src/dex/store/createQuickSwapSlice.ts @@ -20,6 +20,8 @@ import { getSwapActionModalType } from '@/dex/utils/utilsSwap' import { getChainSignerActiveKey, sleep } from '@/dex/utils' import curvejsApi from '@/dex/lib/curvejs' import { CurveApi, TokensMapper, FnStepApproveResponse, FnStepResponse } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -425,9 +427,8 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick const state = get() const sliceState = state[sliceKey] - const provider = state.wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) sliceState.setStateByKey('formStatus', { ...sliceState.formStatus, @@ -468,9 +469,8 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick const state = get() const sliceState = state[sliceKey] - const provider = state.wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, diff --git a/apps/main/src/dex/store/createWalletSlice.ts b/apps/main/src/dex/store/createWalletSlice.ts deleted file mode 100644 index 2b904e6de..000000000 --- a/apps/main/src/dex/store/createWalletSlice.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { CustomNotification, NotificationType } from '@web3-onboard/core/dist/types' -import type { GetState, SetState } from 'zustand' -import type { OnboardAPI, UpdateNotification } from '@web3-onboard/core' -import type { State } from '@/dex/store/useStore' - -import { BrowserProvider, ethers } from 'ethers' -import cloneDeep from 'lodash/cloneDeep' - -import { CONNECT_STAGE } from '@/dex/constants' -import { Provider, Wallet } from '@/dex/types/main.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - onboard: OnboardAPI | null -} - -const sliceKey = 'wallet' - -// prettier-ignore -export type WalletSlice = { - [sliceKey]: SliceState & { - notifyNotification(message: string, type: NotificationType, autoDismiss?: number): ({ dismiss: () => void; update: UpdateNotification | undefined }) - updateConnectWalletStateKeys(): void - getProvider(sliceKey: 'quickSwap' | 'poolDeposit' | 'poolWithdraw' | 'poolSwap' | 'dashboard' | 'lockedCrv' | ''): Provider | null - - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - onboard: null, -} - -const createWalletSlice = (set: SetState, get: GetState): WalletSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - notifyNotification: (message, type = 'pending', autoDismiss) => { - const onboard = get().wallet.onboard - - if (onboard) { - // see https://onboard.blocknative.com/docs/packages/core#options for all options - const customNotification: CustomNotification = { - type, - message, - } - - if (typeof autoDismiss !== 'undefined') { - customNotification.autoDismiss = autoDismiss - } - - return onboard.state.actions.customNotification(customNotification) - } else { - return { dismiss: () => {}, update: undefined } - } - }, - updateConnectWalletStateKeys: () => { - get().updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, ['']) - }, - getProvider: (sliceKey) => { - let provider = null - - // get provider from onboard - try { - const onboard = get().wallet.onboard - - if (onboard) { - const wallet = onboard.state.get().wallets?.[0] - provider = wallet ? new BrowserProvider(wallet.provider) : null - } - } catch (error) { - console.error('error getting wallet') - } - - // update form error if provider is not found - if (!provider && sliceKey) { - const storedFormStatus = get()[sliceKey]?.formStatus - if ( - storedFormStatus && - typeof storedFormStatus === 'object' && - 'step' in storedFormStatus && - 'formProcessing' in storedFormStatus && - 'error' in storedFormStatus - ) { - get()[sliceKey].setStateByKey('formStatus', { - ...storedFormStatus, - step: '', - formProcessing: false, - error: 'error-invalid-provider', - }) - } - } - return provider - }, - - // slice helpers - setStateByActiveKey: (key, activeKey, value) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key, value) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createWalletSlice - -export function getProvider(wallet: Wallet) { - return new ethers.BrowserProvider(wallet.provider) -} - -export function getWalletChainId(wallet: Wallet | undefined | null) { - if (!wallet) return null - const chainId = wallet.chains[0].id - return Number(BigInt(chainId).toString()) -} diff --git a/apps/main/src/dex/store/useStore.ts b/apps/main/src/dex/store/useStore.ts index d59a25bde..aad305f4a 100644 --- a/apps/main/src/dex/store/useStore.ts +++ b/apps/main/src/dex/store/useStore.ts @@ -8,7 +8,6 @@ import debounce from 'lodash/debounce' import createGlobalSlice, { GlobalSlice } from '@/dex/store/createGlobalSlice' import createNetworksSlice, { NetworksSlice } from '@/dex/store/createNetworksSlice' import createGasSlice, { GasSlice } from '@/dex/store/createGasSlice' -import createWalletSlice, { WalletSlice } from '@/dex/store/createWalletSlice' import createPoolsSlice, { PoolsSlice } from '@/dex/store/createPoolsSlice' import createUserSlice, { UserSlice } from '@/dex/store/createUserSlice' import createPoolListSlice, { PoolListSlice } from '@/dex/store/createPoolListSlice' @@ -31,7 +30,6 @@ import createCampaignRewardsSlice, { CampaignRewardsSlice } from '@/dex/store/cr export type State = GlobalSlice & NetworksSlice & GasSlice & - WalletSlice & CacheSlice & PoolsSlice & PoolDepositSlice & @@ -56,7 +54,6 @@ const store = (set: SetState, get: GetState): State => ({ ...createNetworksSlice(set, get), ...createGasSlice(set, get), ...createCacheSlice(set, get), - ...createWalletSlice(set, get), ...createPoolListSlice(set, get), ...createPoolsSlice(set, get), ...createPoolDepositSlice(set, get), diff --git a/apps/main/src/dex/utils/storage.ts b/apps/main/src/dex/utils/storage.ts index 2045eca3f..b0480bc6d 100644 --- a/apps/main/src/dex/utils/storage.ts +++ b/apps/main/src/dex/utils/storage.ts @@ -1,8 +1,6 @@ import merge from 'lodash/merge' -import dayjs from '@ui-kit/lib/dayjs' export const APP_STORAGE = { - APP_CACHE: 'curve-app-cache', APP_DASHBOARD: 'curve-app-dashboard', } @@ -20,12 +18,7 @@ export function getStorageValue(key: Key) { } } - if (key === 'APP_CACHE') { - return { - timestamp: parsedStoredValue.timestamp ?? '', - walletName: getWalletName(parsedStoredValue.walletName, parsedStoredValue.timestamp), - } - } else if (key === 'APP_DASHBOARD') { + if (key === 'APP_DASHBOARD') { return { addresses: Array.isArray(parsedStoredValue.addresses) ? (parsedStoredValue.addresses as string[]) @@ -34,11 +27,6 @@ export function getStorageValue(key: Key) { } } -function getWalletName(walletName: string | undefined, timestamp: string | undefined) { - const isStaled = walletName && timestamp && dayjs().diff(+timestamp, 'days') > 5 - return isStaled || !walletName ? '' : walletName -} - export function setStorageValue(key: Key, updatedValue: T) { const storedValue = getStorageValue(key) const mergedStoredValue = merge(storedValue, updatedValue) diff --git a/apps/main/src/lend/components/ConnectWallet.tsx b/apps/main/src/lend/components/ConnectWallet.tsx deleted file mode 100644 index 532619d8d..000000000 --- a/apps/main/src/lend/components/ConnectWallet.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { isLoading } from '@ui/utils' -import useStore from '@/lend/store/useStore' - -import { CONNECT_STAGE } from '@/lend/constants' - -import ConnectWalletPrompt from '@ui/ConnectWalletPrompt' -import { useUserProfileStore } from '@ui-kit/features/user-profile' - -type ConnectWalletProps = { - description: string - connectText: string - loadingText: string -} - -const ConnectWallet: React.FC = ({ description, connectText, loadingText }) => { - const updateConnectState = useStore((state) => state.updateConnectState) - const connectState = useStore((state) => state.connectState) - const loading = isLoading(connectState) - - const theme = useUserProfileStore((state) => state.theme) - - return ( - updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [''])} - description={description} - connectText={connectText} - loadingText={loadingText} - isLoading={loading} - theme={theme === 'dark' ? 'dark' : 'light'} - /> - ) -} - -export default ConnectWallet diff --git a/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx b/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx index 5aee991df..463f5dbdb 100644 --- a/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx +++ b/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx @@ -36,6 +36,7 @@ import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps, HealthMode } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const LoanCreate = ({ isLeverage = false, ...pageProps }: PageContentProps & { isLeverage?: boolean }) => { const { rChainId, rOwmId, isLoaded, api, market, userActiveKey } = pageProps @@ -56,7 +57,7 @@ const LoanCreate = ({ isLeverage = false, ...pageProps }: PageContentProps & { i const maxRecv = useStore((state) => state.loanCreate.maxRecv[activeKeyMax]) const userDetails = useStore((state) => state.user.loansDetailsMapper[userActiveKey]?.details) const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const refetchMaxRecv = useStore((state) => state.loanCreate.refetchMaxRecv) const fetchStepApprove = useStore((state) => state.loanCreate.fetchStepApprove) const fetchStepCreate = useStore((state) => state.loanCreate.fetchStepCreate) diff --git a/apps/main/src/lend/components/PageLoanCreate/Page.tsx b/apps/main/src/lend/components/PageLoanCreate/Page.tsx index bcae278af..a2020f184 100644 --- a/apps/main/src/lend/components/PageLoanCreate/Page.tsx +++ b/apps/main/src/lend/components/PageLoanCreate/Page.tsx @@ -33,11 +33,13 @@ import { } from '@ui/Chart/styles' import Box from '@ui/Box' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' -import ConnectWallet from '@/lend/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useOneWayMarket } from '@/lend/entities/chain' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -55,7 +57,9 @@ const Page: NextPage = () => { const fetchUserMarketBalances = useStore((state) => state.user.fetchUserMarketBalances) const fetchUserLoanExists = useStore((state) => state.user.fetchUserLoanExists) const { chartExpanded, setChartExpanded } = useStore((state) => state.ohlcCharts) - const provider = useStore((state) => state.wallet.getProvider('')) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) + const provider = useWalletStore((s) => s.provider) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) @@ -186,10 +190,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> )} diff --git a/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx index 091624e97..eb7131a6e 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx @@ -33,6 +33,7 @@ import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps, HealthMode } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const LoanBorrowMore = ({ rChainId, @@ -59,7 +60,7 @@ const LoanBorrowMore = ({ const fetchStepApprove = useStore((state) => state.loanBorrowMore.fetchStepApprove) const fetchStepIncrease = useStore((state) => state.loanBorrowMore.fetchStepIncrease) const refetchMaxRecv = useStore((state) => state.loanBorrowMore.refetchMaxRecv) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanBorrowMore.setFormValues) const resetState = useStore((state) => state.loanBorrowMore.resetState) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx index 2e2941f51..6f129fbf4 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx @@ -1,10 +1,8 @@ import type { FormStatus, FormValues, StepKey } from '@/lend/components/PageLoanManage/LoanCollateralAdd/types' import type { FormEstGas } from '@/lend/components/PageLoanManage/types' import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' - import { DEFAULT_HEALTH_MODE } from '@/lend/components/PageLoanManage/utils' import { DEFAULT_FORM_VALUES } from '@/lend/store/createLoanCollateralAddSlice' import { NOFITY_MESSAGE } from '@/lend/constants' @@ -14,7 +12,6 @@ import { getActiveStep } from '@ui/Stepper/helpers' import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -29,6 +26,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' +import { useWalletStore } from '@ui-kit/features/connect-wallet' import { Api, PageContentProps } from '@/lend/types/lend.types' const LoanCollateralAdd = ({ rChainId, rOwmId, api, isLoaded, market, userActiveKey }: PageContentProps) => { @@ -44,7 +42,7 @@ const LoanCollateralAdd = ({ rChainId, rOwmId, api, isLoaded, market, userActive const userDetails = useStore((state) => state.user.loansDetailsMapper[userActiveKey]?.details) const fetchStepApprove = useStore((state) => state.loanCollateralAdd.fetchStepApprove) const fetchStepIncrease = useStore((state) => state.loanCollateralAdd.fetchStepIncrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanCollateralAdd.setFormValues) const resetState = useStore((state) => state.loanCollateralAdd.resetState) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx index b5eab8e4d..faaa38f68 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx @@ -1,10 +1,8 @@ import type { FormStatus, FormValues, StepKey } from '@/lend/components/PageLoanManage/LoanCollateralRemove/types' import type { FormEstGas } from '@/lend/components/PageLoanManage/types' import type { Step } from '@ui/Stepper/types' - import { t } from '@lingui/macro' import React, { useCallback, useEffect, useRef, useState } from 'react' - import { DEFAULT_CONFIRM_WARNING, DEFAULT_HEALTH_MODE } from '@/lend/components/PageLoanManage/utils' import { DEFAULT_FORM_VALUES } from '@/lend/store/createLoanCollateralRemoveSlice' import { NOFITY_MESSAGE } from '@/lend/constants' @@ -14,7 +12,6 @@ import { formatNumber } from '@ui/utils' import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -30,7 +27,8 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' -import { Api, PageContentProps, HealthMode } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { Api, HealthMode, PageContentProps } from '@/lend/types/lend.types' const LoanCollateralRemove = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) @@ -45,7 +43,7 @@ const LoanCollateralRemove = ({ rChainId, rOwmId, isLoaded, api, market, userAct const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) const userDetails = useStore((state) => state.user.loansDetailsMapper[userActiveKey]?.details) const fetchStepDecrease = useStore((state) => state.loanCollateralRemove.fetchStepDecrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanCollateralRemove.setFormValues) const resetState = useStore((state) => state.loanCollateralRemove.resetState) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx index a0d69624a..4822a7f93 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx @@ -36,6 +36,7 @@ import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps, HealthMode } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const LoanRepay = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) @@ -54,7 +55,7 @@ const LoanRepay = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: P const fetchStepApprove = useStore((state) => state.loanRepay.fetchStepApprove) const fetchStepRepay = useStore((state) => state.loanRepay.fetchStepRepay) const fetchAllUserDetails = useStore((state) => state.user.fetchAll) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanRepay.setFormValues) const resetState = useStore((state) => state.loanRepay.resetState) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx index b362eab0d..f9e6f57ec 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx @@ -32,6 +32,7 @@ import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps, UserLoanState } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const LoanSelfLiquidation = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) @@ -47,7 +48,7 @@ const LoanSelfLiquidation = ({ rChainId, rOwmId, isLoaded, api, market, userActi const fetchDetails = useStore((state) => state.loanSelfLiquidation.fetchDetails) const fetchStepApprove = useStore((state) => state.loanSelfLiquidation.fetchStepApprove) const fetchStepLiquidate = useStore((state) => state.loanSelfLiquidation.fetchStepLiquidate) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const resetState = useStore((state) => state.loanSelfLiquidation.resetState) const maxSlippage = useUserProfileStore((state) => state.maxSlippage.global) diff --git a/apps/main/src/lend/components/PageLoanManage/Page.tsx b/apps/main/src/lend/components/PageLoanManage/Page.tsx index 1edc36dba..262d54e2c 100644 --- a/apps/main/src/lend/components/PageLoanManage/Page.tsx +++ b/apps/main/src/lend/components/PageLoanManage/Page.tsx @@ -37,11 +37,13 @@ import { ExpandIcon, } from '@ui/Chart/styles' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' -import ConnectWallet from '@/lend/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import { useOneWayMarket } from '@/lend/entities/chain' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -62,8 +64,10 @@ const Page: NextPage = () => { const fetchUserLoanExists = useStore((state) => state.user.fetchUserLoanExists) const fetchAllUserMarketDetails = useStore((state) => state.user.fetchAll) const setMarketsStateKey = useStore((state) => state.markets.setStateByKey) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) const { chartExpanded, setChartExpanded } = useStore((state) => state.ohlcCharts) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) @@ -220,10 +224,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> )} diff --git a/apps/main/src/lend/components/PageMarketList/Page.tsx b/apps/main/src/lend/components/PageMarketList/Page.tsx index 0b7253254..e88925dca 100644 --- a/apps/main/src/lend/components/PageMarketList/Page.tsx +++ b/apps/main/src/lend/components/PageMarketList/Page.tsx @@ -18,8 +18,9 @@ import { AppPageContainer } from '@ui/AppPage' import DocumentHead from '@/lend/layout/DocumentHead' import MarketList from '@/lend/components/PageMarketList/index' import Settings from '@/lend/layout/Settings' -import ConnectWallet from '@/lend/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import Box from '@ui/Box' +import { isLoading } from '@ui/utils' enum SEARCH { filter = 'filter', @@ -42,7 +43,9 @@ const Page: NextPage = () => { const isLoadingApi = useStore((state) => state.isLoadingApi) const setStateByKey = useStore((state) => state.marketList.setStateByKey) - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) const [loaded, setLoaded] = useState(false) const [parsedSearchParams, setParsedSearchParams] = useState(null) @@ -137,10 +140,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/lend/components/PageVault/Page.tsx b/apps/main/src/lend/components/PageVault/Page.tsx index 1637f8192..6e6ccd14c 100644 --- a/apps/main/src/lend/components/PageVault/Page.tsx +++ b/apps/main/src/lend/components/PageVault/Page.tsx @@ -29,11 +29,12 @@ import Tabs, { Tab } from '@ui/Tab' import Vault from '@/lend/components/PageVault/index' import Box from '@ui/Box' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' -import ConnectWallet from '@/lend/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { useOneWayMarket } from '@/lend/entities/chain' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Api, PageContentProps } from '@/lend/types/lend.types' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -53,7 +54,9 @@ const Page: NextPage = () => { const fetchUserLoanExists = useStore((state) => state.user.fetchUserLoanExists) const fetchUserMarketBalances = useStore((state) => state.user.fetchUserMarketBalances) const setMarketsStateKey = useStore((state) => state.markets.setStateByKey) - const provider = useStore((state) => state.wallet.getProvider('')) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) + const provider = useWalletStore((s) => s.provider) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) @@ -176,10 +179,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> )} diff --git a/apps/main/src/lend/components/PageVault/VaultClaim/index.tsx b/apps/main/src/lend/components/PageVault/VaultClaim/index.tsx index 03c516af0..2f8c6f577 100644 --- a/apps/main/src/lend/components/PageVault/VaultClaim/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultClaim/index.tsx @@ -1,6 +1,5 @@ -import type { FormStatus } from '@/lend/components/PageVault/VaultClaim/types' +import type { FormStatus, RewardType } from '@/lend/components/PageVault/VaultClaim/types' import type { Step } from '@ui/Stepper/types' -import type { RewardType } from '@/lend/components/PageVault/VaultClaim/types' import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' @@ -22,7 +21,8 @@ import Stats from '@ui/Stats' import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' -import { Api, PageContentProps, MarketClaimable } from '@/lend/types/lend.types' +import { Api, MarketClaimable, PageContentProps } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const VaultClaim = ({ isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) @@ -30,7 +30,7 @@ const VaultClaim = ({ isLoaded, api, market, userActiveKey }: PageContentProps) const formStatus = useStore((state) => state.vaultClaim.formStatus) const claimable = useStore((state) => state.vaultClaim.claimable[userActiveKey]) const fetchStepClaim = useStore((state) => state.vaultClaim.fetchStepClaim) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.vaultClaim.setFormValues) const resetState = useStore((state) => state.vaultClaim.resetState) diff --git a/apps/main/src/lend/components/PageVault/VaultDepositMint/index.tsx b/apps/main/src/lend/components/PageVault/VaultDepositMint/index.tsx index 700f6ec72..eb5c7b809 100644 --- a/apps/main/src/lend/components/PageVault/VaultDepositMint/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultDepositMint/index.tsx @@ -1,9 +1,8 @@ -import type { FormValues, FormStatus, StepKey } from '@/lend/components/PageVault/VaultDepositMint/types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import type { FormStatus, FormValues, StepKey } from '@/lend/components/PageVault/VaultDepositMint/types' import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' - import { _getMaxActiveKey, _isDeposit } from '@/lend/store/createVaultDepositMintSlice' import { formatNumber } from '@ui/utils' import { getActiveStep } from '@ui/Stepper/helpers' @@ -11,7 +10,6 @@ import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' import useMarketAlert from '@/lend/hooks/useMarketAlert' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -41,7 +39,7 @@ const VaultDepositMint = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) const fetchStepApprove = useStore((state) => state.vaultDepositMint.fetchStepApprove) const fetchStepDepositMint = useStore((state) => state.vaultDepositMint.fetchStepDepositMint) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.vaultDepositMint.setFormValues) const resetState = useStore((state) => state.vaultDepositMint.resetState) diff --git a/apps/main/src/lend/components/PageVault/VaultStake/index.tsx b/apps/main/src/lend/components/PageVault/VaultStake/index.tsx index 781326925..a9881f2d0 100644 --- a/apps/main/src/lend/components/PageVault/VaultStake/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultStake/index.tsx @@ -1,15 +1,13 @@ -import type { FormValues, FormStatus, StepKey } from '@/lend/components/PageVault/VaultStake/types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import type { FormStatus, FormValues, StepKey } from '@/lend/components/PageVault/VaultStake/types' import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' - import { formatNumber } from '@ui/utils' import { getActiveStep } from '@ui/Stepper/helpers' import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -34,7 +32,7 @@ const VaultStake = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, userAc const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) const fetchStepApprove = useStore((state) => state.vaultStake.fetchStepApprove) const fetchStepStake = useStore((state) => state.vaultStake.fetchStepStake) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.vaultStake.setFormValues) const resetState = useStore((state) => state.vaultStake.resetState) diff --git a/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx b/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx index 0e743ad6f..54092e309 100644 --- a/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx @@ -1,15 +1,12 @@ import type { FormValues, FormStatus, StepKey } from '@/lend/components/PageVault/VaultUnstake/types' import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' - import { formatNumber } from '@ui/utils' import { getActiveStep } from '@ui/Stepper/helpers' import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -22,6 +19,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api, PageContentProps } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const VaultUnstake = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) @@ -32,7 +30,7 @@ const VaultUnstake = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, user const formValues = useStore((state) => state.vaultUnstake.formValues) const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) const fetchStepUnstake = useStore((state) => state.vaultUnstake.fetchStepUnstake) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.vaultUnstake.setFormValues) const resetState = useStore((state) => state.vaultUnstake.resetState) diff --git a/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx b/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx index fe82f9409..e20d65d3b 100644 --- a/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx @@ -1,9 +1,8 @@ +import { useWalletStore } from '@ui-kit/features/connect-wallet' import type { FormStatus, FormValues, StepKey } from '@/lend/components/PageVault/VaultWithdrawRedeem/types' import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' - import { formatNumber } from '@ui/utils' import { getActiveStep } from '@ui/Stepper/helpers' import { helpers } from '@/lend/lib/apiLending' @@ -11,7 +10,6 @@ import { _getMaxActiveKey } from '@/lend/store/createVaultDepositMintSlice' import { _isWithdraw } from '@/lend/store/createVaultWithdrawRedeemSlice' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/lend/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/lend/components/AlertFormError' @@ -48,7 +46,7 @@ const VaultWithdrawRedeem = ({ const userBalances = useStore((state) => state.user.marketsBalancesMapper[userActiveKey]) const fetchStepWithdrawRedeem = useStore((state) => state.vaultWithdrawRedeem.fetchStepWithdrawRedeem) const fetchUserMarketBalances = useStore((state) => state.user.fetchUserMarketBalances) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.vaultWithdrawRedeem.setFormValues) const resetState = useStore((state) => state.vaultWithdrawRedeem.resetState) diff --git a/apps/main/src/lend/constants.ts b/apps/main/src/lend/constants.ts index 3f17ac77e..5748cf4d6 100644 --- a/apps/main/src/lend/constants.ts +++ b/apps/main/src/lend/constants.ts @@ -1,4 +1,5 @@ import { LEND_ROUTES } from '@ui-kit/shared/routes' +export { CONNECT_STAGE } from '@ui/utils/utilsConnectState' export const INVALID_ADDRESS = '0x0000000000000000000000000000000000000000' export const NETWORK_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' @@ -22,13 +23,6 @@ export const REFRESH_INTERVAL = { '11m': 660000, } -export const CONNECT_STAGE = { - CONNECT_API: 'api', - CONNECT_WALLET: 'connect-wallet', - DISCONNECT_WALLET: 'disconnect-wallet', - SWITCH_NETWORK: 'switch-network', -} as const - // TODO: translation export const NOFITY_MESSAGE = { pendingConfirm: 'Pending wallet confirmation.', diff --git a/apps/main/src/lend/hooks/useContract.ts b/apps/main/src/lend/hooks/useContract.ts index b81a1e483..b3f54d133 100644 --- a/apps/main/src/lend/hooks/useContract.ts +++ b/apps/main/src/lend/hooks/useContract.ts @@ -1,9 +1,8 @@ import { Contract, Interface, JsonRpcProvider } from 'ethers' import { useCallback, useEffect, useState } from 'react' - import networks from '@/lend/networks' -import useStore from '@/lend/store/useStore' import { ChainId, Provider } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const useAbiGaugeTotalSupply = ( rChainId: ChainId, @@ -11,7 +10,7 @@ const useAbiGaugeTotalSupply = ( jsonModuleName: string, contractAddress: string | undefined, ) => { - const getProvider = useStore((state) => state.wallet.getProvider) + const walletProvider = useWalletStore((s) => s.provider) const [contract, setContract] = useState(null) @@ -38,14 +37,14 @@ const useAbiGaugeTotalSupply = ( useEffect(() => { if (rChainId) { const provider = signerRequired - ? getProvider('') - : getProvider('') || new JsonRpcProvider(networks[rChainId].rpcUrl) + ? walletProvider + : walletProvider || new JsonRpcProvider(networks[rChainId].rpcUrl) if (jsonModuleName && contractAddress && provider) { ;(async () => setContract(await getContract(jsonModuleName, contractAddress, provider)))() } } - }, [contractAddress, getContract, getProvider, jsonModuleName, rChainId, signerRequired]) + }, [contractAddress, getContract, walletProvider, jsonModuleName, rChainId, signerRequired]) return contract } diff --git a/apps/main/src/lend/hooks/usePageOnMount.ts b/apps/main/src/lend/hooks/usePageOnMount.ts index 18a5f94f2..2873ed8ca 100644 --- a/apps/main/src/lend/hooks/usePageOnMount.ts +++ b/apps/main/src/lend/hooks/usePageOnMount.ts @@ -2,7 +2,6 @@ import type { Location, NavigateFunction, Params } from 'react-router' import type { ConnectState } from '@ui/utils' import { isFailure, isLoading, isSuccess } from '@ui/utils' import type { INetworkName } from '@curvefi/lending-api/lib/interfaces' - import { ethers } from 'ethers' import { useCallback, useEffect } from 'react' import { @@ -11,11 +10,10 @@ import { useConnectWallet, useSetChain, useSetLocale, + useWalletStore, } from '@ui-kit/features/connect-wallet' - import { CONNECT_STAGE, REFRESH_INTERVAL, ROUTE } from '@/lend/constants' import { dynamicActivate, updateAppLocale } from '@ui-kit/lib/i18n' -import { getStorageValue, setStorageValue } from '@/lend/utils/utilsStorage' import { getNetworkFromUrl, parseParams } from '@/lend/utils/utilsRouter' import { helpers } from '@/lend/lib/apiLending' import networks, { networksIdMapper } from '@/lend/networks' @@ -24,19 +22,16 @@ import { useUserProfileStore } from '@ui-kit/features/user-profile' import { ChainId, PageProps, Wallet } from '@/lend/types/lend.types' function usePageOnMount(params: Params, location: Location, navigate: NavigateFunction, chainIdNotRequired?: boolean) { - const [{ wallet }, connect, disconnect] = useConnectWallet() + const { wallet, connect, disconnect, walletName, setWalletName } = useConnectWallet() const [_, setChain] = useSetChain() const updateWalletLocale = useSetLocale() - const api = useStore((state) => state.api) const connectState = useStore((state) => state.connectState) const updateConnectState = useStore((state) => state.updateConnectState) const updateApi = useStore((state) => state.updateApi) - const updateProvider = useStore((state) => state.wallet.updateProvider) + const chooseWallet = useWalletStore((s) => s.chooseWallet) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) - const setLocale = useUserProfileStore((state) => state.setLocale) - const walletChainId = getWalletChainId(wallet) const walletSignerAddress = getWalletSignerAddress(wallet) const parsedParams = parseParams(params, chainIdNotRequired) @@ -46,7 +41,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu if (options) { try { const [chainId, useWallet] = options - await updateProvider(wallet) + await chooseWallet(wallet) const prevApi = api ?? null updateGlobalStoreByKey('isLoadingApi', true) updateGlobalStoreByKey('isLoadingCurve', true) // remove -> use connectState @@ -63,14 +58,14 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } }, - [updateProvider, wallet, api, updateGlobalStoreByKey, updateApi, updateConnectState], + [chooseWallet, wallet, api, updateGlobalStoreByKey, updateApi, updateConnectState], ) const handleConnectWallet = useCallback( async (options: ConnectState['options']) => { if (options) { const [walletName] = options - let walletState: Wallet | null = null + let walletState: Wallet | null if (walletName) { // If found label in localstorage, after 30s if not connected, reconnect with modal @@ -95,7 +90,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu walletState = walletStates[0] } catch (error) { // if failed to get walletState due to timeout, show connect modal. - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) ;[walletState] = await connect() } } else { @@ -104,7 +99,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu try { if (!walletState) throw new Error('unable to connect') - setStorageValue('APP_CACHE', { walletName: walletState.label, timestamp: Date.now().toString() }) + setWalletName(walletState.label) const walletChainId = getWalletChainId(walletState) if (walletChainId && walletChainId !== parsedParams.rChainId) { const success = await setChain({ chainId: ethers.toQuantity(parsedParams.rChainId) }) @@ -124,24 +119,24 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } catch (error) { updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) } } }, - [connect, navigate, parsedParams, setChain, updateConnectState], + [connect, navigate, parsedParams, setChain, updateConnectState, setWalletName], ) const handleDisconnectWallet = useCallback( async (wallet: Wallet) => { try { await disconnect(wallet) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) } catch (error) { console.error(error) } }, - [disconnect, parsedParams.rChainId, updateConnectState], + [disconnect, parsedParams.rChainId, updateConnectState, setWalletName], ) const handleNetworkSwitch = useCallback( @@ -184,7 +179,6 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu navigate(`${parsedParams.rLocalePathname}/ethereum${ROUTE.PAGE_MARKETS}`) } else { updateGlobalStoreByKey('routerProps', { params, location, navigate }) - const walletName = getStorageValue('APP_CACHE')?.walletName ?? '' if (walletName) { updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [walletName]) } else { diff --git a/apps/main/src/lend/layout/Header.tsx b/apps/main/src/lend/layout/Header.tsx index 6dbef465f..805d33268 100644 --- a/apps/main/src/lend/layout/Header.tsx +++ b/apps/main/src/lend/layout/Header.tsx @@ -21,7 +21,7 @@ type HeaderProps = { chainId: ChainId; sections: NavigationSection[]; BannerProp const isMdUpQuery = (theme: Theme) => theme.breakpoints.up('tablet') const Header = ({ chainId, sections, BannerProps }: HeaderProps) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const navigate = useNavigate() const mainNavRef = useRef(null) const bannerHeight = useStore((state) => state.layout.height.globalAlert) diff --git a/apps/main/src/lend/layout/index.tsx b/apps/main/src/lend/layout/index.tsx index edaaab886..ef8ed9d25 100644 --- a/apps/main/src/lend/layout/index.tsx +++ b/apps/main/src/lend/layout/index.tsx @@ -15,7 +15,7 @@ import { Locale } from '@ui-kit/widgets/Header/types' import { useUserProfileStore } from '@ui-kit/features/user-profile' const BaseLayout = ({ children }: { children: React.ReactNode }) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const globalAlertRef = useRef(null) const elHeight = useHeightResizeObserver(globalAlertRef) const footerRef = useRef(null) diff --git a/apps/main/src/lend/store/createAppSlice.ts b/apps/main/src/lend/store/createAppSlice.ts index 08291ec4a..87f81b0e9 100644 --- a/apps/main/src/lend/store/createAppSlice.ts +++ b/apps/main/src/lend/store/createAppSlice.ts @@ -1,6 +1,6 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/lend/store/useStore' -import type { ConnectState } from '@ui/utils' +import { CONNECT_STAGE, ConnectState } from '@ui/utils' import produce from 'immer' import { log } from '@ui-kit/lib/logging' import isEqual from 'lodash/isEqual' @@ -12,8 +12,8 @@ export type SliceKey = keyof State | '' export type StateKey = string type SliceState = { - connectState: ConnectState api: Api | null + connectState: ConnectState isLoadingApi: boolean isLoadingCurve: true isMobile: boolean @@ -24,7 +24,7 @@ type SliceState = { // prettier-ignore export interface AppSlice extends SliceState { - updateConnectState(status: ConnectState['status'], stage: ConnectState['stage'], options?: ConnectState['options']): void + updateConnectState(status?: ConnectState['status'], stage?: ConnectState['stage'], options?: ConnectState['options']): void updateApi(api: Api, prevApi: Api | null, wallet: Wallet | null): Promise updateGlobalStoreByKey(key: DefaultStateKeys, value: T): void setAppStateByActiveKey(sliceKey: SliceKey, key: StateKey, activeKey: string, value: T, showLog?: boolean): void @@ -34,8 +34,8 @@ export interface AppSlice extends SliceState { } const DEFAULT_STATE: SliceState = { - connectState: { status: '' as const, stage: '' }, api: null, + connectState: { status: '', stage: '' }, isLoadingApi: true, isLoadingCurve: true, isMobile: false, @@ -47,9 +47,8 @@ const DEFAULT_STATE: SliceState = { const createAppSlice = (set: SetState, get: GetState): AppSlice => ({ ...DEFAULT_STATE, - updateConnectState: (status, stage, options) => { - const value = options ? { status, stage, options } : { status, stage } - get().updateGlobalStoreByKey('connectState', value) + updateConnectState: (status = 'loading', stage = CONNECT_STAGE.CONNECT_WALLET, options = ['']) => { + set({ connectState: { status, stage, ...(options && { options }) } }) }, updateApi: async (api, prevApi, wallet) => { const isNetworkSwitched = !!prevApi?.chainId && prevApi.chainId !== api.chainId diff --git a/apps/main/src/lend/store/createGasSlice.ts b/apps/main/src/lend/store/createGasSlice.ts index 653de4d19..14debbed8 100644 --- a/apps/main/src/lend/store/createGasSlice.ts +++ b/apps/main/src/lend/store/createGasSlice.ts @@ -1,15 +1,14 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/lend/store/useStore' import type { GasInfo } from '@/lend/store/types' - import cloneDeep from 'lodash/cloneDeep' - import { getEthereumCustomFeeDataValues } from '@ui/utils/utilsGas' import { gweiToWai } from '@ui-kit/utils' import { httpFetcher, log } from '@/lend/utils/helpers' import lendingApi from '@/lend/lib/apiLending' import networks from '@/lend/networks' import { Api, Provider } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -85,7 +84,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => } } else if (chainId === 42161) { // Arbitrum custom fee data - const provider = get().wallet.provider + const { provider } = useWalletStore.getState() if (provider) { const { customFeeData } = await lendingApi.helpers.fetchCustomGasFees(curve) @@ -99,7 +98,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => } } else if (chainId === 10) { // Optimism - const provider = get().wallet.getProvider('') + const { provider } = useWalletStore.getState() if (provider) { parsedGasInfo = await parseGasInfo(curve, provider) @@ -116,7 +115,7 @@ const createGasSlice = (set: SetState, get: GetState): GasSlice => if (parsedGasInfo) { get()[sliceKey].setStateByKeys(parsedGasInfo) } else { - const provider = get().wallet.provider + const { provider } = useWalletStore.getState() if (provider && chainId) { const parsedGasInfo = await parseGasInfo(curve, provider) if (parsedGasInfo) { diff --git a/apps/main/src/lend/store/createLoanBorrowMoreSlice.ts b/apps/main/src/lend/store/createLoanBorrowMoreSlice.ts index b25af8a58..edfc7ef10 100644 --- a/apps/main/src/lend/store/createLoanBorrowMoreSlice.ts +++ b/apps/main/src/lend/store/createLoanBorrowMoreSlice.ts @@ -19,6 +19,8 @@ import { _parseActiveKey } from '@/lend/utils/helpers' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -238,11 +240,10 @@ const createLoanBorrowMore = (_: SetState, get: GetState): LoanBor // steps fetchStepApprove: async (activeKey, api, market, formValues, maxSlippage, isLeverage) => { - const { gas, wallet } = get() + const { gas } = get() const sliceState = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { ...DEFAULT_FORM_STATUS, isInProgress: true, step: 'APPROVAL' }) @@ -272,11 +273,11 @@ const createLoanBorrowMore = (_: SetState, get: GetState): LoanBor } }, fetchStepIncrease: async (activeKey, api, market, formValues, maxSlippage, isLeverage) => { - const { gas, markets, wallet, user } = get() + const { gas, markets, user } = get() const { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider) return + if (!provider) return setMissingProvider(get()[sliceKey]) // loading sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createLoanCollateralAddSlice.ts b/apps/main/src/lend/store/createLoanCollateralAddSlice.ts index 52c24260a..092724e89 100644 --- a/apps/main/src/lend/store/createLoanCollateralAddSlice.ts +++ b/apps/main/src/lend/store/createLoanCollateralAddSlice.ts @@ -10,6 +10,8 @@ import { _parseActiveKey } from '@/lend/utils/helpers' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -125,11 +127,11 @@ const createLoanCollateralAdd = (_: SetState, get: GetState): Loan // step fetchStepApprove: async (activeKey, api, market, formValues) => { - const { gas, wallet } = get() + const { gas } = get() const sliceState = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider) return + if (!provider) return setMissingProvider(get()[sliceKey]) // loading state sliceState.setStateByKey('formStatus', { ...DEFAULT_FORM_STATUS, isInProgress: true, step: 'APPROVAL' }) @@ -151,11 +153,11 @@ const createLoanCollateralAdd = (_: SetState, get: GetState): Loan } }, fetchStepIncrease: async (activeKey, api, market, formValues) => { - const { gas, markets, wallet, user } = get() + const { gas, markets, user } = get() const sliceState = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider) return + if (!provider) return setMissingProvider(sliceState) // loading sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createLoanCollateralRemoveSlice.ts b/apps/main/src/lend/store/createLoanCollateralRemoveSlice.ts index 9eb73425b..538f2b99b 100644 --- a/apps/main/src/lend/store/createLoanCollateralRemoveSlice.ts +++ b/apps/main/src/lend/store/createLoanCollateralRemoveSlice.ts @@ -10,6 +10,8 @@ import { _parseActiveKey } from '@/lend/utils/helpers' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -138,11 +140,11 @@ const createLoanCollateralRemove = (_: SetState, get: GetState): L // steps fetchStepDecrease: async (activeKey, api, market) => { - const { gas, markets, wallet, user } = get() + const { gas, markets, user } = get() const { formStatus, formValues, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider) return + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createLoanCreateSlice.ts b/apps/main/src/lend/store/createLoanCreateSlice.ts index db4ecf7ec..07c4e2fa4 100644 --- a/apps/main/src/lend/store/createLoanCreateSlice.ts +++ b/apps/main/src/lend/store/createLoanCreateSlice.ts @@ -17,6 +17,8 @@ import { _parseActiveKey } from '@/lend/utils/helpers' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -258,11 +260,11 @@ const createLoanCreate = (set: SetState, get: GetState): LoanCreat // steps fetchStepApprove: async (activeKey, api, market, maxSlippage, formValues, isLeverage) => { - const { gas, wallet } = get() + const { gas } = get() const { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider) return + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { ...DEFAULT_FORM_STATUS, isInProgress: true, step: 'APPROVAL' }) @@ -292,12 +294,13 @@ const createLoanCreate = (set: SetState, get: GetState): LoanCreat } }, fetchStepCreate: async (activeKey, api, market, maxSlippage, formValues, isLeverage) => { - const { gas, markets, wallet, user } = get() + const { gas, markets, user } = get() const { formStatus, ...sliceState } = get()[sliceKey] const { userCollateral, userBorrowed, debt, n } = formValues - const provider = wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() - if (!provider || n === null) return + if (!provider) return setMissingProvider(get()[sliceKey]) + if (n === null) return // update formStatus sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createLoanRepaySlice.ts b/apps/main/src/lend/store/createLoanRepaySlice.ts index a994f834c..ed80196aa 100644 --- a/apps/main/src/lend/store/createLoanRepaySlice.ts +++ b/apps/main/src/lend/store/createLoanRepaySlice.ts @@ -16,6 +16,8 @@ import { _parseActiveKey } from '@/lend/utils/helpers' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api, UserLoanState } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -196,11 +198,10 @@ const createLoanRepaySlice = (set: SetState, get: GetState): LoanR // steps fetchStepApprove: async (activeKey, api, market, formValues, maxSlippage) => { - const { gas, wallet } = get() + const { gas } = get() const sliceState = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { ...DEFAULT_FORM_STATUS, isInProgress: true, step: 'APPROVAL' }) @@ -233,11 +234,10 @@ const createLoanRepaySlice = (set: SetState, get: GetState): LoanR } }, fetchStepRepay: async (activeKey, api, market, formValues, maxSlippage) => { - const { gas, markets, user, wallet } = get() + const { gas, markets, user } = get() const { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createLoanSelfLiquidationSlice.ts b/apps/main/src/lend/store/createLoanSelfLiquidationSlice.ts index 0332fc2cc..5286b8228 100644 --- a/apps/main/src/lend/store/createLoanSelfLiquidationSlice.ts +++ b/apps/main/src/lend/store/createLoanSelfLiquidationSlice.ts @@ -11,6 +11,8 @@ import { DEFAULT_FORM_EST_GAS, DEFAULT_FORM_STATUS as FORM_STATUS } from '@/lend import apiLending from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api, FutureRates } from '@/lend/types/lend.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -129,11 +131,11 @@ const createLoanSelfLiquidationSlice = (set: SetState, get: GetState { - const { gas, wallet } = get() + const { gas } = get() const { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { ...DEFAULT_FORM_STATUS, isInProgress: true, step: 'APPROVAL' }) @@ -155,11 +157,11 @@ const createLoanSelfLiquidationSlice = (set: SetState, get: GetState { - const { gas, markets, wallet, user } = get() + const { gas, markets, user } = get() const { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider(sliceKey) - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus sliceState.setStateByKey('formStatus', { diff --git a/apps/main/src/lend/store/createVaultClaimSlice.ts b/apps/main/src/lend/store/createVaultClaimSlice.ts index 0732de384..48adb7052 100644 --- a/apps/main/src/lend/store/createVaultClaimSlice.ts +++ b/apps/main/src/lend/store/createVaultClaimSlice.ts @@ -10,6 +10,7 @@ import { DEFAULT_FORM_STATUS } from '@/lend/components/PageVault/VaultClaim/util import apiLending from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { Api, MarketClaimable } from '@/lend/types/lend.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -73,9 +74,8 @@ const createVaultClaim = (set: SetState, get: GetState): VaultClai // steps fetchStepClaim: async (userActiveKey, api, market, type) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const step = type === 'crv' ? 'CLAIM_CRV' : 'CLAIM_REWARDS' diff --git a/apps/main/src/lend/store/createVaultDepositMintSlice.ts b/apps/main/src/lend/store/createVaultDepositMintSlice.ts index 8a695b28c..6749eb504 100644 --- a/apps/main/src/lend/store/createVaultDepositMintSlice.ts +++ b/apps/main/src/lend/store/createVaultDepositMintSlice.ts @@ -11,6 +11,7 @@ import { DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES } from '@/lend/components/Page import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api, FutureRates } from '@/lend/types/lend.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE type FormType = string | null @@ -136,9 +137,8 @@ const createVaultMint = (set: SetState, get: GetState): VaultDepos // steps fetchStepApprove: async (activeKey, formType, api, market, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'APPROVAL' } @@ -162,9 +162,8 @@ const createVaultMint = (set: SetState, get: GetState): VaultDepos } }, fetchStepDepositMint: async (activeKey, formType, api, market, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'DEPOSIT_MINT' } diff --git a/apps/main/src/lend/store/createVaultStakeSlice.ts b/apps/main/src/lend/store/createVaultStakeSlice.ts index c1692070a..3e6b226d5 100644 --- a/apps/main/src/lend/store/createVaultStakeSlice.ts +++ b/apps/main/src/lend/store/createVaultStakeSlice.ts @@ -11,6 +11,7 @@ import { DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES } from '@/lend/components/Page import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api } from '@/lend/types/lend.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE type FormType = string | null @@ -101,9 +102,8 @@ const createVaultStake = (set: SetState, get: GetState): VaultStak // steps fetchStepApprove: async (activeKey, formType, api, market, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'APPROVAL' } @@ -126,9 +126,8 @@ const createVaultStake = (set: SetState, get: GetState): VaultStak } }, fetchStepStake: async (activeKey, formType, api, market, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'STAKE' } diff --git a/apps/main/src/lend/store/createVaultUnstakeSlice.ts b/apps/main/src/lend/store/createVaultUnstakeSlice.ts index 866b49f95..ea7783a92 100644 --- a/apps/main/src/lend/store/createVaultUnstakeSlice.ts +++ b/apps/main/src/lend/store/createVaultUnstakeSlice.ts @@ -11,6 +11,7 @@ import { DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES } from '@/lend/components/Page import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api } from '@/lend/types/lend.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE type FormType = string | null @@ -99,9 +100,8 @@ const createVaultUnstake = (set: SetState, get: GetState): VaultUn // steps fetchStepUnstake: async (activeKey, formType, api, market, formValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'UNSTAKE' } diff --git a/apps/main/src/lend/store/createVaultWithdrawRedeemSlice.ts b/apps/main/src/lend/store/createVaultWithdrawRedeemSlice.ts index 4d63eae15..50568e282 100644 --- a/apps/main/src/lend/store/createVaultWithdrawRedeemSlice.ts +++ b/apps/main/src/lend/store/createVaultWithdrawRedeemSlice.ts @@ -12,6 +12,7 @@ import { _getMaxActiveKey } from '@/lend/store/createVaultDepositMintSlice' import apiLending, { helpers } from '@/lend/lib/apiLending' import { OneWayMarketTemplate } from '@curvefi/lending-api/lib/markets' import { ChainId, Api, FutureRates } from '@/lend/types/lend.types' +import { setMissingProvider, useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE type FormType = string | null @@ -130,9 +131,8 @@ const createVaultWithdrawRedeem = (set: SetState, get: GetState): // steps fetchStepWithdrawRedeem: async (activeKey, formType: FormType, api, market, formValues, vaultShares) => { - const provider = get().wallet.getProvider(sliceKey) - - if (!provider) return + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) // update formStatus const partialFormStatus: Partial = { isInProgress: true, step: 'WITHDRAW_REDEEM' } diff --git a/apps/main/src/lend/store/createWalletSlice.ts b/apps/main/src/lend/store/createWalletSlice.ts deleted file mode 100644 index 115f02c44..000000000 --- a/apps/main/src/lend/store/createWalletSlice.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { GetState, SetState } from 'zustand' -import type { State } from '@/lend/store/useStore' -import type { CustomNotification, NotificationType } from '@web3-onboard/core/dist/types' -import type { OnboardAPI, UpdateNotification } from '@web3-onboard/core' - -import { isError } from 'ethers' -import cloneDeep from 'lodash/cloneDeep' -import { getWalletProvider } from '@ui-kit/features/connect-wallet' -import { Provider, Wallet } from '@/lend/types/lend.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - onboard: OnboardAPI | null - provider: Provider | null -} - -type ProviderSliceKey = - | 'loanCreate' - | 'loanBorrowMore' - | 'loanRepay' - | 'loanSelfLiquidation' - | 'loanCollateralRemove' - | 'loanCollateralAdd' - -const sliceKey = 'wallet' - -// prettier-ignore -export type WalletSlice = { - [sliceKey]: SliceState & { - notifyNotification(message: string, type: NotificationType, autoDismiss?: number): { dismiss: () => void; update?: UpdateNotification } - updateProvider(wallet: Wallet | null): Promise - getProvider(sliceKey: string): Provider | null - - // steps helper - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - onboard: null, - provider: null, -} - -const createWalletSlice = (set: SetState, get: GetState): WalletSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - - notifyNotification: (message: string, type: NotificationType = 'pending', autoDismiss?: number) => { - const onboard = get().wallet.onboard - - if (onboard) { - // see https://onboard.blocknative.com/docs/packages/core#options for all options - const customNotification: CustomNotification = { type, message } - - if (typeof autoDismiss !== 'undefined') { - customNotification.autoDismiss = autoDismiss - } - - return onboard.state.actions.customNotification(customNotification) - } - return { dismiss: () => {} } - }, - updateProvider: async (wallet) => { - try { - const storedProvider = get().wallet.provider - const newProvider = wallet ? getWalletProvider(wallet) : null - if (storedProvider) await storedProvider.removeAllListeners() - get().wallet.setStateByKey('provider', newProvider) - } catch (error) { - if (!isError(error, 'NETWORK_ERROR')) { - console.error(error) - } - } - }, - getProvider: (sliceKey: ProviderSliceKey) => { - const provider = get().wallet.provider - if (!provider) { - const storedFormStatus = get()[sliceKey]?.formStatus - if ( - storedFormStatus && - typeof storedFormStatus === 'object' && - 'step' in storedFormStatus && - 'formProcessing' in storedFormStatus && - 'error' in storedFormStatus - ) { - get()[sliceKey].setStateByKey('formStatus', { - ...storedFormStatus, - step: '', - formProcessing: false, - error: 'error-invalid-provider', - }) - } - } - return provider - }, - - // slice helpers - setStateByActiveKey: (key: StateKey, activeKey: string, value: T) => - get().setAppStateByActiveKey(sliceKey, key, activeKey, value), - setStateByKey: (key: StateKey, value: T) => get().setAppStateByKey(sliceKey, key, value), - setStateByKeys: (sliceState: Partial) => get().setAppStateByKeys(sliceKey, sliceState), - resetState: () => get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)), - }, -}) - -export default createWalletSlice diff --git a/apps/main/src/lend/store/useStore.ts b/apps/main/src/lend/store/useStore.ts index af13b2f22..7448e5820 100644 --- a/apps/main/src/lend/store/useStore.ts +++ b/apps/main/src/lend/store/useStore.ts @@ -7,7 +7,6 @@ import merge from 'lodash/merge' import createCacheSlice, { CacheSlice } from '@/lend/store/createCacheSlice' import createAppSlice, { AppSlice } from '@/lend/store/createAppSlice' import createLayoutSlice, { AppLayoutSlice } from '@/lend/store/createLayoutSlice' -import createWalletSlice, { WalletSlice } from '@/lend/store/createWalletSlice' import createGasSlice, { GasSlice } from '@/lend/store/createGasSlice' import createTokensSlice, { TokensSlice } from '@/lend/store/createTokensSlice' import createMarketsSlice, { MarketsSlice } from '@/lend/store/createMarketsSlice' @@ -35,7 +34,6 @@ import type { PersistOptions } from 'zustand/middleware/persist' export type State = CacheSlice & AppSlice & AppLayoutSlice & - WalletSlice & GasSlice & TokensSlice & IntegrationsSlice & @@ -60,7 +58,6 @@ export type State = CacheSlice & const store = (set: SetState, get: GetState): State => ({ ...createCacheSlice(set, get), ...createAppSlice(set, get), - ...createWalletSlice(set, get), ...createLayoutSlice(set, get), ...createGasSlice(set, get), ...createTokensSlice(set, get), diff --git a/apps/main/src/lend/utils/helpers.ts b/apps/main/src/lend/utils/helpers.ts index ca4503882..f3b00c158 100644 --- a/apps/main/src/lend/utils/helpers.ts +++ b/apps/main/src/lend/utils/helpers.ts @@ -4,7 +4,6 @@ import { Api } from '@/lend/types/lend.types' export * from './utilsGasPrices' export * from './utilsRouter' -export * from './utilsStorage' interface CustomError extends Error { data?: { message: string } diff --git a/apps/main/src/lend/utils/utilsStorage.ts b/apps/main/src/lend/utils/utilsStorage.ts deleted file mode 100644 index 518d2a2f2..000000000 --- a/apps/main/src/lend/utils/utilsStorage.ts +++ /dev/null @@ -1,39 +0,0 @@ -import merge from 'lodash/merge' -import dayjs from '@ui-kit/lib/dayjs' - -export const APP_STORAGE = { - APP_CACHE: 'lend-app-cache', -} - -type Key = keyof typeof APP_STORAGE - -export function getStorageValue(key: Key) { - const storedValue = window.localStorage.getItem(APP_STORAGE[key]) - let parsedStoredValue: { [key: string]: string } = {} - - if (storedValue) { - try { - parsedStoredValue = JSON.parse(storedValue) ?? {} - } catch (error) { - console.error(error) - } - } - - if (key === 'APP_CACHE') { - return { - timestamp: parsedStoredValue.timestamp ?? '', - walletName: getWalletName(parsedStoredValue.walletName, parsedStoredValue.timestamp), - } - } -} - -function getWalletName(walletName: string | undefined, timestamp: string | undefined) { - const isStaled = walletName && timestamp && dayjs().diff(+timestamp, 'days') > 5 - return isStaled || !walletName ? '' : walletName -} - -export function setStorageValue(key: Key, updatedValue: T) { - const storedValue = getStorageValue(key) - const mergedStoredValue = merge(storedValue, updatedValue) - window.localStorage.setItem(APP_STORAGE[key], JSON.stringify(mergedStoredValue)) -} diff --git a/apps/main/src/loan/components/ConnectWallet.tsx b/apps/main/src/loan/components/ConnectWallet.tsx deleted file mode 100644 index c4aafe02e..000000000 --- a/apps/main/src/loan/components/ConnectWallet.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { isLoading } from '@ui/utils' -import useStore from '@/loan/store/useStore' - -import { CONNECT_STAGE } from '@/loan/constants' - -import ConnectWalletPrompt from '@ui/ConnectWalletPrompt' -import { useUserProfileStore } from '@ui-kit/features/user-profile' - -type ConnectWalletProps = { - description: string - connectText: string - loadingText: string -} - -const ConnectWallet: React.FC = ({ description, connectText, loadingText }) => { - const updateConnectState = useStore((state) => state.updateConnectState) - const connectState = useStore((state) => state.connectState) - const loading = isLoading(connectState) - - const theme = useUserProfileStore((state) => state.theme) - - return ( - updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [''])} - description={description} - connectText={connectText} - loadingText={loadingText} - isLoading={loading} - theme={theme === 'dark' ? 'dark' : 'light'} - /> - ) -} - -export default ConnectWallet diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DeployButton.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DeployButton.tsx index b246ea454..6c2a15fcc 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DeployButton.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DeployButton.tsx @@ -1,24 +1,23 @@ import { t } from '@lingui/macro' -import { useCallback, useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import BigNumber from 'bignumber.js' import styled from 'styled-components' import useStore from '@/loan/store/useStore' import Button from '@ui/Button' -import React from 'react' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type DeployButtonProps = { className?: string } const DeployButton: React.FC = ({ className }) => { - const onboardInstance = useStore((state) => state.wallet.onboard) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address + const signerAddress = useWalletStore((s) => s.wallet?.accounts?.[0]?.address) const { approval: depositApproved, fetchStatus: depositFetchStatus } = useStore( (state) => state.scrvusd.depositApproval, ) - const { depositApprove, deposit, withdraw, redeem } = useStore((state) => state.scrvusd.deploy) + const { depositApprove, deposit, redeem } = useStore((state) => state.scrvusd.deploy) const { inputAmount, stakingModule, userBalances, getInputAmountApproved } = useStore((state) => state.scrvusd) const userBalance = useMemo( diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DepositModule.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DepositModule.tsx index 3d6513652..8a33fc53e 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DepositModule.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/DepositModule.tsx @@ -9,18 +9,18 @@ import { RCCrvUSDLogoXS, RCScrvUSDLogoXS } from 'ui/src/images' import Box from '@ui/Box' import { + ErrorText, InputLabel, + InputSelectorText, InputWrapper, SelectorBox, StyledIcon, StyledInputComp, - InputSelectorText, - ErrorText, } from './styles' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const DepositModule = () => { - const onboardInstance = useStore((state) => state.wallet.onboard) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address + const signerAddress = useWalletStore((s) => s.wallet?.accounts?.[0]?.address) const userBalances = useStore((state) => state.scrvusd.userBalances[signerAddress?.toLowerCase() ?? '']) const { inputAmount, preview, setInputAmount, setMax } = useStore((state) => state.scrvusd) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/WithdrawModule.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/WithdrawModule.tsx index 7cac751ee..76a12991d 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/WithdrawModule.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/DepositWithdraw/WithdrawModule.tsx @@ -9,18 +9,18 @@ import { RCCrvUSDLogoXS, RCScrvUSDLogoXS } from 'ui/src/images' import Box from '@ui/Box' import { + ErrorText, InputLabel, + InputSelectorText, InputWrapper, SelectorBox, StyledIcon, StyledInputComp, - InputSelectorText, - ErrorText, } from './styles' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const WithdrawModule: React.FC = () => { - const onboardInstance = useStore((state) => state.wallet.onboard) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address + const signerAddress = useWalletStore((s) => s.wallet?.accounts?.[0]?.address) const userBalances = useStore((state) => state.scrvusd.userBalances[signerAddress?.toLowerCase() ?? '']) const { inputAmount, preview, setInputAmount, setMax } = useStore((state) => state.scrvusd) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/TransactionDetails/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/TransactionDetails/index.tsx index 00603ec57..815a86be2 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/TransactionDetails/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/TransactionDetails/index.tsx @@ -14,6 +14,7 @@ import Loader from '@ui/Loader' import Switch from '@/loan/components/PageCrvUsdStaking/components/Switch' import DetailInfoSlippageTolerance from '@/loan/components/DetailInfoSlippageTolerance' import FieldValue from '@/loan/components/PageCrvUsdStaking/TransactionDetails/FieldValue' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type TransactionDetailsProps = { className?: string @@ -21,13 +22,11 @@ type TransactionDetailsProps = { const TransactionDetails: React.FC = ({ className }) => { const [isOpen, setIsOpen] = useState(false) - const onboardInstance = useStore((state) => state.wallet.onboard) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address - // const maxSlippage = useStore((state) => state.maxSlippage) + const signerAddress = useWalletStore((s) => s.wallet?.accounts?.[0]?.address) const { preview, crvUsdExchangeRate, approveInfinite, setApproveInfinite, stakingModule } = useStore( (state) => state.scrvusd, ) - const { gas, fetchStatus } = useStore((state) => state.scrvusd.estGas) + const fetchStatus = useStore((state) => state.scrvusd.estGas.fetchStatus) const estimateGas = useStore((state) => state.scrvusd.getEstimateGas(signerAddress ?? '')) const { estGasCost, estGasCostUsd, tooltip } = useEstimateGasConversion(estimateGas) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/UserPositionBanner/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/UserPositionBanner/index.tsx index 8a554fc0f..53b2972bc 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/UserPositionBanner/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/UserPositionBanner/index.tsx @@ -12,6 +12,7 @@ import { CRVUSD_ADDRESS } from '@/loan/constants' import Box from '@ui/Box' import Loader from '@ui/Loader' import Tooltip from '@ui/Tooltip' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type UserPositionBannerProps = { className?: string @@ -22,7 +23,7 @@ const UserPositionBanner: React.FC = ({ className, mobi const pricesYieldData = useStore((state) => state.scrvusd.pricesYieldData) const crvUsdRate = useStore((state) => state.usdRates.tokens[CRVUSD_ADDRESS]) const crvUsdRateLoading = useStore((state) => state.usdRates.loading) - const signerAddress = useStore((state) => state.wallet.onboard?.state.get().wallets?.[0]?.accounts?.[0]?.address) + const signerAddress = useWalletStore((s) => s.wallet?.accounts?.[0]?.address) const userBalance = useStore((state) => state.scrvusd.userBalances[signerAddress ?? '']) const crvUsdExchangeRateFetchStatus = useStore((state) => state.scrvusd.crvUsdExchangeRate.fetchStatus) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/index.tsx index a544cd594..1dcb1d101 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/index.tsx @@ -8,6 +8,7 @@ import StatsBanner from '@/loan/components/PageCrvUsdStaking/StatsBanner' import DepositWithdraw from '@/loan/components/PageCrvUsdStaking/DepositWithdraw' import UserInformation from '@/loan/components/PageCrvUsdStaking/UserInformation' import UserPositionBanner from '@/loan/components/PageCrvUsdStaking/UserPositionBanner' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const CrvUsdStaking = ({ mobileBreakpoint }: { mobileBreakpoint: string }) => { const { @@ -20,11 +21,10 @@ const CrvUsdStaking = ({ mobileBreakpoint }: { mobileBreakpoint: string }) => { stakingModule, } = useStore((state) => state.scrvusd) const lendApi = useStore((state) => state.lendApi) - const { onboard: onboardInstance, provider } = useStore((state) => state.wallet) - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address + const provider = useWalletStore((s) => s.provider) + const signerAddress = useWalletStore((s) => s.wallet)?.accounts?.[0]?.address const chainId = useStore((state) => state.curve?.chainId) const userScrvUsdBalance = useStore((state) => state.scrvusd.userBalances[signerAddress ?? '']?.scrvUSD) ?? '0' - const isUserScrvUsdBalanceZero = BigNumber(userScrvUsdBalance).isZero() useEffect(() => { diff --git a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx index 69fc84f25..f3a8a21b7 100644 --- a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx +++ b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx @@ -1,11 +1,9 @@ import type { FormEstGas } from '@/loan/components/PageLoanManage/types' -import type { FormValues, FormStatus, StepKey, PageLoanCreateProps } from '@/loan/components/PageLoanCreate/types' +import type { FormStatus, FormValues, PageLoanCreateProps, StepKey } from '@/loan/components/PageLoanCreate/types' import type { Step } from '@ui/Stepper/types' - import { t, Trans } from '@lingui/macro' import React, { useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' - import { DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE, hasDeleverage } from '@/loan/components/PageLoanManage/utils' import { DEFAULT_WALLET_BALANCES } from '@/loan/components/LoanInfoUser/utils' import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanCollateralIncreaseSlice' @@ -15,7 +13,6 @@ import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import { curveProps } from '@/loan/utils/helpers' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - import { StyledInpChip } from '@/loan/components/PageLoanManage/styles' import Accordion from '@ui/Accordion' import AlertBox from '@ui/AlertBox' @@ -31,7 +28,8 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import DialogHealthLeverageWarning from '@/loan/components/PageLoanCreate/LoanFormCreate/components/DialogHealthLeverageWarning' import { useUserProfileStore } from '@ui-kit/features/user-profile' -import { Curve, Llamma, CollateralAlert } from '@/loan/types/loan.types' +import { CollateralAlert, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' const LoanCreate = ({ collateralAlert, @@ -60,7 +58,7 @@ const LoanCreate = ({ const userWalletBalances = useStore( (state) => state.loans.userWalletBalancesMapper[llammaId] ?? DEFAULT_WALLET_BALANCES, ) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchStepApprove = useStore((state) => state.loanCreate.fetchStepApprove) const fetchStepCreate = useStore((state) => state.loanCreate.fetchStepCreate) const setFormValues = useStore((state) => state.loanCreate.setFormValues) diff --git a/apps/main/src/loan/components/PageLoanCreate/Page.tsx b/apps/main/src/loan/components/PageLoanCreate/Page.tsx index 1f8b61576..5c4288235 100644 --- a/apps/main/src/loan/components/PageLoanCreate/Page.tsx +++ b/apps/main/src/loan/components/PageLoanCreate/Page.tsx @@ -32,9 +32,11 @@ import LoanInfoLlamma from '@/loan/components/LoanInfoLlamma' import TextEllipsis from '@ui/TextEllipsis' import Button from '@ui/Button' import Icon from '@ui/Icon' -import ConnectWallet from '@/loan/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -57,7 +59,9 @@ const Page: NextPage = () => { const setFormValues = useStore((state) => state.loanCreate.setFormValues) const setStateByKeys = useStore((state) => state.loanCreate.setStateByKeys) const { chartExpanded, setChartExpanded } = useStore((state) => state.ohlcCharts) - const provider = useStore((state) => state.wallet.getProvider('')) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) + const provider = useWalletStore((s) => s.provider) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) const maxSlippage = useUserProfileStore((state) => state.maxSlippage.global) @@ -223,10 +227,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> )} diff --git a/apps/main/src/loan/components/PageLoanManage/CollateralDecrease/index.tsx b/apps/main/src/loan/components/PageLoanManage/CollateralDecrease/index.tsx index 7a14138fe..c4491da24 100644 --- a/apps/main/src/loan/components/PageLoanManage/CollateralDecrease/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/CollateralDecrease/index.tsx @@ -1,11 +1,8 @@ -import type { FormEstGas } from '@/loan/components/PageLoanManage/types' -import type { FormValues, FormStatus, StepKey } from '@/loan/components/PageLoanManage/CollateralDecrease/types' -import type { PageLoanManageProps } from '@/loan/components/PageLoanManage/types' +import type { FormEstGas, PageLoanManageProps } from '@/loan/components/PageLoanManage/types' +import type { FormStatus, FormValues, StepKey } from '@/loan/components/PageLoanManage/CollateralDecrease/types' import type { Step } from '@ui/Stepper/types' - import { t } from '@lingui/macro' import React, { useCallback, useEffect, useRef, useState } from 'react' - import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE } from '@/loan/components/PageLoanManage/utils' import { DEFAULT_WALLET_BALANCES } from '@/loan/components/LoanInfoUser/utils' import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanCollateralDecreaseSlice' @@ -15,8 +12,7 @@ import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import { formatNumber } from '@ui/utils' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - -import { StyledInpChip, StyledDetailInfoWrapper } from '@/loan/components/PageLoanManage/styles' +import { StyledDetailInfoWrapper, StyledInpChip } from '@/loan/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/loan/components/AlertFormError' import Box from '@ui/Box' @@ -31,6 +27,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' interface Props extends Pick {} @@ -51,7 +48,7 @@ const CollateralDecrease = ({ curve, llamma, llammaId, rChainId }: Props) => { const init = useStore((state) => state.loanCollateralDecrease.init) const fetchStepDecrease = useStore((state) => state.loanCollateralDecrease.fetchStepDecrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanCollateralDecrease.setFormValues) const setStateByKey = useStore((state) => state.loanCollateralDecrease.setStateByKey) const resetState = useStore((state) => state.loanCollateralDecrease.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/CollateralIncrease/index.tsx b/apps/main/src/loan/components/PageLoanManage/CollateralIncrease/index.tsx index d52f420c3..60d404f25 100644 --- a/apps/main/src/loan/components/PageLoanManage/CollateralIncrease/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/CollateralIncrease/index.tsx @@ -1,11 +1,9 @@ -import type { FormValues, FormStatus, StepKey } from '@/loan/components/PageLoanManage/CollateralIncrease/types' +import type { FormStatus, FormValues, StepKey } from '@/loan/components/PageLoanManage/CollateralIncrease/types' import type { FormEstGas, PageLoanManageProps } from '@/loan/components/PageLoanManage/types' import type { ReactNode } from 'react' -import type { Step } from '@ui/Stepper/types' - import React, { useCallback, useEffect, useRef, useState } from 'react' +import type { Step } from '@ui/Stepper/types' import { t } from '@lingui/macro' - import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE } from '@/loan/components/PageLoanManage/utils' import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanCollateralIncreaseSlice' import { DEFAULT_WALLET_BALANCES } from '@/loan/components/LoanInfoUser/utils' @@ -15,7 +13,6 @@ import { getActiveStep } from '@ui/Stepper/helpers' import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/loan/components/PageLoanManage/styles' import AlertBox from '@ui/AlertBox' import AlertFormError from '@/loan/components/AlertFormError' @@ -31,6 +28,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' interface Props extends Pick {} @@ -51,7 +49,7 @@ const CollateralIncrease = ({ curve, isReady, llamma, llammaId }: Props) => { const fetchStepApprove = useStore((state) => state.loanCollateralIncrease.fetchStepApprove) const fetchStepIncrease = useStore((state) => state.loanCollateralIncrease.fetchStepIncrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanCollateralIncrease.setFormValues) const setStateByKey = useStore((state) => state.loanCollateralIncrease.setStateByKey) const resetState = useStore((state) => state.loanCollateralIncrease.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/LoanDecrease/index.tsx b/apps/main/src/loan/components/PageLoanManage/LoanDecrease/index.tsx index 174fa5d0c..39fe8ec85 100644 --- a/apps/main/src/loan/components/PageLoanManage/LoanDecrease/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/LoanDecrease/index.tsx @@ -1,11 +1,9 @@ import type { FormValues, FormStatus, StepKey } from '@/loan/components/PageLoanManage/LoanDecrease/types' import type { FormEstGas, PageLoanManageProps } from '@/loan/components/PageLoanManage/types' import type { Step } from '@ui/Stepper/types' - import { t } from '@lingui/macro' import { useNavigate } from 'react-router-dom' import React, { useCallback, useEffect, useRef, useState } from 'react' - import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE } from '@/loan/components/PageLoanManage/utils' import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanDecreaseSlice' import { DEFAULT_WALLET_BALANCES } from '@/loan/components/LoanInfoUser/utils' @@ -16,7 +14,6 @@ import { getCollateralListPathname } from '@/loan/utils/utilsRouter' import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - import { StyledDetailInfoWrapper, StyledInpChip } from '@/loan/components/PageLoanManage/styles' import AlertFormError from '@/loan/components/AlertFormError' import AlertFormWarning from '@/loan/components/AlertFormWarning' @@ -32,6 +29,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' interface Props extends Pick {} @@ -54,7 +52,7 @@ const LoanDecrease = ({ curve, llamma, llammaId, params, rChainId }: Props) => { const fetchStepApprove = useStore((state) => state.loanDecrease.fetchStepApprove) const fetchStepDecrease = useStore((state) => state.loanDecrease.fetchStepDecrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanDecrease.setFormValues) const setStateByKey = useStore((state) => state.loanDecrease.setStateByKey) const resetState = useStore((state) => state.loanDecrease.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/LoanDeleverage/index.tsx b/apps/main/src/loan/components/PageLoanManage/LoanDeleverage/index.tsx index 3a8e8e5e6..2233d355b 100644 --- a/apps/main/src/loan/components/PageLoanManage/LoanDeleverage/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/LoanDeleverage/index.tsx @@ -45,6 +45,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' // Loan Deleverage const LoanDeleverage = ({ @@ -67,7 +68,7 @@ const LoanDeleverage = ({ const userLoanDetails = useStore((state) => state.loans.userDetailsMapper[llammaId]) const userWalletBalancesLoading = useStore((state) => state.loans.userWalletBalancesLoading) const fetchStepRepay = useStore((state) => state.loanDeleverage.fetchStepRepay) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanDeleverage.setFormValues) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) diff --git a/apps/main/src/loan/components/PageLoanManage/LoanIncrease/index.tsx b/apps/main/src/loan/components/PageLoanManage/LoanIncrease/index.tsx index 5f20e840c..4a02dbef7 100644 --- a/apps/main/src/loan/components/PageLoanManage/LoanIncrease/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/LoanIncrease/index.tsx @@ -34,6 +34,7 @@ import TxInfoBar from '@ui/TxInfoBar' import AlertFormError from '@/loan/components/AlertFormError' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' interface Props extends Pick {} @@ -58,7 +59,7 @@ const LoanIncrease = ({ curve, isReady, llamma, llammaId }: Props) => { const init = useStore((state) => state.loanIncrease.init) const fetchStepApprove = useStore((state) => state.loanIncrease.fetchStepApprove) const fetchStepIncrease = useStore((state) => state.loanIncrease.fetchStepIncrease) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanIncrease.setFormValues) const setStateByKey = useStore((state) => state.loanIncrease.setStateByKey) const resetState = useStore((state) => state.loanIncrease.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/LoanLiquidate/index.tsx b/apps/main/src/loan/components/PageLoanManage/LoanLiquidate/index.tsx index f8bae41a9..3c3bfd24a 100644 --- a/apps/main/src/loan/components/PageLoanManage/LoanLiquidate/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/LoanLiquidate/index.tsx @@ -1,11 +1,9 @@ import type { FormStatus, StepKey } from '@/loan/components/PageLoanManage/LoanLiquidate/types' import type { FormEstGas, PageLoanManageProps } from '@/loan/components/PageLoanManage/types' import type { Step } from '@ui/Stepper/types' - import { t, Trans } from '@lingui/macro' import React, { useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' - import { DEFAULT_FORM_STATUS, haveEnoughCrvusdForLiquidation } from '@/loan/store/createLoanLiquidate' import { DEFAULT_FORM_EST_GAS } from '@/loan/components/PageLoanManage/utils' import { curveProps } from '@/loan/utils/helpers' @@ -15,7 +13,6 @@ import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import { formatNumber } from '@ui/utils' import useStore from '@/loan/store/useStore' import networks from '@/loan/networks' - import AlertFormWarning from '@/loan/components/AlertFormWarning' import AlertFormError from '@/loan/components/AlertFormError' import AlertInfoSelfLiquidation from '@ui/AlertBox/AlertInfoSelfLiquidation' @@ -28,6 +25,7 @@ import Stepper from '@ui/Stepper' import TxInfoBar from '@ui/TxInfoBar' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { Curve, Llamma, UserWalletBalances } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' interface Props extends Pick {} @@ -44,7 +42,7 @@ const LoanLiquidate = ({ curve, llamma, llammaId, params, rChainId }: Props) => const fetchTokensToLiquidate = useStore((state) => state.loanLiquidate.fetchTokensToLiquidate) const fetchStepApprove = useStore((state) => state.loanLiquidate.fetchStepApprove) const fetchStepLiquidate = useStore((state) => state.loanLiquidate.fetchStepLiquidate) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setStateByKey = useStore((state) => state.loanLiquidate.setStateByKey) const resetState = useStore((state) => state.loanLiquidate.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/LoanSwap/index.tsx b/apps/main/src/loan/components/PageLoanManage/LoanSwap/index.tsx index 32cbe2055..5f27741c6 100644 --- a/apps/main/src/loan/components/PageLoanManage/LoanSwap/index.tsx +++ b/apps/main/src/loan/components/PageLoanManage/LoanSwap/index.tsx @@ -1,10 +1,9 @@ -import type { FormValues, FormStatus, StepKey } from '@/loan/components/PageLoanManage/LoanSwap/types' +import type { FormStatus, FormValues, StepKey } from '@/loan/components/PageLoanManage/LoanSwap/types' import type { FormEstGas, PageLoanManageProps } from '@/loan/components/PageLoanManage/types' import type { Step } from '@ui/Stepper/types' - +import { useWalletStore } from '@ui-kit/features/connect-wallet' import { t } from '@lingui/macro' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' - import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_STATUS, DEFAULT_FORM_VALUES } from '@/loan/store/createLoanSwap' import { DEFAULT_FORM_EST_GAS } from '@/loan/components/PageLoanManage/utils' import { DEFAULT_WALLET_BALANCES } from '@/loan/components/LoanInfoUser/utils' @@ -14,7 +13,6 @@ import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import { formatNumber } from '@ui/utils' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - import { StyledInpChip } from '@/loan/components/PageLoanManage/styles' import Box from '@ui/Box' import DetailInfoComp from '@ui/DetailInfo' @@ -52,7 +50,7 @@ const Swap = ({ curve, llamma, llammaId, rChainId }: Props) => { const fetchMaxSwappable = useStore((state) => state.loanSwap.fetchMaxSwappable) const fetchStepApprove = useStore((state) => state.loanSwap.fetchStepApprove) const fetchStepSwap = useStore((state) => state.loanSwap.fetchStepSwap) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const setFormValues = useStore((state) => state.loanSwap.setFormValues) const setStateByKey = useStore((state) => state.loanSwap.setStateByKey) const resetState = useStore((state) => state.loanSwap.resetState) diff --git a/apps/main/src/loan/components/PageLoanManage/Page.tsx b/apps/main/src/loan/components/PageLoanManage/Page.tsx index fc29fb7d0..f70fe7940 100644 --- a/apps/main/src/loan/components/PageLoanManage/Page.tsx +++ b/apps/main/src/loan/components/PageLoanManage/Page.tsx @@ -35,8 +35,9 @@ import Tabs, { Tab } from '@ui/Tab' import TextEllipsis from '@ui/TextEllipsis' import Button from '@ui/Button' import Icon from '@ui/Icon' -import ConnectWallet from '@/loan/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import { useUserProfileStore } from '@ui-kit/features/user-profile' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -56,7 +57,9 @@ const Page: NextPage = () => { const fetchUserLoanDetails = useStore((state) => state.loans.fetchUserLoanDetails) const resetUserDetailsState = useStore((state) => state.loans.resetUserDetailsState) const { chartExpanded, setChartExpanded } = useStore((state) => state.ohlcCharts) - const provider = useStore((state) => state.wallet.getProvider('')) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) + const provider = useWalletStore((s) => s.provider) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) @@ -227,10 +230,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> )} diff --git a/apps/main/src/loan/components/PageMarketList/Page.tsx b/apps/main/src/loan/components/PageMarketList/Page.tsx index 23ec9abad..10d689872 100644 --- a/apps/main/src/loan/components/PageMarketList/Page.tsx +++ b/apps/main/src/loan/components/PageMarketList/Page.tsx @@ -1,11 +1,9 @@ import type { NextPage } from 'next' import type { SearchParams } from '@/loan/components/PageMarketList/types' - import { t } from '@lingui/macro' import { useEffect, useState } from 'react' import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom' import styled from 'styled-components' - import { DEFAULT_SEARCH_PARAMS } from '@/loan/components/PageMarketList/utils' import { ROUTE, TITLE } from '@/loan/constants' import { breakpoints } from '@ui/utils/responsive' @@ -15,13 +13,13 @@ import usePageOnMount from '@/loan/hooks/usePageOnMount' import useSearchTermMapper from '@/loan/hooks/useSearchTermMapper' import useTitleMapper from '@/loan/hooks/useTitleMapper' import useStore from '@/loan/store/useStore' - import DocumentHead from '@/loan/layout/DocumentHead' import CollateralList from '@/loan/components/PageMarketList/index' import Settings from '@/loan/layout/Settings' import TableStats from '@/loan/components/PageMarketList/components/TableStats' -import ConnectWallet from '@/loan/components/ConnectWallet' +import { ConnectWalletPrompt, useWalletStore } from '@ui-kit/features/connect-wallet' import Box from '@ui/Box' +import { isLoading } from '@ui/utils' enum SEARCH { sortBy = 'sortBy', @@ -38,10 +36,12 @@ const Page: NextPage = () => { const titleMapper = useTitleMapper() const searchTermMapper = useSearchTermMapper() const { rChainId } = routerParams - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) const isLoadingApi = useStore((state) => state.isLoadingApi) const setStateByKey = useStore((state) => state.collateralList.setStateByKey) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) const [loaded, setLoaded] = useState(false) const [parsedSearchParams, setParsedSearchParams] = useState(DEFAULT_SEARCH_PARAMS) @@ -111,10 +111,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/loan/components/PagePegKeepers/Page.tsx b/apps/main/src/loan/components/PagePegKeepers/Page.tsx index 623a14318..e2bda8232 100644 --- a/apps/main/src/loan/components/PagePegKeepers/Page.tsx +++ b/apps/main/src/loan/components/PagePegKeepers/Page.tsx @@ -1,21 +1,20 @@ import type { NextPage } from 'next' - import { t } from '@lingui/macro' import { useEffect } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' import styled from 'styled-components' - import { breakpoints } from '@ui/utils/responsive' import { scrollToTop } from '@/loan/utils/helpers' import usePageOnMount from '@/loan/hooks/usePageOnMount' -import useStore from '@/loan/store/useStore' - import Box from '@ui/Box' -import ConnectWallet from '@/loan/components/ConnectWallet' +import { ConnectWalletPrompt } from '@ui-kit/features/connect-wallet' import DocumentHead from '@/loan/layout/DocumentHead' import ExternalLink from '@ui/Link/ExternalLink' import Settings from '@/loan/layout/Settings' import PagePegKeepers from '@/loan/components/PagePegKeepers' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import useStore from '@/loan/store/useStore' +import { isLoading } from '@ui/utils' const Page: NextPage = () => { const params = useParams() @@ -24,7 +23,9 @@ const Page: NextPage = () => { const { routerParams } = usePageOnMount(params, location, navigate) const { rChainId } = routerParams - const provider = useStore((state) => state.wallet.getProvider('')) + const provider = useWalletStore((s) => s.provider) + const connectWallet = useStore((s) => s.updateConnectState) + const connectState = useStore((s) => s.connectState) useEffect(() => { scrollToTop() @@ -49,10 +50,12 @@ const Page: NextPage = () => { ) : ( - connectWallet()} + isLoading={isLoading(connectState)} /> diff --git a/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperForm.tsx b/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperForm.tsx index 8390488e7..0dffa64cf 100644 --- a/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperForm.tsx +++ b/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperForm.tsx @@ -1,17 +1,16 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from '@lingui/macro' import styled from 'styled-components' - import { breakpoints, formatNumber } from '@ui/utils' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' - import AlertFormError from '@/loan/components/AlertFormError' import Button from '@ui/Button' import DetailInfo from '@ui/DetailInfo' import IconTooltip from '@ui/Tooltip/TooltipIcon' import LoanFormConnect from '@/loan/components/LoanFormConnect' import TxInfoBar from '@ui/TxInfoBar' +import { useWalletStore } from '@ui-kit/features/connect-wallet' import { ChainId, Curve } from '@/loan/types/loan.types' type Props = { @@ -26,7 +25,7 @@ const PegKeeperForm = ({ rChainId, poolName, pegKeeperAddress }: Props) => { const curve = useStore((state) => state.curve) const detailsMapper = useStore((state) => state.pegKeepers.detailsMapper) const formStatus = useStore((state) => state.pegKeepers.formStatus) - const notifyNotification = useStore((state) => state.wallet.notifyNotification) + const notifyNotification = useWalletStore((s) => s.notify) const fetchUpdate = useStore((state) => state.pegKeepers.fetchUpdate) const [txInfoBar, setTxInfoBar] = useState(null) diff --git a/apps/main/src/loan/entities/crvusd.ts b/apps/main/src/loan/entities/crvusd.ts deleted file mode 100644 index 4be561de6..000000000 --- a/apps/main/src/loan/entities/crvusd.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { PoolParams, PoolQuery, queryFactory, rootKeys } from '@ui-kit/lib/model/query' -import { poolValidationSuite } from '@ui-kit/lib/model/query/pool-validation' - -type CrvUsdSnapshotFromApi = { - rate: number - borrow_apy: number - lend_apy: number - liquidation_discount: number - loan_discount: number - n_loans: number - price_oracle: number - amm_price: number - base_price: number - total_debt: number - total_assets: number - total_debt_usd: number - total_assets_usd: number - minted: number - redeemed: number - minted_usd: number - redeemed_usd: number - min_band: number - max_band: number - collateral_balance: number - borrowed_balance: number - collateral_balance_usd: number - borrowed_balance_usd: number - sum_debt_squared: number - timestamp: string -} - -type CrvUsdSnapshotsFromApi = { - chain: string - market_id: number - data: CrvUsdSnapshotFromApi[] -} - -export const _getCrvUsdSnapshots = async ({ chainId, poolId }: PoolQuery): Promise => { - const url = `https://prices.curve.fi/v1/crvusd/markets/${chainId}/${poolId}/snapshots` - const response = await fetch(url) - const { data } = (await response.json()) as { data?: CrvUsdSnapshotsFromApi } - if (!data) { - throw new Error('Failed to fetch crvUSD snapshots') - } - return data -} - -export const { useQuery: useCrvUsdSnapshots } = queryFactory({ - queryKey: (params: PoolParams) => [...rootKeys.pool(params), 'crvUsdSnapshots'] as const, - queryFn: _getCrvUsdSnapshots, - staleTime: '1d', - validationSuite: poolValidationSuite, -}) diff --git a/apps/main/src/loan/hooks/usePageOnMount.ts b/apps/main/src/loan/hooks/usePageOnMount.ts index a20d13e00..4303c50ce 100644 --- a/apps/main/src/loan/hooks/usePageOnMount.ts +++ b/apps/main/src/loan/hooks/usePageOnMount.ts @@ -2,33 +2,36 @@ import type { Location, NavigateFunction, Params } from 'react-router' import type { ConnectState } from '@ui/utils' import { isFailure, isLoading, isSuccess } from '@ui/utils' import type { INetworkName } from '@curvefi/stablecoin-api/lib/interfaces' - import { ethers } from 'ethers' import { useCallback, useEffect } from 'react' -import { getWalletSignerAddress, useConnectWallet, useSetChain, useSetLocale } from '@ui-kit/features/connect-wallet' - +import { + getWalletChainId, + getWalletSignerAddress, + useConnectWallet, + useSetChain, + useSetLocale, +} from '@ui-kit/features/connect-wallet' import { CONNECT_STAGE, REFRESH_INTERVAL, ROUTE } from '@/loan/constants' import { dynamicActivate, updateAppLocale } from '@ui-kit/lib/i18n' -import { getStorageValue, setStorageValue } from '@/loan/utils/storage' import { getNetworkFromUrl, parseParams } from '@/loan/utils/utilsRouter' -import { getWalletChainId } from '@/loan/store/createWalletSlice' import { initCurveJs, initLendApi } from '@/loan/utils/utilsCurvejs' import networks, { networksIdMapper } from '@/loan/networks' import useStore from '@/loan/store/useStore' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { ChainId, PageProps, Wallet } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' function usePageOnMount(params: Params, location: Location, navigate: NavigateFunction, chainIdNotRequired?: boolean) { - const [{ wallet }, connect, disconnect] = useConnectWallet() + const { wallet, connect, disconnect, walletName, setWalletName } = useConnectWallet() const [_, setChain] = useSetChain() const updateWalletLocale = useSetLocale() + const connectState = useStore((state) => state.connectState) + const chooseWallet = useWalletStore((s) => s.chooseWallet) const curve = useStore((state) => state.curve) const { lendApi, updateLendApi } = useStore((state) => state) - const connectState = useStore((state) => state.connectState) const updateConnectState = useStore((state) => state.updateConnectState) const updateCurveJs = useStore((state) => state.updateCurveJs) - const updateProvider = useStore((state) => state.wallet.updateProvider) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) const setLocale = useUserProfileStore((state) => state.setLocale) @@ -42,7 +45,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu if (options) { try { const [chainId, useWallet] = options - await updateProvider(wallet) + await chooseWallet(wallet) const prevCurveApi = curve updateGlobalStoreByKey('isLoadingApi', true) updateGlobalStoreByKey('isLoadingCurve', true) // remove -> use connectState @@ -59,7 +62,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } }, - [curve, updateConnectState, updateCurveJs, updateGlobalStoreByKey, updateProvider, wallet], + [curve, updateConnectState, updateCurveJs, updateGlobalStoreByKey, chooseWallet, wallet], ) const handleConnectLendApi = useCallback( @@ -67,7 +70,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu if (options) { try { const [chainId, useWallet] = options - await updateProvider(wallet) + await chooseWallet(wallet) const prevApi = lendApi ?? null updateGlobalStoreByKey('isLoadingLendApi', true) const apiNew = await initLendApi(chainId, useWallet ? wallet : null) @@ -83,14 +86,14 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } }, - [updateProvider, wallet, lendApi, updateGlobalStoreByKey, updateLendApi, updateConnectState], + [chooseWallet, wallet, lendApi, updateGlobalStoreByKey, updateLendApi, updateConnectState], ) const handleConnectWallet = useCallback( async (options: ConnectState['options']) => { if (options) { const [walletName] = options - let walletState: Wallet | null = null + let walletState: Wallet | null if (walletName) { // If found label in localstorage, after 30s if not connected, reconnect with modal @@ -115,7 +118,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu walletState = walletStates[0] } catch (error) { // if failed to get walletState due to timeout, show connect modal. - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) ;[walletState] = await connect() } } else { @@ -124,7 +127,7 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu try { if (!walletState) throw new Error('unable to connect') - setStorageValue('APP_CACHE', { walletName: walletState.label, timestamp: Date.now().toString() }) + setWalletName(walletState.label) const walletChainId = getWalletChainId(walletState) if (walletChainId && walletChainId !== parsedParams.rChainId) { const success = await setChain({ chainId: ethers.toQuantity(parsedParams.rChainId) }) @@ -144,24 +147,24 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu } } catch (error) { updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) } } }, - [connect, navigate, parsedParams, setChain, updateConnectState], + [connect, navigate, parsedParams, setChain, updateConnectState, setWalletName], ) const handleDisconnectWallet = useCallback( async (wallet: Wallet) => { try { await disconnect(wallet) - setStorageValue('APP_CACHE', { walletName: '', timestamp: '' }) + setWalletName(null) updateConnectState('loading', CONNECT_STAGE.CONNECT_API, [parsedParams.rChainId, false]) } catch (error) { console.error(error) } }, - [disconnect, parsedParams.rChainId, updateConnectState], + [disconnect, parsedParams.rChainId, updateConnectState, setWalletName], ) const handleNetworkSwitch = useCallback( @@ -204,7 +207,6 @@ function usePageOnMount(params: Params, location: Location, navigate: NavigateFu navigate(`${parsedParams.rLocalePathname}/ethereum${ROUTE.PAGE_MARKETS}`) } else { updateGlobalStoreByKey('routerProps', { params, location, navigate }) - const walletName = getStorageValue('APP_CACHE')?.walletName ?? '' if (walletName) { updateConnectState('loading', CONNECT_STAGE.CONNECT_WALLET, [walletName]) } else { diff --git a/apps/main/src/loan/layout/Header.tsx b/apps/main/src/loan/layout/Header.tsx index b4d82c5fa..70d18cb25 100644 --- a/apps/main/src/loan/layout/Header.tsx +++ b/apps/main/src/loan/layout/Header.tsx @@ -18,7 +18,7 @@ import { ChainId, CollateralDatasMapper, LoanDetailsMapper, UsdRate } from '@/lo type HeaderProps = { sections: NavigationSection[]; BannerProps: GlobalBannerProps } export const Header = ({ sections, BannerProps }: HeaderProps) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const mainNavRef = useRef(null) const navigate = useNavigate() useLayoutHeight(mainNavRef, 'mainNav') diff --git a/apps/main/src/loan/layout/index.tsx b/apps/main/src/loan/layout/index.tsx index fe54f20d2..745cde1a1 100644 --- a/apps/main/src/loan/layout/index.tsx +++ b/apps/main/src/loan/layout/index.tsx @@ -4,9 +4,8 @@ import styled from 'styled-components' import { CONNECT_STAGE, ROUTE } from '@/loan/constants' import { layoutHeightKeys } from '@/loan/store/createLayoutSlice' import { getNetworkFromUrl } from '@/loan/utils/utilsRouter' -import { getWalletChainId } from '@/loan/store/createWalletSlice' +import { getWalletChainId, useConnectWallet } from '@ui-kit/features/connect-wallet' import { isFailure, isLoading } from '@ui/utils' -import { useConnectWallet } from '@ui-kit/features/connect-wallet' import useLayoutHeight from '@/loan/hooks/useLayoutHeight' import useStore from '@/loan/store/useStore' import Header from '@/loan/layout/Header' @@ -16,7 +15,7 @@ import { Footer } from '@ui-kit/widgets/Footer' import { useUserProfileStore } from '@ui-kit/features/user-profile' const BaseLayout = ({ children }: { children: React.ReactNode }) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const globalAlertRef = useRef(null) useLayoutHeight(globalAlertRef, 'globalAlert') diff --git a/apps/main/src/loan/store/createAppSlice.ts b/apps/main/src/loan/store/createAppSlice.ts index ca3acf9e3..1b792bc65 100644 --- a/apps/main/src/loan/store/createAppSlice.ts +++ b/apps/main/src/loan/store/createAppSlice.ts @@ -1,6 +1,6 @@ import type { GetState, SetState } from 'zustand' import type { State } from '@/loan/store/useStore' -import type { ConnectState } from '@ui/utils' +import { CONNECT_STAGE, ConnectState } from '@ui/utils' import produce from 'immer' @@ -16,8 +16,8 @@ export type SliceKey = keyof State | '' export type StateKey = string type SliceState = { - connectState: ConnectState curve: Curve | null + connectState: ConnectState lendApi: LendApi | null crvusdTotalSupply: { total: string; minted: string; pegKeepersDebt: string; error: string } dailyVolume: number | null @@ -35,7 +35,7 @@ export interface AppSlice extends SliceState { getContract(jsonModuleName: string, contractAddress: string, provider: ContractRunner): Promise fetchCrvUSDTotalSupply(api: Curve): Promise fetchDailyVolume(): Promise - updateConnectState(status: ConnectState['status'], stage: ConnectState['stage'], options?: ConnectState['options']): void + updateConnectState(status?: ConnectState['status'], stage?: ConnectState['stage'], options?: ConnectState['options']): void updateCurveJs(curve: Curve, prevCurveApi: Curve | null, wallet: Wallet | null): Promise updateLendApi(lendApi: LendApi, prevLendApi: LendApi | null, wallet: Wallet | null): Promise updateGlobalStoreByKey(key: DefaultStateKeys, value: T): void @@ -47,8 +47,8 @@ export interface AppSlice extends SliceState { } const DEFAULT_STATE: SliceState = { - connectState: { status: '' as const, stage: '' }, curve: null, + connectState: { status: '', stage: '' }, lendApi: null, crvusdTotalSupply: { total: '', minted: '', pegKeepersDebt: '', error: '' }, dailyVolume: null, @@ -92,13 +92,8 @@ const createAppSlice = (set: SetState, get: GetState): AppSlice => updateGlobalStoreByKey('dailyVolume', 'NaN') } }, - updateConnectState: ( - status: ConnectState['status'], - stage: ConnectState['stage'], - options?: ConnectState['options'], - ) => { - const value = options ? { status, stage, options } : { status, stage } - get().updateGlobalStoreByKey('connectState', value) + updateConnectState: (status = 'loading', stage = CONNECT_STAGE.CONNECT_WALLET, options = ['']) => { + set({ connectState: { status, stage, ...(options && { options }) } }) }, updateCurveJs: async (curveApi: Curve, prevCurveApi: Curve | null, wallet: Wallet | null) => { const { gas, loans, usdRates, ...state } = get() diff --git a/apps/main/src/loan/store/createLoanCollateralDecreaseSlice.ts b/apps/main/src/loan/store/createLoanCollateralDecreaseSlice.ts index 711919539..e7be22acb 100644 --- a/apps/main/src/loan/store/createLoanCollateralDecreaseSlice.ts +++ b/apps/main/src/loan/store/createLoanCollateralDecreaseSlice.ts @@ -14,6 +14,8 @@ import { getTokenName } from '@/loan/utils/utilsLoan' import { loadingLRPrices } from '@/loan/utils/utilsCurvejs' import networks from '@/loan/networks' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -151,46 +153,38 @@ const createLoanCollateralDecrease = (set: SetState, get: GetState // steps fetchStepDecrease: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'REMOVE', + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'REMOVE', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const removeCollateralFn = networks[chainId].api.collateralDecrease.removeCollateral + const resp = await removeCollateralFn(activeKey, provider, llamma, formValues.collateral) + get()[sliceKey].fetchMaxRemovable(chainId, llamma) + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) + } + if (resp.activeKey === get()[sliceKey].activeKey) { + get()[sliceKey].setStateByKeys({ + detailInfo: {}, + formEstGas: {}, + formValues: DEFAULT_FORM_VALUES, + formStatus: { + ...get()[sliceKey].formStatus, + error: resp.error, + isInProgress: false, + isComplete: !resp.error, + step: '', + }, }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const removeCollateralFn = networks[chainId].api.collateralDecrease.removeCollateral - const resp = await removeCollateralFn(activeKey, provider, llamma, formValues.collateral) - - // re-fetch max removable - get()[sliceKey].fetchMaxRemovable(chainId, llamma) - - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - if (resp.activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKeys({ - detailInfo: {}, - formEstGas: {}, - formValues: DEFAULT_FORM_VALUES, - formStatus: { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: !resp.error, - step: '', - }, - }) - - return resp - } + return resp } }, diff --git a/apps/main/src/loan/store/createLoanCollateralIncreaseSlice.ts b/apps/main/src/loan/store/createLoanCollateralIncreaseSlice.ts index d6734d7e0..c1e9c3433 100644 --- a/apps/main/src/loan/store/createLoanCollateralIncreaseSlice.ts +++ b/apps/main/src/loan/store/createLoanCollateralIncreaseSlice.ts @@ -12,6 +12,8 @@ import { loadingLRPrices } from '@/loan/utils/utilsCurvejs' import networks from '@/loan/networks' import cloneDeep from 'lodash/cloneDeep' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -141,74 +143,68 @@ const createLoanCollateralIncrease = (set: SetState, get: GetState // step fetchStepApprove: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const collateralIncreaseApproveFn = networks[chainId].api.collateralIncrease.approve + const resp = await collateralIncreaseApproveFn(activeKey, provider, llamma, formValues.collateral) + if (activeKey === get()[sliceKey].activeKey) { get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'APPROVAL', + isApproved: !resp.error, + step: '', + formProcessing: !resp.error, + error: resp.error, }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const collateralIncreaseApproveFn = networks[chainId].api.collateralIncrease.approve - const resp = await collateralIncreaseApproveFn(activeKey, provider, llamma, formValues.collateral) - - if (activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - formProcessing: !resp.error, - error: resp.error, - }) - - if (!resp.error) { - get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) - } - return resp + if (!resp.error) { + get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) } + return resp } }, fetchStepIncrease: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'ADD', + }) + const chainId = curve.chainId + await get().gas.fetchGasInfo(curve) + const addCollateralFn = networks[chainId].api.collateralIncrease.addCollateral + const resp = await addCollateralFn(activeKey, provider, llamma, formValues.collateral) + if (activeKey === get()[sliceKey].activeKey) { + // re-fetch loan info + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) + } - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'ADD', + get()[sliceKey].setStateByKeys({ + detailInfo: {}, + formEstGas: {}, + formValues: DEFAULT_FORM_VALUES, + formStatus: { + ...get()[sliceKey].formStatus, + error: resp.error, + isInProgress: false, + isComplete: !resp.error, + step: '', + }, }) - const chainId = curve.chainId - await get().gas.fetchGasInfo(curve) - const addCollateralFn = networks[chainId].api.collateralIncrease.addCollateral - const resp = await addCollateralFn(activeKey, provider, llamma, formValues.collateral) - - if (activeKey === get()[sliceKey].activeKey) { - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - get()[sliceKey].setStateByKeys({ - detailInfo: {}, - formEstGas: {}, - formValues: DEFAULT_FORM_VALUES, - formStatus: { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: !resp.error, - step: '', - }, - }) - - return { ...resp, loanExists: loanExists.loanExists } - } + return { ...resp, loanExists: loanExists.loanExists } } }, diff --git a/apps/main/src/loan/store/createLoanCreateSlice.ts b/apps/main/src/loan/store/createLoanCreateSlice.ts index 4e4460024..1795bae5e 100644 --- a/apps/main/src/loan/store/createLoanCreateSlice.ts +++ b/apps/main/src/loan/store/createLoanCreateSlice.ts @@ -16,6 +16,8 @@ import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_EST_GAS } from '@/loan/components/Pag import { loadingLRPrices } from '@/loan/utils/utilsCurvejs' import networks from '@/loan/networks' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -361,34 +363,31 @@ const createLoanCreate = (set: SetState, get: GetState) => ({ formValues: FormValues, maxSlippage: string, ) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const { collateral } = formValues + const approveFn = networks[chainId].api.loanCreate.approve + const resp = await approveFn(activeKey, provider, llamma, isLeverage, collateral) + if (resp.activeKey === get()[sliceKey].activeKey) { get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'APPROVAL', + isApproved: !resp.error, + step: '', + formProcessing: !resp.error, + error: resp.error, }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const { collateral } = formValues - const approveFn = networks[chainId].api.loanCreate.approve - const resp = await approveFn(activeKey, provider, llamma, isLeverage, collateral) - - if (resp.activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - formProcessing: !resp.error, - error: resp.error, - }) - - get()[sliceKey].fetchEstGasApproval(activeKey, chainId, isLeverage, llamma, formValues, maxSlippage) + get()[sliceKey].fetchEstGasApproval(activeKey, chainId, isLeverage, llamma, formValues, maxSlippage) - return resp - } + return resp } }, fetchStepCreate: async ( @@ -400,52 +399,50 @@ const createLoanCreate = (set: SetState, get: GetState) => ({ maxSlippage: string, ) => { const chainId = curve.chainId - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'CREATE', + }) + await get().gas.fetchGasInfo(curve) + const { collateral, debt, n } = formValues + if (n !== null) { + const createFn = networks[chainId].api.loanCreate.create + const resp = await createFn(activeKey, provider, llamma, isLeverage, collateral, debt, n, maxSlippage) - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'CREATE', - }) + if (resp.activeKey === get()[sliceKey].activeKey) { + get()[sliceKey].setStateByKeys({ + liqRanges: {}, + liqRangesMapper: {}, + }) - await get().gas.fetchGasInfo(curve) - const { collateral, debt, n } = formValues - if (n !== null) { - const createFn = networks[chainId].api.loanCreate.create - const resp = await createFn(activeKey, provider, llamma, isLeverage, collateral, debt, n, maxSlippage) + // re-fetch loan info + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - if (resp.activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKeys({ - liqRanges: {}, - liqRangesMapper: {}, - }) + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) + } - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - // reset form values - const updatedFormValues = DEFAULT_FORM_VALUES - get()[sliceKey].setStateByKeys({ - ...getCreateLoanActiveKey(llamma, updatedFormValues, isLeverage, maxSlippage), - isEditLiqRange: false, - formStatus: { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: !resp.error, - step: '', - }, - formValues: updatedFormValues, - maxRecv: {}, - }) + // reset form values + const updatedFormValues = DEFAULT_FORM_VALUES + get()[sliceKey].setStateByKeys({ + ...getCreateLoanActiveKey(llamma, updatedFormValues, isLeverage, maxSlippage), + isEditLiqRange: false, + formStatus: { + ...get()[sliceKey].formStatus, + error: resp.error, + isInProgress: false, + isComplete: !resp.error, + step: '', + }, + formValues: updatedFormValues, + maxRecv: {}, + }) - return { ...resp, loanExists: loanExists.loanExists } - } + return { ...resp, loanExists: loanExists.loanExists } } } }, diff --git a/apps/main/src/loan/store/createLoanDecreaseSlice.ts b/apps/main/src/loan/store/createLoanDecreaseSlice.ts index 5fe49b219..abfdcdcb7 100644 --- a/apps/main/src/loan/store/createLoanDecreaseSlice.ts +++ b/apps/main/src/loan/store/createLoanDecreaseSlice.ts @@ -13,6 +13,8 @@ import { import { loadingLRPrices } from '@/loan/utils/utilsCurvejs' import networks from '@/loan/networks' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -169,7 +171,8 @@ const createLoanDecrease = (set: SetState, get: GetState) => ({ // steps fetchStepApprove: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, @@ -177,66 +180,60 @@ const createLoanDecrease = (set: SetState, get: GetState) => ({ step: 'APPROVAL', }) - if (provider) { - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const { debt, isFullRepay } = formValues - const approveFn = networks[chainId].api.loanDecrease.approve - const resp = await approveFn(activeKey, provider, llamma, debt, isFullRepay) - - if (activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - formProcessing: !resp.error, - error: resp.error, - }) - get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) - - return resp - } + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const { debt, isFullRepay } = formValues + const approveFn = networks[chainId].api.loanDecrease.approve + const resp = await approveFn(activeKey, provider, llamma, debt, isFullRepay) + if (activeKey === get()[sliceKey].activeKey) { + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isApproved: !resp.error, + step: '', + formProcessing: !resp.error, + error: resp.error, + }) + get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) + + return resp } }, fetchStepDecrease: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'PAY', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const repayFn = networks[chainId].api.loanDecrease.repay + const resp = await repayFn(activeKey, provider, llamma, formValues.debt, formValues.isFullRepay) + if (activeKey === get()[sliceKey].activeKey) { + // re-fetch loan info + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) + } - if (provider) { get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'PAY', + error: resp.error, + isInProgress: false, + isComplete: resp.error ? '' : true, + step: '', }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const repayFn = networks[chainId].api.loanDecrease.repay - const resp = await repayFn(activeKey, provider, llamma, formValues.debt, formValues.isFullRepay) - - if (activeKey === get()[sliceKey].activeKey) { - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: resp.error ? '' : true, - step: '', - }) - - get()[sliceKey].setStateByKeys({ - detailInfo: {}, - formEstGas: {}, - formValues: DEFAULT_FORM_VALUES, - }) - - return { ...resp, loanExists: loanExists.loanExists } - } + get()[sliceKey].setStateByKeys({ + detailInfo: {}, + formEstGas: {}, + formValues: DEFAULT_FORM_VALUES, + }) + + return { ...resp, loanExists: loanExists.loanExists } } }, diff --git a/apps/main/src/loan/store/createLoanDeleverageSlice.ts b/apps/main/src/loan/store/createLoanDeleverageSlice.ts index 203ce2a90..29acdb492 100644 --- a/apps/main/src/loan/store/createLoanDeleverageSlice.ts +++ b/apps/main/src/loan/store/createLoanDeleverageSlice.ts @@ -13,6 +13,8 @@ import { import { DEFAULT_FORM_EST_GAS } from '@/loan/components/PageLoanManage/utils' import networks from '@/loan/networks' import { ChainId, Curve, Llamma, UserLoanDetails } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -143,46 +145,43 @@ const createLoanDeleverageSlice = (set: SetState, get: GetState): get()[sliceKey].setStateByKey('formStatus', clonedFormStatus) }, fetchStepRepay: async (activeKey, curve, llamma, formValues, maxSlippage) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'REPAY', - }) - - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const repayFn = networks[chainId].api.loanDeleverage.repay - const resp = await repayFn(activeKey, provider, llamma, formValues.collateral, maxSlippage) - - if (resp.activeKey === get()[sliceKey].activeKey) { - let loanExists = true - let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) - cFormStatus.isApproved = get()[sliceKey].formStatus.isApproved - - if (resp.error) { - get()[sliceKey].setStateByKey('formStatus', cloneDeep({ ...cFormStatus, error: resp.error })) - } else { - // re-fetch loan info - const respLoanDetails = await get().loans.fetchLoanDetails(curve, llamma) - loanExists = respLoanDetails.loanExists.loanExists - - if (!loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - get()[sliceKey].setStateByKeys({ - formValues: DEFAULT_FORM_VALUES, - formStatus: cloneDeep({ ...cFormStatus, isComplete: true }), - detailInfo: {}, - formEstGas: {}, - }) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'REPAY', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const repayFn = networks[chainId].api.loanDeleverage.repay + const resp = await repayFn(activeKey, provider, llamma, formValues.collateral, maxSlippage) + if (resp.activeKey === get()[sliceKey].activeKey) { + let loanExists = true + let cFormStatus = cloneDeep(DEFAULT_FORM_STATUS) + cFormStatus.isApproved = get()[sliceKey].formStatus.isApproved + + if (resp.error) { + get()[sliceKey].setStateByKey('formStatus', cloneDeep({ ...cFormStatus, error: resp.error })) + } else { + // re-fetch loan info + const respLoanDetails = await get().loans.fetchLoanDetails(curve, llamma) + loanExists = respLoanDetails.loanExists.loanExists + + if (!loanExists) { + get().loans.resetUserDetailsState(llamma) } - return { ...resp, loanExists } + get()[sliceKey].setStateByKeys({ + formValues: DEFAULT_FORM_VALUES, + formStatus: cloneDeep({ ...cFormStatus, isComplete: true }), + detailInfo: {}, + formEstGas: {}, + }) } + + return { ...resp, loanExists } } }, diff --git a/apps/main/src/loan/store/createLoanIncreaseSlice.ts b/apps/main/src/loan/store/createLoanIncreaseSlice.ts index 7167bbe09..f1c7754de 100644 --- a/apps/main/src/loan/store/createLoanIncreaseSlice.ts +++ b/apps/main/src/loan/store/createLoanIncreaseSlice.ts @@ -13,6 +13,8 @@ import { import { loadingLRPrices } from '@/loan/utils/utilsCurvejs' import networks from '@/loan/networks' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -176,7 +178,8 @@ const createLoanIncrease = (set: SetState, get: GetState) => ({ // steps fetchStepApprove: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, @@ -184,70 +187,64 @@ const createLoanIncrease = (set: SetState, get: GetState) => ({ step: 'APPROVAL', }) - if (provider) { - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const { collateral } = formValues - const approveFn = networks[chainId].api.loanIncrease.approve - const resp = await approveFn(activeKey, provider, llamma, collateral) - - if (activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - formProcessing: !resp.error, - error: resp.error, - }) - get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const { collateral } = formValues + const approveFn = networks[chainId].api.loanIncrease.approve + const resp = await approveFn(activeKey, provider, llamma, collateral) + if (activeKey === get()[sliceKey].activeKey) { + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isApproved: !resp.error, + step: '', + formProcessing: !resp.error, + error: resp.error, + }) + get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues) - return resp - } + return resp } }, fetchStepIncrease: async (activeKey: string, curve: Curve, llamma: Llamma, formValues: FormValues) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'BORROW', - }) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const { collateral, debt } = formValues - const borrowMoreFn = networks[chainId].api.loanIncrease.borrowMore - const resp = await borrowMoreFn(activeKey, provider, llamma, collateral, debt) + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'BORROW', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const { collateral, debt } = formValues + const borrowMoreFn = networks[chainId].api.loanIncrease.borrowMore - // re-fetch max - get()[sliceKey].fetchMaxRecv(chainId, llamma, formValues) + // re-fetch max + const resp = await borrowMoreFn(activeKey, provider, llamma, collateral, debt) + get()[sliceKey].fetchMaxRecv(chainId, llamma, formValues) - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + // re-fetch loan info + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) + } - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } + if (activeKey === get()[sliceKey].activeKey) { + get()[sliceKey].setStateByKeys({ + detailInfo: {}, + formEstGas: {}, + maxRecv: {}, + formStatus: { + ...get()[sliceKey].formStatus, + error: resp.error, + isInProgress: false, + isComplete: !resp.error, + step: '', + }, + formValues: DEFAULT_FORM_VALUES, + }) - if (activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKeys({ - detailInfo: {}, - formEstGas: {}, - maxRecv: {}, - formStatus: { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: !resp.error, - step: '', - }, - formValues: DEFAULT_FORM_VALUES, - }) - - return resp - } + return resp } }, diff --git a/apps/main/src/loan/store/createLoanLiquidate.ts b/apps/main/src/loan/store/createLoanLiquidate.ts index 81748e671..c1c938357 100644 --- a/apps/main/src/loan/store/createLoanLiquidate.ts +++ b/apps/main/src/loan/store/createLoanLiquidate.ts @@ -8,6 +8,8 @@ import cloneDeep from 'lodash/cloneDeep' import { DEFAULT_FORM_EST_GAS, DEFAULT_FORM_STATUS as FORM_STATUS } from '@/loan/components/PageLoanManage/utils' import networks from '@/loan/networks' import { ChainId, Curve, Llamma, UserWalletBalances } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -103,69 +105,57 @@ const createLoanLiquidate = (set: SetState, get: GetState) => ({ // step fetchStepApprove: async (curve: Curve, llamma: Llamma, maxSlippage: string) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'APPROVAL', - }) - - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const resp = await networks[chainId].api.loanLiquidate.approve(provider, llamma) - - const updatedFormStatus: FormStatus = { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - isInProgress: !resp.error, - error: resp.error, - } - - get()[sliceKey].setStateByKey('formStatus', updatedFormStatus) - get()[sliceKey].fetchEstGasApproval(chainId, llamma, maxSlippage, updatedFormStatus) - - return resp + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const resp = await networks[chainId].api.loanLiquidate.approve(provider, llamma) + const updatedFormStatus: FormStatus = { + ...get()[sliceKey].formStatus, + isApproved: !resp.error, + step: '', + isInProgress: !resp.error, + error: resp.error, } + get()[sliceKey].setStateByKey('formStatus', updatedFormStatus) + get()[sliceKey].fetchEstGasApproval(chainId, llamma, maxSlippage, updatedFormStatus) + return resp }, fetchStepLiquidate: async (curve: Curve, llamma: Llamma, liquidationAmt: string, maxSlippage: string) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'LIQUIDATE', - }) - - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const liquidateFn = networks[chainId].api.loanLiquidate.liquidate - const resp = await liquidateFn(provider, llamma, maxSlippage) - - // re-fetch loan info - const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) - - if (!loanExists.loanExists) { - get().loans.resetUserDetailsState(llamma) - } - - get()[sliceKey].setStateByKeys({ - formEstGas: DEFAULT_FORM_EST_GAS, - formStatus: { - ...get()[sliceKey].formStatus, - isInProgress: false, - isComplete: !resp.error, - step: '', - error: resp.error, - }, - liquidationAmt: '', - }) - - return { ...resp, loanExists: loanExists.loanExists } + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'LIQUIDATE', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const liquidateFn = networks[chainId].api.loanLiquidate.liquidate + const resp = await liquidateFn(provider, llamma, maxSlippage) + const { loanExists } = await get().loans.fetchLoanDetails(curve, llamma) + if (!loanExists.loanExists) { + get().loans.resetUserDetailsState(llamma) } + get()[sliceKey].setStateByKeys({ + formEstGas: DEFAULT_FORM_EST_GAS, + formStatus: { + ...get()[sliceKey].formStatus, + isInProgress: false, + isComplete: !resp.error, + step: '', + error: resp.error, + }, + liquidationAmt: '', + }) + return { ...resp, loanExists: loanExists.loanExists } }, // slice helpers diff --git a/apps/main/src/loan/store/createLoanSwap.ts b/apps/main/src/loan/store/createLoanSwap.ts index 0fdb768a9..e99d3ef8e 100644 --- a/apps/main/src/loan/store/createLoanSwap.ts +++ b/apps/main/src/loan/store/createLoanSwap.ts @@ -7,6 +7,8 @@ import { DEFAULT_FORM_EST_GAS, DEFAULT_FORM_STATUS as FORM_STATUS } from '@/loan import networks from '@/loan/networks' import cloneDeep from 'lodash/cloneDeep' import { ChainId, Curve, Llamma } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' +import { setMissingProvider } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -237,33 +239,30 @@ const createLoanSwap = (set: SetState, get: GetState) => ({ formValues: FormValues, maxSlippage: string, ) => { - const provider = get().wallet.getProvider(sliceKey) - - if (provider) { + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'APPROVAL', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const approveFn = networks[chainId].api.swap.approve + const resp = await approveFn(activeKey, provider, llamma, formValues) + if (activeKey === get()[sliceKey].activeKey) { get()[sliceKey].setStateByKey('formStatus', { ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'APPROVAL', + isApproved: !resp.error, + step: '', + formProcessing: !resp.error, + error: resp.error, }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const approveFn = networks[chainId].api.swap.approve - const resp = await approveFn(activeKey, provider, llamma, formValues) - - if (activeKey === get()[sliceKey].activeKey) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isApproved: !resp.error, - step: '', - formProcessing: !resp.error, - error: resp.error, - }) - - get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues, formValues.item1, maxSlippage) + get()[sliceKey].fetchEstGasApproval(activeKey, chainId, llamma, formValues, formValues.item1, maxSlippage) - return resp - } + return resp } }, fetchStepSwap: async ( @@ -273,51 +272,48 @@ const createLoanSwap = (set: SetState, get: GetState) => ({ formValues: FormValues, maxSlippage: string, ) => { - const provider = get().wallet.getProvider(sliceKey) + const { provider } = useWalletStore.getState() + if (!provider) return setMissingProvider(get()[sliceKey]) + + get()[sliceKey].setStateByKey('formStatus', { + ...get()[sliceKey].formStatus, + isInProgress: true, + step: 'SWAP', + }) + await get().gas.fetchGasInfo(curve) + const chainId = curve.chainId + const swapFn = networks[chainId].api.swap.swap + const resp = await swapFn(activeKey, provider, llamma, formValues, maxSlippage) + if (activeKey === get()[sliceKey].activeKey) { + // re-fetch loan info + get().loans.fetchLoanDetails(curve, llamma) + + // reset form + const updatedFormValues = { + ...DEFAULT_FORM_VALUES, + item1Key: formValues.item1Key, + item2Key: formValues.item2Key, + } - if (provider) { - get()[sliceKey].setStateByKey('formStatus', { - ...get()[sliceKey].formStatus, - isInProgress: true, - step: 'SWAP', + // re-fetch max + get()[sliceKey].fetchMaxSwappable(chainId, llamma, updatedFormValues) + + get()[sliceKey].setStateByKeys({ + activeKey: getSwapActiveKey(llamma, updatedFormValues), + detailInfo: {}, + formEstGas: {}, + formValues: updatedFormValues, + formStatus: { + ...get()[sliceKey].formStatus, + error: resp.error, + isInProgress: false, + isComplete: !resp.error, + step: '', + }, + maxSwappable: {}, }) - await get().gas.fetchGasInfo(curve) - const chainId = curve.chainId - const swapFn = networks[chainId].api.swap.swap - const resp = await swapFn(activeKey, provider, llamma, formValues, maxSlippage) - - if (activeKey === get()[sliceKey].activeKey) { - // re-fetch loan info - get().loans.fetchLoanDetails(curve, llamma) - - // reset form - const updatedFormValues = { - ...DEFAULT_FORM_VALUES, - item1Key: formValues.item1Key, - item2Key: formValues.item2Key, - } - - // re-fetch max - get()[sliceKey].fetchMaxSwappable(chainId, llamma, updatedFormValues) - - get()[sliceKey].setStateByKeys({ - activeKey: getSwapActiveKey(llamma, updatedFormValues), - detailInfo: {}, - formEstGas: {}, - formValues: updatedFormValues, - formStatus: { - ...get()[sliceKey].formStatus, - error: resp.error, - isInProgress: false, - isComplete: !resp.error, - step: '', - }, - maxSwappable: {}, - }) - - return resp - } + return resp } }, diff --git a/apps/main/src/loan/store/createPegKeepersSlice.ts b/apps/main/src/loan/store/createPegKeepersSlice.ts index adca13f56..bf71d0d0c 100644 --- a/apps/main/src/loan/store/createPegKeepersSlice.ts +++ b/apps/main/src/loan/store/createPegKeepersSlice.ts @@ -9,6 +9,7 @@ import crvusdjsApi from '@/loan/lib/apiCrvusd' import { DEFAULT_FORM_STATUS } from '@/loan/components/PagePegKeepers/utils' import { PEG_KEEPERS_ADDRESSES } from '@/loan/constants' import { Curve, Provider } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -42,7 +43,7 @@ const createPegKeepersSlice = (set: SetState, get: GetState): PegK ...DEFAULT_STATE, fetchDetails: async (provider) => { - const { wallet, ...state } = get() + const state = get() let { detailsMapper, ...sliceState } = get()[sliceKey] try { @@ -85,7 +86,7 @@ const createPegKeepersSlice = (set: SetState, get: GetState): PegK } }, fetchEstCallerProfit: async (provider, pegKeeperAddress) => { - const { wallet, ...state } = get() + const state = get() let { detailsMapper, ...sliceState } = get()[sliceKey] try { @@ -103,10 +104,10 @@ const createPegKeepersSlice = (set: SetState, get: GetState): PegK } }, fetchUpdate: async (curve, pegKeeperAddress) => { - const { gas, wallet, ...state } = get() + const { gas, ...state } = get() let { formStatus, ...sliceState } = get()[sliceKey] - const provider = wallet.getProvider('') + const { provider } = useWalletStore.getState() if (!provider) return { hash: '', error: 'no provider' } const signer = await provider.getSigner() diff --git a/apps/main/src/loan/store/createScrvUsdSlice.ts b/apps/main/src/loan/store/createScrvUsdSlice.ts index b384841fc..f447a3a87 100644 --- a/apps/main/src/loan/store/createScrvUsdSlice.ts +++ b/apps/main/src/loan/store/createScrvUsdSlice.ts @@ -9,6 +9,7 @@ import networks from '@/loan/networks' import { Contract } from 'ethers' import cloneDeep from 'lodash/cloneDeep' import { FetchStatus, TransactionStatus } from '@/loan/types/loan.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' type StateKey = keyof typeof DEFAULT_STATE @@ -322,7 +323,7 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ depositApprove: async (amount: string) => { const lendApi = get().lendApi const curve = get().curve - const provider = get().wallet.provider + const { provider, notify } = useWalletStore.getState() const approveInfinite = get()[sliceKey].approveInfinite // TODO: check so curve always is set when approving @@ -331,12 +332,7 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ const chainId = curve.chainId const fetchGasInfo = get().gas.fetchGasInfo - const notifyNotification = get().wallet.notifyNotification - let dismissNotificationHandler - - const notifyPendingMessage = t`Please confirm to approve ${amount} crvUSD.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = notify(t`Please confirm to approve ${amount} crvUSD.`, 'pending').dismiss await fetchGasInfo(curve) get()[sliceKey].setStateByKey('approveDepositTransaction', { @@ -353,11 +349,9 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash[0]), errorMessage: '', }) - dismissConfirm() + dismissNotificationHandler() - const deployingNotificationMessage = t`Approving ${amount} crvUSD...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') - dismissNotificationHandler = dismissDeploying + dismissNotificationHandler = notify(t`Approving ${amount} crvUSD...`, 'pending').dismiss await provider.waitForTransaction(transactionHash[0]) @@ -366,11 +360,11 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash[0]), errorMessage: '', }) - dismissDeploying() + dismissNotificationHandler() get()[sliceKey].checkApproval.depositApprove(amount) const successNotificationMessage = t`Succesfully approved ${amount} crvUSD!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) return true } catch (error) { @@ -387,19 +381,15 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ deposit: async (amount: string) => { const lendApi = get().lendApi const curve = get().curve - const provider = get().wallet.provider + const { provider, notify } = useWalletStore.getState() if (!lendApi || !curve || !provider) return const chainId = curve.chainId const fetchGasInfo = get().gas.fetchGasInfo - const notifyNotification = get().wallet.notifyNotification - let dismissNotificationHandler - const notifyPendingMessage = t`Please confirm to deposit ${amount} crvUSD.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = notify(t`Please confirm to deposit ${amount} crvUSD.`, 'pending').dismiss await fetchGasInfo(curve) get()[sliceKey].setStateByKey('depositTransaction', { @@ -416,11 +406,9 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissConfirm() + dismissNotificationHandler() - const deployingNotificationMessage = t`Depositing ${amount} crvUSD...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') - dismissNotificationHandler = dismissDeploying + dismissNotificationHandler = notify(t`Depositing ${amount} crvUSD...`, 'pending').dismiss await provider.waitForTransaction(transactionHash) @@ -429,12 +417,12 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissDeploying() + dismissNotificationHandler() get()[sliceKey].fetchUserBalances() get()[sliceKey].setStakingModuleChangeReset() const successNotificationMessage = t`Succesfully deposited ${amount} crvUSD!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) } catch (error) { dismissNotificationHandler() get()[sliceKey].setStateByKey('depositTransaction', { @@ -448,19 +436,14 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ withdraw: async (amount: string) => { const lendApi = get().lendApi const curve = get().curve - const provider = get().wallet.provider + const { provider, notify } = useWalletStore.getState() if (!lendApi || !curve || !provider) return const chainId = curve.chainId const fetchGasInfo = get().gas.fetchGasInfo - const notifyNotification = get().wallet.notifyNotification - let dismissNotificationHandler - - const notifyPendingMessage = t`Please confirm to withdraw ${amount} scrvUSD.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = notify(t`Please confirm to withdraw ${amount} scrvUSD.`, 'pending').dismiss await fetchGasInfo(curve) get()[sliceKey].setStateByKey('withdrawTransaction', { @@ -477,11 +460,10 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissConfirm() + dismissNotificationHandler() const deployingNotificationMessage = t`Withdrawing ${amount} scrvUSD...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') - dismissNotificationHandler = dismissDeploying + dismissNotificationHandler = notify(deployingNotificationMessage, 'pending').dismiss await provider.waitForTransaction(transactionHash) @@ -490,12 +472,12 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissDeploying() + dismissNotificationHandler() get()[sliceKey].fetchUserBalances() get()[sliceKey].setStakingModuleChangeReset() const successNotificationMessage = t`Succesfully withdrew ${amount} scrvUSD!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) } catch (error) { dismissNotificationHandler() get()[sliceKey].setStateByKey('withdrawTransaction', { @@ -509,19 +491,15 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ redeem: async (amount: string) => { const lendApi = get().lendApi const curve = get().curve - const provider = get().wallet.provider + const { provider, notify } = useWalletStore.getState() if (!lendApi || !curve || !provider) return const chainId = curve.chainId const fetchGasInfo = get().gas.fetchGasInfo - const notifyNotification = get().wallet.notifyNotification - let dismissNotificationHandler - const notifyPendingMessage = t`Please confirm to withdraw ${amount} scrvUSD.` - const { dismiss: dismissConfirm } = notifyNotification(notifyPendingMessage, 'pending') - dismissNotificationHandler = dismissConfirm + let dismissNotificationHandler = notify(t`Please confirm to withdraw ${amount} scrvUSD.`, 'pending').dismiss await fetchGasInfo(curve) get()[sliceKey].setStateByKey('withdrawTransaction', { @@ -540,11 +518,9 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissConfirm() + dismissNotificationHandler() - const deployingNotificationMessage = t`Withdrawing ${amount} scrvUSD...` - const { dismiss: dismissDeploying } = notifyNotification(deployingNotificationMessage, 'pending') - dismissNotificationHandler = dismissDeploying + dismissNotificationHandler = notify(t`Withdrawing ${amount} scrvUSD...`, 'pending').dismiss await provider.waitForTransaction(transactionHash) @@ -553,12 +529,12 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ transaction: networks[chainId].scanTxPath(transactionHash), errorMessage: '', }) - dismissDeploying() + dismissNotificationHandler() get()[sliceKey].fetchUserBalances() get()[sliceKey].setStakingModuleChangeReset() const successNotificationMessage = t`Succesfully withdrew ${amount} scrvUSD!` - notifyNotification(successNotificationMessage, 'success', 15000) + notify(successNotificationMessage, 'success', 15000) } catch (error) { dismissNotificationHandler() get()[sliceKey].setStateByKey('withdrawTransaction', { @@ -618,8 +594,7 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ } }, previewAction: async (flag: PreviewFlag, amount: string) => { - const onboardInstance = get().wallet.onboard - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address.toLowerCase() + const signerAddress = useWalletStore.getState().wallet?.accounts?.[0]?.address.toLowerCase() get()[sliceKey].setStateByKey('preview', { fetchStatus: 'loading', value: '0' }) const lendApi = get().lendApi @@ -646,8 +621,7 @@ const createScrvUsdSlice = (set: SetState, get: GetState) => ({ } }, fetchUserBalances: async () => { - const onboardInstance = get().wallet.onboard - const signerAddress = onboardInstance?.state.get().wallets?.[0]?.accounts?.[0]?.address.toLowerCase() + const signerAddress = useWalletStore.getState().wallet?.accounts?.[0]?.address.toLowerCase() const lendApi = get().lendApi if (!lendApi || !signerAddress) return diff --git a/apps/main/src/loan/store/createWalletSlice.ts b/apps/main/src/loan/store/createWalletSlice.ts deleted file mode 100644 index e8ce02e88..000000000 --- a/apps/main/src/loan/store/createWalletSlice.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { GetState, SetState } from 'zustand' -import type { State } from '@/loan/store/useStore' -import type { CustomNotification, NotificationType } from '@web3-onboard/core/dist/types' -import type { Provider } from '@/loan/store/types' -import type { OnboardAPI, UpdateNotification } from '@web3-onboard/core' -import cloneDeep from 'lodash/cloneDeep' -import { BrowserProvider } from 'ethers' -import { Wallet } from '@/loan/types/loan.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - onboard: OnboardAPI | null - provider: Provider | null -} - -type ProviderSliceKey = - | 'loanCollateralDecrease' - | 'loanCollateralIncrease' - | 'loanCreate' - | 'loanDecrease' - | 'loanIncrease' - | 'loanLiquidate' - | 'loanSwap' - | 'loanDeleverage' - -const sliceKey = 'wallet' - -// prettier-ignore -export type WalletSlice = { - [sliceKey]: SliceState & { - notifyNotification(message: string, type: NotificationType, autoDismiss?: number): { dismiss: () => void; update?: UpdateNotification } - updateProvider(wallet: Wallet | null): Promise - getProvider(sliceKey: string): Provider | null - - // steps helper - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - onboard: null, - provider: null, -} - -const createWalletSlice = (set: SetState, get: GetState): WalletSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - - notifyNotification: (message: string, type: NotificationType = 'pending', autoDismiss?: number) => { - const onboard = get().wallet.onboard - - if (onboard) { - // see https://onboard.blocknative.com/docs/packages/core#options for all options - const customNotification: CustomNotification = { type, message } - - if (typeof autoDismiss !== 'undefined') { - customNotification.autoDismiss = autoDismiss - } - - return onboard.state.actions.customNotification(customNotification) - } - return { dismiss: () => {} } - }, - updateProvider: async (wallet) => { - try { - const storedProvider = get().wallet.provider - const newProvider = wallet ? getProvider(wallet) : null - if (storedProvider) await storedProvider.removeAllListeners() - get().wallet.setStateByKey('provider', newProvider) - } catch (error) { - console.error(error) - } - }, - getProvider: (sliceKey: ProviderSliceKey) => { - const provider = get().wallet.provider - if (!provider) { - const storedFormStatus = get()[sliceKey]?.formStatus - if ( - storedFormStatus && - typeof storedFormStatus === 'object' && - 'step' in storedFormStatus && - 'formProcessing' in storedFormStatus && - 'error' in storedFormStatus - ) { - get()[sliceKey].setStateByKey('formStatus', { - ...storedFormStatus, - step: '', - formProcessing: false, - error: 'error-invalid-provider', - }) - } - } - return provider - }, - - // slice helpers - setStateByActiveKey: (key: StateKey, activeKey: string, value: T) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key: StateKey, value: T) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState: Partial) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createWalletSlice - -export function getProvider(wallet: Wallet) { - return new BrowserProvider(wallet.provider) -} - -export function getWalletChainId(wallet: Wallet | undefined | null) { - if (!wallet) return null - return +(wallet as Wallet).chains[0].id -} diff --git a/apps/main/src/loan/store/useStore.ts b/apps/main/src/loan/store/useStore.ts index 4152a5262..e84fbec6e 100644 --- a/apps/main/src/loan/store/useStore.ts +++ b/apps/main/src/loan/store/useStore.ts @@ -7,7 +7,6 @@ import merge from 'lodash/merge' import createCacheSlice, { CacheSlice } from '@/loan/store/createCacheSlice' import createAppSlice, { AppSlice } from '@/loan/store/createAppSlice' import createLayoutSlice, { AppLayoutSlice } from '@/loan/store/createLayoutSlice' -import createWalletSlice, { WalletSlice } from '@/loan/store/createWalletSlice' import createGasSlice, { GasSlice } from '@/loan/store/createGasSlice' import createUsdRatesSlice, { UsdRatesSlice } from '@/loan/store/createUsdRatesSlice' import createTokensSlice, { TokensSlice } from '@/loan/store/createTokensSlice' @@ -36,7 +35,6 @@ import type { PersistOptions } from 'zustand/middleware/persist' export type State = CacheSlice & AppSlice & AppLayoutSlice & - WalletSlice & GasSlice & UsdRatesSlice & TokensSlice & @@ -60,7 +58,6 @@ export type State = CacheSlice & const store = (set: SetState, get: GetState): State => ({ ...createCacheSlice(set, get), ...createAppSlice(set, get), - ...createWalletSlice(set, get), ...createLayoutSlice(set, get), ...createGasSlice(set, get), ...createUsdRatesSlice(set, get), diff --git a/apps/main/src/loan/utils/storage.ts b/apps/main/src/loan/utils/storage.ts deleted file mode 100644 index 9d0e3de14..000000000 --- a/apps/main/src/loan/utils/storage.ts +++ /dev/null @@ -1,49 +0,0 @@ -import merge from 'lodash/merge' -import dayjs from '@ui-kit/lib/dayjs' -import { Theme } from '@/loan/types/loan.types' - -export const APP_STORAGE = { - APP_CACHE: 'crvusd-app-cache', -} - -type Key = keyof typeof APP_STORAGE - -export function getStorageValue(key: Key) { - const storedValue = window.localStorage.getItem(APP_STORAGE[key]) - let parsedStoredValue: { [key: string]: string } = {} - - if (storedValue) { - try { - parsedStoredValue = JSON.parse(storedValue) ?? {} - } catch (error) { - console.error(error) - } - } - - if (key === 'APP_CACHE') { - return { - themeType: getTheme(parsedStoredValue.themeType), - timestamp: parsedStoredValue.timestamp ?? '', - walletName: getWalletName(parsedStoredValue.walletName, parsedStoredValue.timestamp), - isAdvanceMode: parsedStoredValue.isAdvanceMode ?? false, - } - } -} - -function getTheme(svThemeType: string | undefined) { - if (svThemeType) { - const foundThemeType = ['default', 'dark', 'chad'].find((t) => t === svThemeType) as Theme - return (foundThemeType || 'default') as Theme - } -} - -function getWalletName(walletName: string | undefined, timestamp: string | undefined) { - const isStaled = walletName && timestamp && dayjs().diff(+timestamp, 'days') > 5 - return isStaled || !walletName ? '' : walletName -} - -export function setStorageValue(key: Key, updatedValue: T) { - const storedValue = getStorageValue(key) - const mergedStoredValue = merge(storedValue, updatedValue) - window.localStorage.setItem(APP_STORAGE[key], JSON.stringify(mergedStoredValue)) -} diff --git a/apps/main/src/pages/crvusd.tsx b/apps/main/src/pages/crvusd.tsx index 712203b36..4a0f18109 100644 --- a/apps/main/src/pages/crvusd.tsx +++ b/apps/main/src/pages/crvusd.tsx @@ -9,7 +9,6 @@ import { OverlayProvider } from '@react-aria/overlays' import delay from 'lodash/delay' import { useCallback, useEffect, useState } from 'react' import { HashRouter } from 'react-router-dom' -import { connectWalletLocales, initOnboard } from '@ui-kit/features/connect-wallet' import GlobalStyle from '@/loan/globalStyle' import usePageVisibleInterval from '@/loan/hooks/usePageVisibleInterval' import Page from '@/loan/layout/index' @@ -25,6 +24,7 @@ import { ChadCssProperties } from '@ui-kit/themes/typography' import { persister, queryClient } from '@ui-kit/lib/api/query-client' import { QueryProvider } from '@ui/QueryProvider' import { useUserProfileStore } from '@ui-kit/features/user-profile' +import { useWalletStore } from '@ui-kit/features/connect-wallet' i18n.load({ en: messagesEn }) i18n.activate('en') @@ -53,8 +53,7 @@ const App: NextPage = () => { const fetchGasInfo = useStore((state) => state.gas.fetchGasInfo) const setLayoutWidth = useStore((state) => state.layout.setLayoutWidth) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) - const updateWalletStateByKey = useStore((state) => state.wallet.setStateByKey) - + const initializeWallet = useWalletStore((s) => s.initialize) const theme = useUserProfileStore((state) => state.theme) const locale = useUserProfileStore((state) => state.locale) @@ -88,10 +87,7 @@ const App: NextPage = () => { dynamicActivate(parsedLocale, data) })() - // init onboard - const onboardInstance = initOnboard(connectWalletLocales, locale, theme, networks) - updateWalletStateByKey('onboard', onboardInstance) - + initializeWallet(locale, theme, networks) const handleVisibilityChange = () => updateGlobalStoreByKey('isPageVisible', !document.hidden) setAppLoaded(true) diff --git a/apps/main/src/pages/dao.tsx b/apps/main/src/pages/dao.tsx index bb97c5588..450ed3c4c 100644 --- a/apps/main/src/pages/dao.tsx +++ b/apps/main/src/pages/dao.tsx @@ -12,7 +12,6 @@ import delay from 'lodash/delay' import 'focus-visible' import { ThemeProvider } from '@ui-kit/shared/ui/ThemeProvider' import { dynamicActivate, initTranslation, updateAppLocale } from '@ui-kit/lib/i18n' -import { connectWalletLocales, initOnboard } from '@ui-kit/features/connect-wallet' import { getLocaleFromUrl } from '@/dao/utils' import { getIsMobile, getPageWidthClassName, isSuccess } from '@ui/utils' import { messages as messagesEn } from '@/locales/en/messages.js' @@ -23,6 +22,7 @@ import Page from '@/dao/layout' import GlobalStyle from '@/dao/globalStyle' import { ChadCssProperties } from '@ui-kit/themes/typography' import { useUserProfileStore } from '@ui-kit/features/user-profile' +import { useWalletStore } from '@ui-kit/features/connect-wallet' i18n.load({ en: messagesEn }) i18n.activate('en') @@ -43,19 +43,18 @@ const App: NextPage = () => { const setPageWidth = useStore((state) => state.layout.setLayoutWidth) const updateShowScrollButton = useStore((state) => state.updateShowScrollButton) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) - const updateWalletStoreByKey = useStore((state) => state.wallet.setStateByKey) const updateUserData = useStore((state) => state.user.updateUserData) const getProposals = useStore((state) => state.proposals.getProposals) const getGauges = useStore((state) => state.gauges.getGauges) const getGaugesData = useStore((state) => state.gauges.getGaugesData) const fetchAllStoredUsdRates = useStore((state) => state.usdRates.fetchAllStoredUsdRates) const curve = useStore((state) => state.curve) - const onboard = useStore((state) => state.wallet.onboard) + const wallet = useWalletStore((s) => s.wallet) const isPageVisible = useStore((state) => state.isPageVisible) - const theme = useUserProfileStore((state) => state.theme) const locale = useUserProfileStore((state) => state.locale) const setLocale = useUserProfileStore((state) => state.setLocale) + const initializeWallet = useWalletStore((s) => s.initialize) const [appLoaded, setAppLoaded] = useState(false) @@ -87,10 +86,7 @@ const App: NextPage = () => { })() setLocale(parsedLocale) updateAppLocale(parsedLocale) - - // init onboard - const onboardInstance = initOnboard(connectWalletLocales, locale, theme, networks) - updateWalletStoreByKey('onboard', onboardInstance) + initializeWallet(locale, theme, networks) const handleVisibilityChange = () => { updateGlobalStoreByKey('isPageVisible', !document.hidden) @@ -114,17 +110,10 @@ const App: NextPage = () => { }, []) useEffect(() => { - if (isSuccess(connectState) && curve && onboard) { - const updateUserDataIfReady = async () => { - const connectedWallets = onboard.state.get().wallets - if (connectedWallets.length > 0) { - updateUserData(curve, connectedWallets[0]) - } - } - - updateUserDataIfReady() + if (isSuccess(connectState) && curve && wallet) { + updateUserData(curve, wallet) } - }, [curve, connectState, updateUserData, onboard]) + }, [curve, connectState, updateUserData, wallet]) // initiate proposals list useEffect(() => { diff --git a/apps/main/src/pages/dex.tsx b/apps/main/src/pages/dex.tsx index bb21b79fb..7a149e62e 100644 --- a/apps/main/src/pages/dex.tsx +++ b/apps/main/src/pages/dex.tsx @@ -10,7 +10,6 @@ import delay from 'lodash/delay' import { useCallback, useEffect, useState } from 'react' import { I18nProvider as AriaI18nProvider } from 'react-aria' import { HashRouter } from 'react-router-dom' -import { connectWalletLocales, initOnboard } from '@ui-kit/features/connect-wallet' import { persister, queryClient } from '@ui-kit/lib/api/query-client' import { ThemeProvider } from '@ui-kit/shared/ui/ThemeProvider' import GlobalStyle from '@/dex/globalStyle' @@ -26,6 +25,7 @@ import { getLocaleFromUrl } from '@/dex/utils/utilsRouter' import { ChadCssProperties } from '@ui-kit/themes/typography' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { CurveApi } from '@/dex/types/main.types' +import { useWalletStore } from '@ui-kit/features/connect-wallet' i18n.load({ en: messagesEn }) i18n.activate('en') @@ -59,8 +59,8 @@ const App: NextPage = () => { const setTokensMapper = useStore((state) => state.tokens.setTokensMapper) const updateShowScrollButton = useStore((state) => state.updateShowScrollButton) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) - const updateWalletStoreByKey = useStore((state) => state.wallet.setStateByKey) const network = useStore((state) => state.networks.networks[chainId]) + const initializeWallet = useWalletStore((s) => s.initialize) const theme = useUserProfileStore((state) => state.theme) const locale = useUserProfileStore((state) => state.locale) @@ -106,9 +106,7 @@ const App: NextPage = () => { ;(async () => { const networks = await fetchNetworks() - // init onboard - const onboardInstance = initOnboard(connectWalletLocales, locale, theme, networks) - updateWalletStoreByKey('onboard', onboardInstance) + initializeWallet(locale, theme, networks) const handleVisibilityChange = () => { updateGlobalStoreByKey('isPageVisible', !document.hidden) diff --git a/apps/main/src/pages/lend.tsx b/apps/main/src/pages/lend.tsx index 5b620ffbc..092c4afc7 100644 --- a/apps/main/src/pages/lend.tsx +++ b/apps/main/src/pages/lend.tsx @@ -9,7 +9,6 @@ import delay from 'lodash/delay' import { useCallback, useEffect, useState } from 'react' import 'focus-visible' import { HashRouter } from 'react-router-dom' -import { connectWalletLocales, initOnboard } from '@ui-kit/features/connect-wallet' import { persister, queryClient } from '@ui-kit/lib/api/query-client' import { ThemeProvider } from '@ui-kit/shared/ui/ThemeProvider' import GlobalStyle from '@/lend/globalStyle' @@ -24,6 +23,7 @@ import { isMobile, removeExtraSpaces } from '@/lend/utils/helpers' import { getLocaleFromUrl } from '@/lend/utils/utilsRouter' import { ChadCssProperties } from '@ui-kit/themes/typography' import { useUserProfileStore } from '@ui-kit/features/user-profile' +import { useWalletStore } from '@ui-kit/features/connect-wallet' i18n.load({ en: messagesEn }) i18n.activate('en') @@ -40,10 +40,9 @@ const App: NextPage = () => { const pageWidth = useStore((state) => state.layout.pageWidth) const setLayoutWidth = useStore((state) => state.layout.setLayoutWidth) const updateGlobalStoreByKey = useStore((state) => state.updateGlobalStoreByKey) - const updateWalletStateByKey = useStore((state) => state.wallet.setStateByKey) - const theme = useUserProfileStore((state) => state.theme) const locale = useUserProfileStore((state) => state.locale) + const initializeWallet = useWalletStore((s) => s.initialize) const [appLoaded, setAppLoaded] = useState(false) @@ -76,8 +75,7 @@ const App: NextPage = () => { })() // init onboard - const onboardInstance = initOnboard(connectWalletLocales, locale, theme, networks) - updateWalletStateByKey('onboard', onboardInstance) + initializeWallet(locale, theme, networks) const handleVisibilityChange = () => { updateGlobalStoreByKey('isPageVisible', !document.hidden) diff --git a/apps/main/tsconfig.json b/apps/main/tsconfig.json index 75298e38b..34be2f8a3 100644 --- a/apps/main/tsconfig.json +++ b/apps/main/tsconfig.json @@ -7,7 +7,9 @@ "@ui": ["../../../packages/ui/src/index.ts"], "@ui/*": ["../../../packages/ui/src/*"], "@ui-kit/*": ["../../../packages/curve-ui-kit/src/*"], - "@external-rewards": ["../../../packages/external-rewards/src/index.ts"] + "@external-rewards": ["../../../packages/external-rewards/src/index.ts"], + "@curvefi/prices-api": ["../../../packages/prices-api/src/index.ts"], + "@curvefi/prices-api/*": ["../../../packages/prices-api/src/*"] } }, "exclude": ["node_modules", "cypress"], diff --git a/packages/curve-ui-kit/package.json b/packages/curve-ui-kit/package.json index ca4c2d9f7..1edbe2c74 100644 --- a/packages/curve-ui-kit/package.json +++ b/packages/curve-ui-kit/package.json @@ -16,9 +16,9 @@ "@emotion/styled": "^11.13.0", "@lingui/detect-locale": "^4.6.0", "@lingui/react": "^4.6.0", - "@mui/icons-material": "^6.1.4", - "@mui/material": "^6.1.4", - "@mui/utils": "^6.1.4", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", + "@mui/utils": "^6.4.1", "@safe-global/safe-apps-provider": "^0.18.3", "@safe-global/safe-apps-sdk": "^9.1.0", "@tanstack/query-sync-storage-persister": "^5.59.0", diff --git a/packages/curve-ui-kit/src/features/connect-wallet/index.ts b/packages/curve-ui-kit/src/features/connect-wallet/index.ts index 9fe0f33ba..d425fe468 100644 --- a/packages/curve-ui-kit/src/features/connect-wallet/index.ts +++ b/packages/curve-ui-kit/src/features/connect-wallet/index.ts @@ -1,2 +1,3 @@ export * from './lib' export * from './ui' +export * from './store' diff --git a/packages/curve-ui-kit/src/features/connect-wallet/lib/hooks.ts b/packages/curve-ui-kit/src/features/connect-wallet/lib/hooks.ts new file mode 100644 index 000000000..6641d9773 --- /dev/null +++ b/packages/curve-ui-kit/src/features/connect-wallet/lib/hooks.ts @@ -0,0 +1,20 @@ +import { useEffect } from 'react' +import { useConnectWallet as useOnboardWallet } from '@web3-onboard/react' +import { useWalletStore } from '../store' +import { getFromLocalStorage, useLocalStorage } from '@ui-kit/hooks/useLocalStorage' + +export { useSetChain, useSetLocale } from '@web3-onboard/react' + +export const useConnectWallet = () => { + const [{ wallet, connecting }, connect, disconnect] = useOnboardWallet() + const [storedWalletName, setWalletName] = useLocalStorage('walletName') + // todo: remove this after a while. It tries to read the walletName from the old cache + const walletName = storedWalletName || getFromLocalStorage<{ walletName: string }>('curve-app-cache')?.walletName + + const chooseWallet = useWalletStore((s) => s.chooseWallet) + useEffect(() => { + chooseWallet(wallet) + }, [chooseWallet, wallet]) + + return { wallet, connecting, connect, disconnect, walletName, setWalletName } +} diff --git a/packages/curve-ui-kit/src/features/connect-wallet/lib/index.ts b/packages/curve-ui-kit/src/features/connect-wallet/lib/index.ts index 3c401b946..49433bf6e 100644 --- a/packages/curve-ui-kit/src/features/connect-wallet/lib/index.ts +++ b/packages/curve-ui-kit/src/features/connect-wallet/lib/index.ts @@ -1,4 +1,2 @@ -export { connectWalletLocales } from './locales' -export { useConnectWallet, useSetChain, useSetLocale } from '@web3-onboard/react' -export { initOnboard } from './init' -export { getWalletSignerAddress, getWalletChainId, getWalletProvider } from './utils/wallet-helpers' +export { useConnectWallet, useSetChain, useSetLocale } from './hooks' +export { getWalletSignerAddress, getWalletChainId, getWalletSignerEns } from './utils/wallet-helpers' diff --git a/packages/curve-ui-kit/src/features/connect-wallet/lib/init.ts b/packages/curve-ui-kit/src/features/connect-wallet/lib/init.ts index cbbc256e8..d1c6833ae 100644 --- a/packages/curve-ui-kit/src/features/connect-wallet/lib/init.ts +++ b/packages/curve-ui-kit/src/features/connect-wallet/lib/init.ts @@ -14,25 +14,15 @@ import { walletConnect, } from './utils/walletModules' import * as onboard from '@web3-onboard/react' +import { ThemeKey } from '@ui-kit/themes/basic-theme' +import zhHans from '@ui-kit/features/connect-wallet/lib/locales/zh-Hans' +import zhHant from '@ui-kit/features/connect-wallet/lib/locales/zh-Hant' -export function initOnboard(i18n: any, locale: any, themeType: any, networks: any) { - let theme: 'system' | 'light' | 'dark' = 'system' - if (themeType === 'default' || themeType === 'chad') { - theme = 'light' - } else if (themeType === 'dark') { - theme = 'dark' - } - - const chains = Object.keys(networks).map((key) => { - const network = networks[+key] - return { - id: network.hex, - token: network.symbol, - label: network.name, - rpcUrl: network.rpcUrl, - } - }) - +export const initOnboard = ( + locale: string, + themeType: ThemeKey, + networks: Record, +) => { const walletState = onboard.init({ wallets: [ injected, @@ -49,14 +39,19 @@ export function initOnboard(i18n: any, locale: any, themeType: any, networks: an trust, metamaskSDKWallet, ], - chains, + chains: Object.values(networks).map(({ hex, name, rpcUrl, symbol }) => ({ + id: hex, + token: symbol, + label: name, + rpcUrl, + })), appMetadata: { name: 'Curve', description: 'Efficient stablecoin and non-stablecoin swapping', icon: 'https://classic.curve.fi/logo-square.svg', }, disableFontDownload: true, - i18n, + i18n: { 'zh-Hans': zhHans, 'zh-Hant': zhHant }, notify: { desktop: { enabled: true, @@ -64,7 +59,7 @@ export function initOnboard(i18n: any, locale: any, themeType: any, networks: an position: 'topRight', }, }, - theme, + theme: themeType === 'chad' ? 'light' : themeType, accountCenter: { desktop: { enabled: false, diff --git a/packages/curve-ui-kit/src/features/connect-wallet/lib/locales/index.ts b/packages/curve-ui-kit/src/features/connect-wallet/lib/locales/index.ts deleted file mode 100644 index aef5ccb78..000000000 --- a/packages/curve-ui-kit/src/features/connect-wallet/lib/locales/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import zhHans from './zh-Hans' -import zhHant from './zh-Hant' - -export const connectWalletLocales = { 'zh-Hans': zhHans, 'zh-Hant': zhHant } diff --git a/packages/curve-ui-kit/src/features/connect-wallet/lib/utils/wallet-helpers.ts b/packages/curve-ui-kit/src/features/connect-wallet/lib/utils/wallet-helpers.ts index 278e0018b..bf2841524 100644 --- a/packages/curve-ui-kit/src/features/connect-wallet/lib/utils/wallet-helpers.ts +++ b/packages/curve-ui-kit/src/features/connect-wallet/lib/utils/wallet-helpers.ts @@ -1,14 +1,13 @@ import type { WalletState as Wallet } from '@web3-onboard/core/dist/types' -import { BrowserProvider } from 'ethers' -import type { Address } from '@ui-kit/utils' - -export const getWalletProvider = (wallet: Wallet) => new BrowserProvider(wallet.provider) +import { Address } from '@ui-kit/utils' export function getWalletChainId(wallet: Wallet | undefined | null) { - if (!wallet) return null - const chainId = wallet.chains[0].id - return Number(BigInt(chainId).toString()) + const chainId = wallet?.chains[0].id + return chainId && Number(BigInt(chainId).toString()) } export const getWalletSignerAddress = (wallet: Wallet | undefined | null): Address | undefined => wallet?.accounts[0]?.address + +export const getWalletSignerEns = (wallet: Wallet | undefined | null): string | undefined => + wallet?.accounts[0]?.ens?.name diff --git a/packages/curve-ui-kit/src/features/connect-wallet/store.ts b/packages/curve-ui-kit/src/features/connect-wallet/store.ts new file mode 100644 index 000000000..4e7ef8960 --- /dev/null +++ b/packages/curve-ui-kit/src/features/connect-wallet/store.ts @@ -0,0 +1,105 @@ +import { create, type StateCreator } from 'zustand' +import type { OnboardAPI, UpdateNotification, WalletState as Wallet } from '@web3-onboard/core' +import { BrowserProvider } from 'ethers' +import type { NotificationType } from '@web3-onboard/core/dist/types' +import { initOnboard } from './lib/init' +import { devtools } from 'zustand/middleware' +import type { Address } from 'abitype' +import type { EIP1193Provider } from '@web3-onboard/common' +import { logInfo } from '@ui-kit/lib' + +type WalletState = { + onboard: OnboardAPI | null + provider: BrowserProvider | null + cleanup: (() => Promise) | null // removes listeners from provider + wallet: Wallet | null +} + +type WalletActions = { + /** + * Sends a message via the onboard notification system. + * Note: This should be migrated to mui. + */ + notify( + message: string, + type: NotificationType, + autoDismiss?: number, + ): { + dismiss: () => void + update: UpdateNotification | undefined + } + /** + * Set a new wallet as current, listening for account changes + */ + chooseWallet(wallet: Wallet | null): Promise + /** + * Initialize the onboard instance + */ + initialize(...params: Parameters): void +} + +export type WalletStore = WalletState & WalletActions + +const DEFAULT_STATE: WalletState = { + onboard: null, + provider: null, + cleanup: null, + wallet: null, +} + +const walletStore: StateCreator = (set, get): WalletStore => ({ + ...DEFAULT_STATE, + notify: (message, type = 'pending', autoDismiss) => { + const { onboard } = get() + if (!onboard) { + throw new Error('Onboard not initialized') + } + return onboard.state.actions.customNotification({ + type, + message, + ...(typeof autoDismiss !== 'undefined' && { autoDismiss }), + }) + }, + chooseWallet: async (wallet: Wallet | null) => { + const { cleanup, chooseWallet, wallet: oldWallet } = get() + if (oldWallet === wallet) return // avoid double calls when updated via the useConnectWallet hook + cleanup?.() + return set(createProvider(wallet, chooseWallet)) + }, + initialize: async (locale, themeType, networks) => { + const { chooseWallet } = get() + const onboard = initOnboard(locale, themeType, networks) + const wallet = onboard.state.get().wallets?.[0] + return set({ onboard, ...createProvider(wallet, chooseWallet) }) + }, +}) + +export const useWalletStore = + process.env.NODE_ENV === 'development' ? create(devtools(walletStore)) : create(walletStore) + +function getRpcProvider(wallet: Wallet): EIP1193Provider { + if ('isTrustWallet' in wallet.provider && window.ethereum) { + // unable to connect to curvejs with wallet.provider + return window.ethereum as any // todo: why do we need any here? + } + if ('isExodus' in wallet.provider && typeof window.exodus.ethereum !== 'undefined') { + return window.exodus.ethereum + } + return wallet.provider +} + +function createProvider(wallet: Wallet | null | undefined, chooseWallet: (wallet: Wallet | null) => Promise) { + if (!wallet) return { rpcProvider: null, wallet: null, provider: null } + const rpcProvider = getRpcProvider(wallet) + const handler = (newAccounts: Address[]) => { + logInfo('accountsChanged', newAccounts) + return chooseWallet(newAccounts.length === 0 ? null : wallet) + } + rpcProvider.on('accountsChanged', handler) + const provider = new BrowserProvider(wallet.provider) + const cleanup = async () => { + rpcProvider.removeListener('accountsChanged', handler) + await provider?.removeAllListeners() + } + return { cleanup, wallet, provider } +} diff --git a/packages/curve-ui-kit/src/features/connect-wallet/ui/ConnectWalletPrompt.tsx b/packages/curve-ui-kit/src/features/connect-wallet/ui/ConnectWalletPrompt.tsx new file mode 100644 index 000000000..665e60d89 --- /dev/null +++ b/packages/curve-ui-kit/src/features/connect-wallet/ui/ConnectWalletPrompt.tsx @@ -0,0 +1,88 @@ +import React from 'react' +import { getBackgroundUrl } from '@ui/utils' +import Button from '@mui/material/Button' +import Stack from '@mui/material/Stack' +import { LogoImg } from '@ui/images' +import { styled } from '@mui/material/styles' +import NextImage from 'next/image' +import Typography from '@mui/material/Typography' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { t } from '@lingui/macro' + +type ConnectWalletPromptProps = { + description: string + connectText: string + loadingText: string + isLoading: boolean + connectWallet: () => void +} + +export const ConnectWalletPrompt = ({ + description, + connectText, + loadingText, + connectWallet, + isLoading, +}: ConnectWalletPromptProps) => ( + + `url(${getBackgroundUrl(t.key)})`, + backgroundSize: 'contain', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + }} + > + + {t`Enter Curve`} + + + {description} + + + +) + +const CurveLogo = styled(NextImage)({ + width: '3rem', + height: '3rem', + margin: '0 auto', + '@media (min-width: 43.75rem)': { + width: '5.5rem', + height: '5.5rem', + }, +}) + +export const setMissingProvider = (slice: { + setStateByKey: (key: 'formStatus', value: T) => void + formStatus: T +}): undefined => { + slice.setStateByKey('formStatus', { + ...slice.formStatus, + step: '', + formProcessing: false, + error: 'error-invalid-provider', + }) + return +} diff --git a/packages/curve-ui-kit/src/features/connect-wallet/ui/index.ts b/packages/curve-ui-kit/src/features/connect-wallet/ui/index.ts index 792f92325..33582a9ab 100644 --- a/packages/curve-ui-kit/src/features/connect-wallet/ui/index.ts +++ b/packages/curve-ui-kit/src/features/connect-wallet/ui/index.ts @@ -1,3 +1,4 @@ export * from './ConnectWalletButton' export * from './ConnectWalletIndicator' export * from './ConnectedWalletLabel' +export * from './ConnectWalletPrompt' diff --git a/packages/curve-ui-kit/src/features/user-profile/home/Header.tsx b/packages/curve-ui-kit/src/features/user-profile/home/Header.tsx index 851471af8..68c898d8b 100644 --- a/packages/curve-ui-kit/src/features/user-profile/home/Header.tsx +++ b/packages/curve-ui-kit/src/features/user-profile/home/Header.tsx @@ -6,7 +6,7 @@ import IconButton from '@mui/material/IconButton' import SettingsIcon from '@mui/icons-material/Settings' import CloseIcon from '@mui/icons-material/Close' -import { LlamaImg } from 'ui/src/images' +import { LlamaImg } from '@ui/images' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { addressShort, type Address } from '@ui-kit/utils' diff --git a/packages/curve-ui-kit/src/features/user-profile/home/Home.tsx b/packages/curve-ui-kit/src/features/user-profile/home/Home.tsx index d8c13631b..dcee7466b 100644 --- a/packages/curve-ui-kit/src/features/user-profile/home/Home.tsx +++ b/packages/curve-ui-kit/src/features/user-profile/home/Home.tsx @@ -13,7 +13,7 @@ type Props = { } export const Home = ({ onClose }: Props) => { - const [{ wallet }] = useConnectWallet() + const { wallet } = useConnectWallet() const walletAddress = getWalletSignerAddress(wallet) return ( diff --git a/packages/curve-ui-kit/src/lib/logging.ts b/packages/curve-ui-kit/src/lib/logging.ts index 9484f55d1..f0bb21c2d 100644 --- a/packages/curve-ui-kit/src/lib/logging.ts +++ b/packages/curve-ui-kit/src/lib/logging.ts @@ -55,7 +55,9 @@ function argToString(i: unknown, max = 200, trailing = 3) { return str.replaceAll('%', '%%') } -export function log(key: string | QueryKey | MutationKey | string[], status?: LogStatus | unknown, ...args: unknown[]) { +type LogKey = string | QueryKey | MutationKey | string[] + +export function log(key: LogKey, status?: LogStatus | unknown, ...args: unknown[]) { if (process.env.NODE_ENV !== 'development') return const timestamp = new Date().toISOString() @@ -109,7 +111,8 @@ export function log(key: string | QueryKey | MutationKey | string[], status?: Lo logMethod(status)(format, ...styles) } -export const logQuery = (key: QueryKey, ...args: unknown[]) => log(key, LogStatus.QUERY, ...args) -export const logMutation = (key: MutationKey, ...args: unknown[]) => log(key, LogStatus.MUTATION, ...args) -export const logError = (key: QueryKey | MutationKey, ...args: unknown[]) => log(key, LogStatus.ERROR, ...args) -export const logSuccess = (key: QueryKey | MutationKey, ...args: unknown[]) => log(key, LogStatus.SUCCESS, ...args) +export const logQuery = (key: LogKey, ...args: unknown[]) => log(key, LogStatus.QUERY, ...args) +export const logMutation = (key: LogKey, ...args: unknown[]) => log(key, LogStatus.MUTATION, ...args) +export const logError = (key: LogKey, ...args: unknown[]) => log(key, LogStatus.ERROR, ...args) +export const logSuccess = (key: LogKey, ...args: unknown[]) => log(key, LogStatus.SUCCESS, ...args) +export const logInfo = (key: LogKey, ...args: unknown[]) => log(key, LogStatus.INFO, ...args) diff --git a/packages/curve-ui-kit/src/shared/routes.ts b/packages/curve-ui-kit/src/shared/routes.ts index aed1a435e..d9f737a56 100644 --- a/packages/curve-ui-kit/src/shared/routes.ts +++ b/packages/curve-ui-kit/src/shared/routes.ts @@ -1,4 +1,4 @@ -import { AppRoutes } from '@ui-kit/widgets/Header/types' +import type { AppRoutes } from '@ui-kit/widgets/Header/types' import { t } from '@lingui/macro' import { isBeta } from '@ui-kit/utils' diff --git a/packages/curve-ui-kit/src/shared/ui/TableFilters.tsx b/packages/curve-ui-kit/src/shared/ui/TableFilters.tsx index cc9142089..0690b004b 100644 --- a/packages/curve-ui-kit/src/shared/ui/TableFilters.tsx +++ b/packages/curve-ui-kit/src/shared/ui/TableFilters.tsx @@ -78,11 +78,11 @@ export const TableFilters = ({ return ( - + {title} {subtitle} - + { const typography = createTypography(design) return createMuiTheme({ ...basicMuiTheme, + key: theme, design: { ...design, options }, palette: createPalette(paletteMode(theme, options), design), typography, diff --git a/packages/curve-ui-kit/src/themes/mui-overrides.d.ts b/packages/curve-ui-kit/src/themes/mui-overrides.d.ts index 1dbdbc568..abebf2361 100644 --- a/packages/curve-ui-kit/src/themes/mui-overrides.d.ts +++ b/packages/curve-ui-kit/src/themes/mui-overrides.d.ts @@ -1,12 +1,15 @@ -import { DesignSystem, DesignOptions } from './design' +import type { DesignSystem, DesignOptions } from './design' +import type { ThemeKey } from './basic-theme' declare module '@mui/material/styles' { interface Theme { design: DesignSystem & { options: DesignOptions } + key: ThemeKey } // allow configuration using `createTheme()` interface ThemeOptions { design?: DesignSystem & { options: DesignOptions } + key?: ThemeKey } interface TypeText { diff --git a/packages/curve-ui-kit/src/widgets/Footer/Description.tsx b/packages/curve-ui-kit/src/widgets/Footer/Description.tsx index 151b153d5..e7ac7f3a6 100644 --- a/packages/curve-ui-kit/src/widgets/Footer/Description.tsx +++ b/packages/curve-ui-kit/src/widgets/Footer/Description.tsx @@ -7,7 +7,7 @@ import SvgIcon from '@mui/material/SvgIcon' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { Sizing } from '@ui-kit/themes/design/0_primitives' -import { RCLogoSM } from 'ui/src/images' +import { RCLogoSM } from '@ui/images' const { IconSize, Spacing, FontWeight } = SizesAndSpaces diff --git a/packages/curve-ui-kit/src/widgets/Footer/Footer.tsx b/packages/curve-ui-kit/src/widgets/Footer/Footer.tsx index 2c035e18d..c64be7a25 100644 --- a/packages/curve-ui-kit/src/widgets/Footer/Footer.tsx +++ b/packages/curve-ui-kit/src/widgets/Footer/Footer.tsx @@ -4,7 +4,7 @@ import Grid from '@mui/material/Grid2' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { AppName } from '@ui-kit/shared/routes' -import { LlamaImg } from 'ui/src/images' +import { LlamaImg } from '@ui/images' import { Description } from './Description' import { Section } from './Section' diff --git a/packages/curve-ui-kit/src/widgets/Header/DesktopHeader.tsx b/packages/curve-ui-kit/src/widgets/Header/DesktopHeader.tsx index a5363e079..8a1ef5d2b 100644 --- a/packages/curve-ui-kit/src/widgets/Header/DesktopHeader.tsx +++ b/packages/curve-ui-kit/src/widgets/Header/DesktopHeader.tsx @@ -16,7 +16,7 @@ import { DEFAULT_BAR_SIZE } from '@ui-kit/themes/components' import { useState } from 'react' import { AppName } from '@ui-kit/shared/routes' import { t } from '@lingui/macro' -import GlobalBanner from 'ui/src/Banner' +import GlobalBanner from '@ui/Banner' import { isBeta, isCypress } from '@ui-kit/utils' export const DESKTOP_HEADER_HEIGHT = '96px' // note: hardcoded height is tested in cypress diff --git a/packages/curve-ui-kit/src/widgets/Header/HeaderLogo.tsx b/packages/curve-ui-kit/src/widgets/Header/HeaderLogo.tsx index b0a1f14cd..6dc7279a4 100644 --- a/packages/curve-ui-kit/src/widgets/Header/HeaderLogo.tsx +++ b/packages/curve-ui-kit/src/widgets/Header/HeaderLogo.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import Box from '@mui/material/Box' -import { LogoImg, RCLogoText } from 'ui/src/images' +import { LogoImg, RCLogoText } from '@ui/images' import { styled } from '@mui/material/styles' import Typography from '@mui/material/Typography' import Link from '@mui/material/Link' diff --git a/packages/curve-ui-kit/src/widgets/Header/MobileHeader.tsx b/packages/curve-ui-kit/src/widgets/Header/MobileHeader.tsx index 841d4f78a..5d9b0790f 100644 --- a/packages/curve-ui-kit/src/widgets/Header/MobileHeader.tsx +++ b/packages/curve-ui-kit/src/widgets/Header/MobileHeader.tsx @@ -14,7 +14,7 @@ import { MobileTopBar } from './MobileTopBar' import { useLocation } from 'react-router-dom' import { APP_LINK, AppName, externalAppUrl } from '@ui-kit/shared/routes' import { t } from '@lingui/macro' -import GlobalBanner from 'ui/src/Banner' +import GlobalBanner from '@ui/Banner' import { DEFAULT_BAR_SIZE, MOBILE_SIDEBAR_WIDTH } from '@ui-kit/themes/components' import { useUserProfileStore } from '@ui-kit/features/user-profile' diff --git a/packages/curve-ui-kit/src/widgets/Header/types.ts b/packages/curve-ui-kit/src/widgets/Header/types.ts index a90d82748..9b9fdde6a 100644 --- a/packages/curve-ui-kit/src/widgets/Header/types.ts +++ b/packages/curve-ui-kit/src/widgets/Header/types.ts @@ -2,7 +2,7 @@ import { ChainSwitcherProps } from '@ui-kit/features/switch-chain' import { ConnectWalletIndicatorProps } from '@ui-kit/features/connect-wallet' import { RefObject } from 'react' import { AppName } from '@ui-kit/shared/routes' -import { GlobalBannerProps } from 'ui/src/Banner/GlobalBanner' +import { GlobalBannerProps } from '@ui/Banner/GlobalBanner' export type Locale = 'en' | 'zh-Hans' | 'zh-Hant' | 'pseudo' diff --git a/packages/curve-ui-kit/tsconfig.json b/packages/curve-ui-kit/tsconfig.json index c2121c279..f0dd39bbd 100644 --- a/packages/curve-ui-kit/tsconfig.json +++ b/packages/curve-ui-kit/tsconfig.json @@ -10,6 +10,8 @@ "resolveJsonModule": true, "baseUrl": "./src", "paths": { + "@ui": ["../../../packages/ui/src/index.ts"], + "@ui/*": ["../../../packages/ui/src/*"], "@ui-kit/*": ["./*"] } }, diff --git a/packages/prices-api/package.json b/packages/prices-api/package.json new file mode 100644 index 000000000..a411782c5 --- /dev/null +++ b/packages/prices-api/package.json @@ -0,0 +1,21 @@ +{ + "name": "@curvefi/prices-api", + "version": "1.0.0", + "type": "module", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "MIT", + "private": true, + "scripts": { + "lint": "eslint \"**/*.ts*\"", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "typescript": "*" + }, + "devDependencies": { + "eslint": "*", + "eslint-config-custom": "*", + "typescript": "*" + } +} diff --git a/packages/prices-api/src/chains/api.ts b/packages/prices-api/src/chains/api.ts new file mode 100644 index 000000000..ecc7e3f8e --- /dev/null +++ b/packages/prices-api/src/chains/api.ts @@ -0,0 +1,32 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getSupportedChains(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/chains/`) + + return Parsers.parseSupportedChains(resp) +} + +export async function getChainInfo(chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/chains/${chain}?page=1&per_page=1`) + + return Parsers.parseChainInfo(resp) +} + +export async function getTxs(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/chains/activity/transactions`) + + return Parsers.parseTxs(resp) +} + +export async function getUsers(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/chains/activity/users`) + + return Parsers.parseUsers(resp) +} diff --git a/packages/prices-api/src/chains/index.ts b/packages/prices-api/src/chains/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/chains/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/chains/models.ts b/packages/prices-api/src/chains/models.ts new file mode 100644 index 000000000..777196881 --- /dev/null +++ b/packages/prices-api/src/chains/models.ts @@ -0,0 +1,29 @@ +import type { Chain } from '..' + +export type ChainInfo = { + chain: string + total: { + tvl: number + tradingVolume24h: number + tradingFee24h: number + liquidityVolume24h: number + liquidityFee24h: number + } +} + +export const activityTypes = ['crvusd', 'lending', 'pools', 'router', 'dao'] as const +export type ActivityType = (typeof activityTypes)[number] + +export type Activity = { + timestamp: Date + chain: Chain + type: ActivityType +} + +export type Transactions = Activity & { + transactions: number +} + +export type Users = Activity & { + users: number +} diff --git a/packages/prices-api/src/chains/parsers.ts b/packages/prices-api/src/chains/parsers.ts new file mode 100644 index 000000000..93fc0edc7 --- /dev/null +++ b/packages/prices-api/src/chains/parsers.ts @@ -0,0 +1,38 @@ +import { chains, type Chain } from '..' +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseSupportedChains = (x: Responses.GetSupportedChainsResponse): Chain[] => + x.data.map((y) => y.name as Chain).filter((y) => chains.includes(y)) + +export const parseChainInfo = (x: Responses.GetChainInfoResponse): Models.ChainInfo => ({ + chain: x.chain, + total: { + tvl: x.total.total_tvl, + tradingVolume24h: x.total.trading_volume_24h, + tradingFee24h: x.total.trading_fee_24h, + liquidityVolume24h: x.total.liquidity_volume_24h, + liquidityFee24h: x.total.liquidity_fee_24h, + }, +}) + +export const parseTxs = (x: Responses.GetTransactionsResponse): Models.Transactions[] => + x.data.flatMap((data) => + data.transactions.map((tx) => ({ + chain: data.chain, + timestamp: toUTC(tx.timestamp), + type: tx.type, + transactions: tx.transactions, + })), + ) + +export const parseUsers = (x: Responses.GetUsersResponse): Models.Users[] => + x.data.flatMap((data) => + data.users.map((tx) => ({ + chain: data.chain, + timestamp: toUTC(tx.timestamp), + type: tx.type, + users: tx.users, + })), + ) diff --git a/packages/prices-api/src/chains/responses.ts b/packages/prices-api/src/chains/responses.ts new file mode 100644 index 000000000..c3e591ab9 --- /dev/null +++ b/packages/prices-api/src/chains/responses.ts @@ -0,0 +1,39 @@ +import type { Chain } from '..' +import type { ActivityType } from './models' + +export type GetSupportedChainsResponse = { + data: { name: string }[] +} + +export type GetChainInfoResponse = { + chain: Chain + total: { + total_tvl: number + trading_volume_24h: number + trading_fee_24h: number + liquidity_volume_24h: number + liquidity_fee_24h: number + } +} + +export type GetTransactionsResponse = { + data: { + chain: Chain + transactions: { + type: ActivityType + transactions: number + timestamp: string + }[] + }[] +} + +export type GetUsersResponse = { + data: { + chain: Chain + users: { + type: ActivityType + users: number + timestamp: string + }[] + }[] +} diff --git a/packages/prices-api/src/crvusd/api.ts b/packages/prices-api/src/crvusd/api.ts new file mode 100644 index 000000000..ba6bc81aa --- /dev/null +++ b/packages/prices-api/src/crvusd/api.ts @@ -0,0 +1,82 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getMarkets(chain: Chain, page: number, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/markets/${chain}?fetch_on_chain=true&page=${page}&per_page=10`, + ) + + return resp.data.map(Parsers.parseMarket) +} + +export async function getSnapshots(chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/markets/${chain}/${marketAddr}/snapshots?fetch_on_chain=true&agg=day`, + ) + + return resp.data.map(Parsers.parseSnapshot) +} + +export async function getCrvUsdSupply(chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/crvusd/markets/${chain}/supply`) + + return resp.data.map(Parsers.parseSupply) +} + +export async function getKeepers(chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/crvusd/pegkeepers/${chain}`) + + return resp.keepers.map(Parsers.parseKeeper) +} + +export async function getUserMarkets(userAddr: string, chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/users/${chain}/${userAddr}?page=1&per_page=100`, + ) + + return Parsers.parseUserMarkets(resp) +} + +export async function getUserMarketStats(userAddr: string, chain: Chain, marketController: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/users/${chain}/${userAddr}/${marketController}/stats?page=1&per_page=100`, + ) + + return Parsers.parseUserMarketStats(resp) +} + +export async function getUserMarketSnapshots( + userAddr: string, + chain: Chain, + marketController: string, + options?: Options, +) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/users/${chain}/${userAddr}/${marketController}/snapshots?page=1&per_page=100`, + ) + + return Parsers.parseUserMarketSnapshots(resp) +} + +export async function getUserMarketCollateralEvents( + userAddr: string, + chain: Chain, + marketController: string, + options?: Options, +) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/crvusd/collateral_events/${chain}/${marketController}/${userAddr}`, + ) + + return Parsers.parseUserCollateralEvents(resp) +} diff --git a/packages/prices-api/src/crvusd/index.ts b/packages/prices-api/src/crvusd/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/crvusd/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/crvusd/models.ts b/packages/prices-api/src/crvusd/models.ts new file mode 100644 index 000000000..ae1dab07d --- /dev/null +++ b/packages/prices-api/src/crvusd/models.ts @@ -0,0 +1,134 @@ +import type { Address } from '..' + +export type Market = { + name: string + address: Address + factory: Address + llamma: Address + rate: number + borrowed: number + borrowable: number + collateral: number + collateralUsd: number + debtCeiling: number + loans: number + tokenCollateral: { + symbol: string + address: Address + } + tokenStablecoin: { + symbol: string + address: Address + } + fees: { + pending: number + collected: number + } +} + +export type Snapshot = { + timestamp: Date + rate: number + nLoans: number + minted: number + redeemed: number + totalCollateral: number + totalCollateralUsd: number + totalStablecoin: number + totalDebt: number + priceAMM: number + priceOracle: number + borrowable: number + discountLiquidation: number + discountLoan: number +} + +export type PoolPrice = { + timestamp: number + [token: string]: number +} + +export type PriceHistogram = { + x: number[] + y: number[] +} + +export type CrvUsdSupply = { + timestamp: Date + market: string + supply: number + borrowable: number +} + +export type Keeper = { + address: Address + pool: string + poolAddress: Address + pair: { + symbol: string + address: Address + }[] + active: boolean + totalDebt: number + totalProfit: number +} + +/** More specifically, the markets where a user holds a position */ +export type UserMarkets = { + collateral: string + controller: string + snapshotFirst: Date + snapshotLast: Date +}[] + +export type UserMarketStats = { + health: number + healthFull: number + n1: number + n2: number + n: number + debt: number + collateral: number + stablecoin: number + softLiquidation: boolean + totalDeposited: number + loss: number + lossPct: number + collateralUp: number + oraclePrice: number + blockNumber: number + timestamp: Date +} + +export type UserMarketSnapshots = UserMarketStats[] + +export type UserCollateralEvents = { + controller: Address + user: Address + totalDeposit: number + totalDepositPrecise: string + totalDepositUsd: number + totalBorrowed: number + totalBorrowedPrecise: string + events: { + timestamp: Date + txHash: Address + type: 'Borrow' | 'Deposit' + user: Address + collateralChange: number + collateralChangeUsd?: number + loanChange: number + loanChangeUsd?: number + liquidation?: { + user: Address + liquidator: Address + collateralReceived: number + collateralReceivedUsd: number + stablecoinReceived: number + debt: number + } + n1: number + n2: number + oraclePrice: number + }[] +} diff --git a/packages/prices-api/src/crvusd/parsers.ts b/packages/prices-api/src/crvusd/parsers.ts new file mode 100644 index 000000000..c04a1f8c2 --- /dev/null +++ b/packages/prices-api/src/crvusd/parsers.ts @@ -0,0 +1,132 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseMarket = (x: Responses.GetMarketsResponse['data'][number]): Models.Market => ({ + name: x.collateral_token.symbol, + address: x.address, + factory: x.factory_address, + llamma: x.llamma, + rate: x.rate, + borrowed: x.total_debt, + borrowable: x.borrowable, + collateral: x.collateral_amount, + collateralUsd: x.collateral_amount_usd, + debtCeiling: x.debt_ceiling, + loans: x.n_loans, + tokenCollateral: { + symbol: x.collateral_token.symbol, + address: x.collateral_token.address, + }, + tokenStablecoin: { + symbol: x.stablecoin_token.symbol, + address: x.stablecoin_token.address, + }, + fees: { + pending: x.pending_fees, + collected: x.collected_fees, + }, +}) + +export const parseSnapshot = (x: Responses.GetSnapshotsResponse['data'][number]): Models.Snapshot => ({ + timestamp: toUTC(x.dt), + rate: x.rate, + nLoans: x.n_loans, + minted: x.minted, + redeemed: x.redeemed, + totalCollateral: x.total_collateral, + totalCollateralUsd: x.total_collateral_usd, + totalStablecoin: x.total_stablecoin, + totalDebt: x.total_debt, + priceAMM: x.amm_price, + priceOracle: x.price_oracle, + borrowable: x.borrowable, + discountLiquidation: x.liquidation_discount, + discountLoan: x.loan_discount, +}) + +export const parseKeeper = (x: Responses.GetKeepersResponse['keepers'][number]): Models.Keeper => ({ + address: x.address, + pool: x.pool, + poolAddress: x.pool_address, + pair: x.pair.map((p) => ({ + symbol: p.symbol, + address: p.address, + })), + active: x.active, + totalDebt: x.total_debt, + totalProfit: x.total_profit, +}) + +export const parseSupply = (x: Responses.GetSupplyResponse['data'][number]): Models.CrvUsdSupply => ({ + timestamp: toUTC(x.timestamp), + market: x.market, + supply: x.supply, + borrowable: x.borrowable, +}) + +export const parseUserMarkets = (x: Responses.GetUserMarketsResponse): Models.UserMarkets => + x.markets.map((market) => ({ + collateral: market.collateral, + controller: market.controller, + snapshotFirst: toUTC(market.first_snapshot), + snapshotLast: toUTC(market.last_snapshot), + })) + +export const parseUserMarketStats = (x: Responses.GetUserMarketStatsResponse) => ({ + health: x.health, + healthFull: x.health_full, + n: x.n, + n1: x.n1, + n2: x.n2, + debt: x.debt, + collateral: x.collateral, + collateralUp: x.collateral_up, + stablecoin: x.stablecoin, + softLiquidation: x.soft_liquidation, + totalDeposited: x.total_deposited, + loss: x.loss, + lossPct: x.loss_pct, + oraclePrice: x.oracle_price, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), +}) + +export const parseUserMarketSnapshots = (x: Responses.GetUserMarketSnapshotsResponse): Models.UserMarketSnapshots => + x.data.map(parseUserMarketStats) + +export const parseUserCollateralEvents = ( + x: Responses.GetUserCollateralEventsResponse, +): Models.UserCollateralEvents => ({ + controller: x.controller, + user: x.user, + totalDeposit: x.total_deposit, + totalDepositPrecise: x.total_deposit_precise, + totalDepositUsd: x.total_deposit_usd_value, + totalBorrowed: x.total_borrowed, + totalBorrowedPrecise: x.total_borrowed_precise, + events: x.data.map((y) => ({ + timestamp: toUTC(y.dt), + txHash: y.transaction_hash, + type: y.type, + user: y.user, + collateralChange: y.collateral_change, + collateralChangeUsd: y.collateral_change_usd ?? undefined, + loanChange: y.loan_change, + loanChangeUsd: y.loan_change_usd ?? undefined, + liquidation: + y.liquidation === null + ? undefined + : { + user: y.liquidation.user, + liquidator: y.liquidation.liquidator, + collateralReceived: y.liquidation.collateral_received, + collateralReceivedUsd: y.liquidation.collateral_received_usd, + stablecoinReceived: y.liquidation.stablecoin_received, + debt: y.liquidation.debt, + }, + n1: y.n1, + n2: y.n2, + oraclePrice: y.oracle_price, + })), +}) diff --git a/packages/prices-api/src/crvusd/responses.ts b/packages/prices-api/src/crvusd/responses.ts new file mode 100644 index 000000000..6f3487202 --- /dev/null +++ b/packages/prices-api/src/crvusd/responses.ts @@ -0,0 +1,149 @@ +import type { Address } from '..' + +export type GetMarketsResponse = { + data: { + address: Address + factory_address: Address + llamma: Address + rate: number + total_debt: number + n_loans: number + debt_ceiling: number + borrowable: number + pending_fees: number + collected_fees: number + collateral_amount: number + collateral_amount_usd: number + stablecoin_amount: number + collateral_token: { + symbol: string + address: Address + } + stablecoin_token: { + symbol: string + address: Address + } + }[] + count: number +} + +export type GetSnapshotsResponse = { + data: { + rate: number + minted: number + redeemed: number + total_collateral: number + total_collateral_usd: number + total_stablecoin: number + total_debt: number + n_loans: number + amm_price: number + price_oracle: number + borrowable: number + dt: string + liquidation_discount: number + loan_discount: number + }[] +} + +export type GetKeepersResponse = { + keepers: { + address: Address + pool: string + pool_address: Address + pair: { + symbol: string + address: Address + }[] + active: boolean + total_debt: number + total_profit: number + }[] +} + +export type GetSupplyResponse = { + data: { + market: string + supply: number + borrowable: number + timestamp: string + }[] +} + +export type GetUserMarketsResponse = { + user: string + page: number + per_page: number + count: number + markets: { + collateral: string + controller: string + first_snapshot: string + last_snapshot: string + }[] +} + +type UserMarketStats = { + health: number + health_full: number + n1: number + n2: number + n: number + debt: number + collateral: number + stablecoin: number + soft_liquidation: boolean + total_deposited: number + loss: number + loss_pct: number + collateral_up: number + oracle_price: number + block_number: number + timestamp: string +} + +export type GetUserMarketStatsResponse = UserMarketStats + +export type GetUserMarketSnapshotsResponse = { + user: string + page: number + per_page: number + count: number + data: UserMarketStats[] +} + +export type GetUserCollateralEventsResponse = { + controller: Address + user: Address + total_deposit: number + total_borrowed: number + total_deposit_precise: string + total_borrowed_precise: string + total_deposit_usd_value: number + count: number + pagination: number + page: number + data: [ + { + dt: string + transaction_hash: Address + type: 'Borrow' | 'Deposit' + user: Address + collateral_change: number + collateral_change_usd: number | null + loan_change: number + loan_change_usd: number | null + liquidation: { + user: Address + liquidator: Address + collateral_received: number + collateral_received_usd: number + stablecoin_received: number + debt: number + } | null + n1: number + n2: number + oracle_price: number + }, + ] +} diff --git a/packages/prices-api/src/dao/api.ts b/packages/prices-api/src/dao/api.ts new file mode 100644 index 000000000..57a6d7ac0 --- /dev/null +++ b/packages/prices-api/src/dao/api.ts @@ -0,0 +1,39 @@ +import { getHost, type Options } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getVotesOverview(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/votes/overview`) + + return resp.data.map(Parsers.parseVotesOverview) +} + +export async function getLocksDaily(days: number, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/locks/daily/${days}`) + + return resp.locks.map(Parsers.parseLocksDaily) +} + +export async function getUserLocks(user: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/locks/${user}`) + + return resp.locks.map(Parsers.parseUserLock) +} + +export async function getLockers(top: number, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/lockers/${top}`) + + return resp.users.map(Parsers.parseLockers) +} + +export async function getSupply(days: number, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/supply/${days}`) + + return resp.supply.map(Parsers.parseSupply) +} diff --git a/packages/prices-api/src/dao/index.ts b/packages/prices-api/src/dao/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/dao/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/dao/models.ts b/packages/prices-api/src/dao/models.ts new file mode 100644 index 000000000..54bb660cb --- /dev/null +++ b/packages/prices-api/src/dao/models.ts @@ -0,0 +1,46 @@ +import type { Address } from '..' + +export type VotesOverview = { + proposals: number + votesProposals: number + votesGauges: number + votersUnique: number + epoch: number +} + +export type LocksDaily = { + day: Date + amount: bigint +} + +export type UserLock = { + timestamp: Date + amount: bigint + unlockTime: Date + lockType: 'CREATE_LOCK' | 'INCREASE_LOCK_AMOUNT' | 'INCREASE_UNLOCK_TIME' + lockedBalance: bigint + txHash: string +} + +export type Supply = { + timestamp: Date + veCrvTotal: bigint + crvEscrowed: bigint + crvSupply: bigint + circulatingSupply: bigint + lockedSupplyDetails: { + address: Address + label: string + locked: bigint + }[] + blockNumber: number + txHash: Address +} + +export type Locker = { + user: Address + locked: bigint + weight: bigint + weightRatio: number + unlockTime: Date +} diff --git a/packages/prices-api/src/dao/parsers.ts b/packages/prices-api/src/dao/parsers.ts new file mode 100644 index 000000000..ba252e79f --- /dev/null +++ b/packages/prices-api/src/dao/parsers.ts @@ -0,0 +1,48 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseVotesOverview = (x: Responses.GetVotesOverviewResponse['data'][number]): Models.VotesOverview => ({ + proposals: x.proposals, + votesProposals: x.prop_votes, + votesGauges: x.gauge_votes, + votersUnique: x.prop_unique_voters, + epoch: x.epoch, +}) + +export const parseLocksDaily = (x: Responses.GetLocksDailyResponse['locks'][number]): Models.LocksDaily => ({ + day: toUTC(x.day), + amount: BigInt(x.amount), +}) + +export const parseUserLock = (x: Responses.GetUserLocksResponse['locks'][number]): Models.UserLock => ({ + timestamp: toUTC(x.dt), + amount: BigInt(Math.round(parseFloat(x.amount))), + unlockTime: toUTC(x.unlock_time), + lockType: x.lock_type as Models.UserLock['lockType'], + lockedBalance: BigInt(Math.round(parseFloat(x.locked_balance))), + txHash: x.transaction_hash, +}) + +export const parseSupply = (x: Responses.GetSupplyResponse['supply'][number]): Models.Supply => ({ + timestamp: toUTC(x.dt), + veCrvTotal: BigInt(x.total_vecrv), + crvEscrowed: BigInt(x.escrowed_crv), + crvSupply: BigInt(x.crv_supply), + circulatingSupply: BigInt(x.circulating_supply), + lockedSupplyDetails: x.locked_supply_details.map((y) => ({ + address: y.address, + label: y.label, + locked: BigInt(y.locked), + })), + blockNumber: x.block_number, + txHash: x.transaction_hash, +}) + +export const parseLockers = (x: Responses.GetLockersResponse['users'][number]): Models.Locker => ({ + user: x.user, + locked: BigInt(Math.round(parseFloat(x.locked))), + weight: BigInt(Math.round(parseFloat(x.weight))), + weightRatio: parseFloat(x.weight_ratio.slice(0, -1)), + unlockTime: toUTC(x.unlock_time), +}) diff --git a/packages/prices-api/src/dao/responses.ts b/packages/prices-api/src/dao/responses.ts new file mode 100644 index 000000000..edeb0a618 --- /dev/null +++ b/packages/prices-api/src/dao/responses.ts @@ -0,0 +1,58 @@ +import type { Address } from '..' + +export type GetVotesOverviewResponse = { + data: { + proposals: number + prop_votes: number + prop_unique_voters: number + gauge_votes: number + epoch: number + }[] +} + +export type GetLocksDailyResponse = { + locks: { + day: string + amount: string + }[] +} + +export type GetSupplyResponse = { + supply: { + total_vecrv: string + escrowed_crv: string + crv_supply: string + circulating_supply: string + locked_supply_details: [ + { + address: Address + label: string + locked: string + }, + ] + block_number: number + dt: string + transaction_hash: Address + }[] +} + +export type GetUserLocksResponse = { + locks: { + amount: string + unlock_time: number + lock_type: string + locked_balance: string + dt: string + transaction_hash: string + }[] +} + +export type GetLockersResponse = { + users: { + user: Address + locked: string + weight: string + weight_ratio: string + unlock_time: number + }[] +} diff --git a/packages/prices-api/src/fetch.ts b/packages/prices-api/src/fetch.ts new file mode 100644 index 000000000..5b7709db9 --- /dev/null +++ b/packages/prices-api/src/fetch.ts @@ -0,0 +1,48 @@ +export class FetchError extends Error { + constructor( + public status: number, + message: string, + ) { + super(message) + } +} + +/** + * Fetches data from a URL and parses the response as JSON. + * Custom made to avoid a ky or axios dependency. + * + * @template T - The expected type of the parsed JSON data. + * @param url - The URL to fetch from. + * @param body - Optional request body for POST requests. + * @param signal - Optional AbortSignal to abort the fetch request. + * @returns A Promise that resolves to the parsed JSON data of type T. + * @throws Error if the fetch fails or returns a non-2xx status code. + */ +export async function fetchJson(url: string, body?: Record, signal?: AbortSignal): Promise { + try { + const resp = await fetch(url, { + method: body ? 'POST' : 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + signal, + }) + + if (!resp.ok) { + // Make the promise be rejected if we didn't get a 2xx response + throw new FetchError(resp.status, `Fetch error ${resp.status} for URL: ${url}`) + } else { + const json = (await resp.json()) as T + + return json + } + } catch (err) { + if (err instanceof Error) { + throw err + } + + throw new Error(`Unknown fetch error`) + } +} diff --git a/packages/prices-api/src/gauge/api.ts b/packages/prices-api/src/gauge/api.ts new file mode 100644 index 000000000..9b3c3ff6d --- /dev/null +++ b/packages/prices-api/src/gauge/api.ts @@ -0,0 +1,46 @@ +import { getHost, type Options } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getGauges(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/overview`) + + return resp.gauges.map(Parsers.parseGauge) +} + +export async function getGauge(gaugeAddress: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/${gaugeAddress}/metadata`) + + return Parsers.parseGauge(resp) +} + +export async function getVotes(gaugeAddress: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/${gaugeAddress}/votes`) + + return resp.votes.map(Parsers.parseVote) +} + +export async function getWeightHistory(gaugeAddress: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/${gaugeAddress}/weight_history`) + + return resp.data.map(Parsers.parseWeightHistory) +} + +export async function getDeployment(gaugeAddress: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/${gaugeAddress}/deployment`) + + return Parsers.parseDeployment(resp) +} + +export async function getUserGaugeVotes(user: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/gauges/votes/user/${user}`) + + return resp.votes.map(Parsers.parseUserGaugeVote) +} diff --git a/packages/prices-api/src/gauge/index.ts b/packages/prices-api/src/gauge/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/gauge/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/gauge/models.ts b/packages/prices-api/src/gauge/models.ts new file mode 100644 index 000000000..cdb8e2843 --- /dev/null +++ b/packages/prices-api/src/gauge/models.ts @@ -0,0 +1,71 @@ +import type { Address, Chain } from '..' + +export type Gauge = { + address: Address + type: string + name?: string + version?: string + lpToken?: Address + pool?: { + address: Address + name: string + chain: Chain + tvlUsd: number + tradingVolume24h: number + } + tokens: { + symbol: string + address: Address + precision: number + }[] + market?: { + name: string + chain: Chain + } + killed: boolean + emissions: number + weight: bigint + weightDelta7d?: number + weightDelta60d?: number + weightRelative: number + weightRelativeDelta7d?: number + weightRelativeDelta60d?: number + creationTx: Address + creationDate: Date + lastVoteTx?: Address + lastVoteDate?: Date +} + +export type GaugeVote = { + user: Address + weight: number + blockNumber: number + timestamp: Date + tx: Address +} + +export type WeightHistory = { + killed: boolean + weight: number + weightRelative: number + emissions: number + epoch: number +} + +export type Deployment = { + addressFrom: Address + addressTo?: Address + calldata: string + calldataDecoded?: string + blockNumber: number + timestamp: Date +} + +export type UserGaugeVote = { + timestamp: Date + gauge: Address + gaugeName: string + weight: number + blockNumber: number + txHash: Address +} diff --git a/packages/prices-api/src/gauge/parsers.ts b/packages/prices-api/src/gauge/parsers.ts new file mode 100644 index 000000000..a76fb13f9 --- /dev/null +++ b/packages/prices-api/src/gauge/parsers.ts @@ -0,0 +1,78 @@ +import type { Chain } from '..' +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseGauge = (x: Responses.GetGaugeResponse): Models.Gauge => ({ + address: x.address, + type: x.gauge_type, + name: x.name ?? undefined, + version: x.version ?? undefined, + lpToken: x.lp_token ? x.lp_token : undefined, + pool: x.pool + ? { + address: x.pool.address, + name: x.pool.name, + chain: x.pool.chain as Chain, + tvlUsd: x.pool.tvl_usd, + tradingVolume24h: x.pool.trading_volume_24h, + } + : undefined, + tokens: (x.tokens ?? []).map((token) => ({ + symbol: token.symbol, + address: token.address, + precision: token.precision, + })), + market: x.market + ? { + name: x.market.name, + chain: x.market.chain as Chain, + } + : undefined, + killed: x.is_killed, + emissions: x.emissions, + weight: BigInt(x.gauge_weight), + weightDelta7d: x.gauge_weight_7d_delta ? x.gauge_weight_7d_delta : undefined, + weightDelta60d: x.gauge_weight_60d_delta ? x.gauge_weight_60d_delta : undefined, + weightRelative: x.gauge_relative_weight, + weightRelativeDelta7d: x.gauge_relative_weight_7d_delta ? x.gauge_relative_weight_7d_delta : undefined, + weightRelativeDelta60d: x.gauge_relative_weight_60d_delta ? x.gauge_relative_weight_60d_delta : undefined, + creationTx: x.creation_tx, + creationDate: toUTC(x.creation_date), + lastVoteTx: x.last_vote_tx ?? undefined, + lastVoteDate: x.last_vote_date ? toUTC(x.last_vote_date) : undefined, +}) + +export const parseVote = (x: Responses.GetVotesResponse['votes'][number]): Models.GaugeVote => ({ + user: x.user, + weight: x.weight, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), + tx: x.transaction, +}) + +export const parseWeightHistory = (x: Responses.GetWeightHistoryResponse['data'][number]): Models.WeightHistory => ({ + killed: x.is_killed, + weight: parseFloat(x.gauge_weight), + weightRelative: parseFloat(x.gauge_relative_weight), + emissions: parseFloat(x.emissions), + epoch: x.epoch, +}) + +export const parseDeployment = (x: Responses.GetDeploymentResponse): Models.Deployment => ({ + addressFrom: x.from_address, + addressTo: x.to_address ?? undefined, + calldata: x.calldata, + calldataDecoded: x.decoded_calldata ?? undefined, + blockNumber: x.block_number, + timestamp: toUTC(x.dt), +}) + +export const parseUserGaugeVote = (x: Responses.GetUserGaugeVotesResponse['votes'][number]): Models.UserGaugeVote => ({ + gauge: x.gauge, + gaugeName: x.gauge_name, + weight: x.weight, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), + txHash: x.transaction, +}) diff --git a/packages/prices-api/src/gauge/responses.ts b/packages/prices-api/src/gauge/responses.ts new file mode 100644 index 000000000..7c99b141a --- /dev/null +++ b/packages/prices-api/src/gauge/responses.ts @@ -0,0 +1,86 @@ +import type { Address } from '..' + +type Gauge = { + address: Address + gauge_type: string + name: string | null + version: string | null + lp_token: Address | null + pool: { + address: Address + name: string + chain: string + tvl_usd: number + trading_volume_24h: number + } | null + tokens: + | { + symbol: string + address: Address + precision: number + }[] + | null + market: { + name: string + chain: string + } | null + is_killed: boolean + emissions: number + gauge_weight: string + gauge_weight_7d_delta: number | null + gauge_weight_60d_delta: number | null + gauge_relative_weight: number + gauge_relative_weight_7d_delta: number | null + gauge_relative_weight_60d_delta: number | null + creation_tx: Address + creation_date: string + last_vote_date: string | null + last_vote_tx: Address | null +} + +export type GetGaugeResponse = Gauge + +export type GetGaugesResponse = { + gauges: Gauge[] +} + +export type GetVotesResponse = { + votes: { + user: Address + weight: number + block_number: number + timestamp: string + transaction: Address + }[] +} + +export type GetWeightHistoryResponse = { + data: { + is_killed: boolean + gauge_weight: string + gauge_relative_weight: string + emissions: string + epoch: number + }[] +} + +export type GetDeploymentResponse = { + from_address: Address + to_address: Address | null + calldata: string + decoded_calldata: string | null + transaction_hash: Address + block_number: number + dt: string +} + +export type GetUserGaugeVotesResponse = { + votes: { + gauge: Address + gauge_name: string + weight: number + block_number: number + timestamp: string + transaction: Address + }[] +} diff --git a/packages/prices-api/src/index.ts b/packages/prices-api/src/index.ts new file mode 100644 index 000000000..1074b810b --- /dev/null +++ b/packages/prices-api/src/index.ts @@ -0,0 +1,50 @@ +/** + * Options for making API requests + * @example + * const options: Options = { + * host: "http://localhost:3000", // Custom host for local testing + * signal: new AbortController().signal // For request cancellation + * } + */ +export type Options = { + host?: string + signal?: AbortSignal +} + +/** + * Gets the API host URL, using the provided host or falling back to default + * @param options - Optional configuration options + * @returns Promise that resolves to the host URL + * @example + * const host = getHost() // "https://prices.curve.fi" + */ +export const getHost = (options?: Options): Required['host'] => options?.host ?? 'https://prices.curve.fi' + +/** + * List of supported blockchain networks + * @example + * const chain: Chain = "ethereum" + */ +export const chains = [ + 'ethereum', + 'arbitrum', + 'optimism', + 'fantom', + 'avalanche', + 'xdai', + 'matic', + 'harmony', + 'moonbeam', + 'base', + 'polygon', + 'fraxtal', +] as const + +export type Chain = (typeof chains)[number] + +export function isChain(chain: string): chain is Chain { + return chains.includes(chain as Chain) +} + +// Copied from Viem such that you don't actually need a Viem dependency but may also use Ethers. +export type Address = `0x${string}` diff --git a/packages/prices-api/src/lending/api.ts b/packages/prices-api/src/lending/api.ts new file mode 100644 index 000000000..72d5a4b8c --- /dev/null +++ b/packages/prices-api/src/lending/api.ts @@ -0,0 +1,15 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export type Endpoint = 'crvusd' | 'lending' + +export async function getLoanDistribution(endpoint: Endpoint, chain: Chain, controller: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/markets/${chain}/${controller}/loans/distribution`, + ) + + return Parsers.parseLoanDistribution(resp) +} diff --git a/packages/prices-api/src/lending/index.ts b/packages/prices-api/src/lending/index.ts new file mode 100644 index 000000000..78b1cd03a --- /dev/null +++ b/packages/prices-api/src/lending/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export * from './api' +export type { Endpoint } from './api' diff --git a/packages/prices-api/src/lending/models.ts b/packages/prices-api/src/lending/models.ts new file mode 100644 index 000000000..419169bf0 --- /dev/null +++ b/packages/prices-api/src/lending/models.ts @@ -0,0 +1,10 @@ +type Bar = { + value: number + label: string +} + +export type LoanDistribution = { + stablecoin: Bar[] + debt: Bar[] + collateral: Bar[] +} diff --git a/packages/prices-api/src/lending/parsers.ts b/packages/prices-api/src/lending/parsers.ts new file mode 100644 index 000000000..aec3ae8e6 --- /dev/null +++ b/packages/prices-api/src/lending/parsers.ts @@ -0,0 +1,8 @@ +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseLoanDistribution = (x: Responses.GetLoanDistributionResponse): Models.LoanDistribution => ({ + stablecoin: x.stablecoin.map((x) => ({ value: x.value, label: x.label })), + debt: x.debt.map((x) => ({ value: x.value, label: x.label })), + collateral: x.collateral.map((x) => ({ value: x.value, label: x.label })), +}) diff --git a/packages/prices-api/src/lending/responses.ts b/packages/prices-api/src/lending/responses.ts new file mode 100644 index 000000000..47533190f --- /dev/null +++ b/packages/prices-api/src/lending/responses.ts @@ -0,0 +1,10 @@ +type Bar = { + value: number + label: string +} + +export type GetLoanDistributionResponse = { + stablecoin: Bar[] + debt: Bar[] + collateral: Bar[] +} diff --git a/packages/prices-api/src/liquidations/api.ts b/packages/prices-api/src/liquidations/api.ts new file mode 100644 index 000000000..4dd881b5d --- /dev/null +++ b/packages/prices-api/src/liquidations/api.ts @@ -0,0 +1,60 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export type Endpoint = 'crvusd' | 'lending' + +export async function getSoftLiqRatios(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/soft_liquidation_ratio`, + ) + + return resp.data.map(Parsers.parseSoftLiqRatio) +} + +export async function getLiqsDetailed(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/history/detailed`, + ) + + return resp.data.map(Parsers.parseLiqsDetailed) +} + +export async function getLiqsAggregate(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/history/aggregated`, + ) + + return resp.data.map(Parsers.parseLiqsAggregate) +} + +export async function getLiqOverview(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/overview?fetch_on_chain=true`, + ) + + return Parsers.parseLiqOverview(resp) +} + +export async function getLiqLosses(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/losses/history`, + ) + + return resp.data.map(Parsers.parseLiqLosses) +} + +export async function getLiqHealthDeciles(endpoint: Endpoint, chain: Chain, marketAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/liquidations/${chain}/${marketAddr}/health/distribution`, + ) + + return resp.data.map(Parsers.parseLiqHealthDeciles) +} diff --git a/packages/prices-api/src/liquidations/index.ts b/packages/prices-api/src/liquidations/index.ts new file mode 100644 index 000000000..78b1cd03a --- /dev/null +++ b/packages/prices-api/src/liquidations/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export * from './api' +export type { Endpoint } from './api' diff --git a/packages/prices-api/src/liquidations/models.ts b/packages/prices-api/src/liquidations/models.ts new file mode 100644 index 000000000..35483e1fb --- /dev/null +++ b/packages/prices-api/src/liquidations/models.ts @@ -0,0 +1,60 @@ +import type { Address } from '..' + +export type SoftLiqRatio = { + timestamp: Date + proportion: number +} + +export type LiquidationDetails = { + timestamp: Date + user: Address + liquidator: Address + self: boolean + collateralReceived: number + collateralReceivedUsd: number + stablecoinReceived: number + priceOracle: number + debt: number + n1: number + n2: number + tx: Address + block: number +} + +export type LiquidationAggregate = { + timestamp: Date + selfCount: number + hardCount: number + selfValue: number + hardValue: number + price: number +} + +export type LiqOverview = { + softLiqUsers: number + liqablePositions: number + liqableDebtUsd: number + liqableCollatUsd: number + liqableBorrowedUsd: number + medianHealth: number + avgHealth: number + collatRatio: number +} + +export type LiqLosses = { + timestamp: Date + pctLossMedian: number + pctLossAverage: number + absoluteLossMedian: number + absoluteLossAverage: number + numTotalUsers: number + numUsersWithLosses: number + ratio: number +} + +export type LiqHealthDecile = { + decile: string + collateralUsdValue: number + debt: number + stablecoin: number +} diff --git a/packages/prices-api/src/liquidations/parsers.ts b/packages/prices-api/src/liquidations/parsers.ts new file mode 100644 index 000000000..c3d13304b --- /dev/null +++ b/packages/prices-api/src/liquidations/parsers.ts @@ -0,0 +1,66 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseSoftLiqRatio = (x: Responses.GetSoftLiqRatiosResponse['data'][number]): Models.SoftLiqRatio => ({ + timestamp: toUTC(x.timestamp), + proportion: x.proportion / 100, +}) + +export const parseLiqsDetailed = (x: Responses.GetLiqsDetailedResponse['data'][number]): Models.LiquidationDetails => ({ + timestamp: toUTC(x.dt), + user: x.user, + liquidator: x.liquidator, + self: x.self, + collateralReceived: x.collateral_received, + collateralReceivedUsd: x.collateral_received_usd, + stablecoinReceived: x.stablecoin_received, + priceOracle: x.oracle_price, + debt: x.debt, + n1: x.n1, + n2: x.n2, + tx: x.tx, + block: x.block, +}) + +export const parseLiqsAggregate = ( + x: Responses.GetLiqsAggregateResponse['data'][number], +): Models.LiquidationAggregate => ({ + timestamp: toUTC(x.timestamp), + selfCount: x.self_count, + hardCount: x.hard_count, + selfValue: x.self_value, + hardValue: x.hard_value, + price: x.price, +}) + +export const parseLiqOverview = (x: Responses.GetLiqOverviewResponse): Models.LiqOverview => ({ + softLiqUsers: x.soft_liquidation_users, + liqablePositions: x.liquidatable_positions, + liqableDebtUsd: x.liquidatable_pos_debt_usd, + liqableCollatUsd: x.liquidatable_collateral_usd, + liqableBorrowedUsd: x.liquidatable_borrowed_usd, + medianHealth: x.median_health, + avgHealth: x.average_health, + collatRatio: x.collat_ratio, +}) + +export const parseLiqLosses = (x: Responses.GetLiqLossesResponse['data'][number]): Models.LiqLosses => ({ + timestamp: toUTC(x.timestamp), + pctLossAverage: x.avg_pct_loss, + pctLossMedian: x.median_pct_loss, + absoluteLossAverage: x.avg_abs_loss, + absoluteLossMedian: x.median_abs_loss, + numTotalUsers: x.total_users, + numUsersWithLosses: x.users_with_losses, + ratio: x.ratio, +}) + +export const parseLiqHealthDeciles = ( + x: Responses.GetLiqHealthDecilesResponse['data'][number], +): Models.LiqHealthDecile => ({ + decile: x.health_decile, + collateralUsdValue: x.collateral, + debt: x.debt, + stablecoin: x.stablecoin, +}) diff --git a/packages/prices-api/src/liquidations/responses.ts b/packages/prices-api/src/liquidations/responses.ts new file mode 100644 index 000000000..a05a25ac6 --- /dev/null +++ b/packages/prices-api/src/liquidations/responses.ts @@ -0,0 +1,73 @@ +import type { Address } from '..' + +export type GetSoftLiqRatiosResponse = { + data: { + timestamp: string + proportion: number + }[] +} + +export type GetLiqsDetailedResponse = { + data: { + user: Address + liquidator: Address + self: boolean + collateral_received: number + collateral_received_usd: number + stablecoin_received: number + oracle_price: number + debt: number + n1: number + n2: number + dt: string + tx: Address + block: number + }[] +} + +export type GetLiqsAggregateResponse = { + data: { + timestamp: string + self_count: number + hard_count: number + self_value: number + hard_value: number + price: number + }[] +} + +export type GetLiqOverviewResponse = { + soft_liquidation_users: number + median_health: number + average_health: number + collat_ratio: number + liquidatable_positions: number + liquidatable_pos_debt: string + liquidatable_borrowed: string + liquidatable_collateral: string + liquidatable_pos_debt_usd: number + liquidatable_collateral_usd: number + liquidatable_borrowed_usd: number +} + +export type GetLiqLossesResponse = { + data: { + timestamp: string + median_pct_loss: number + avg_pct_loss: number + median_abs_loss: number + avg_abs_loss: number + total_users: number + users_with_losses: number + ratio: number + }[] +} + +export type GetLiqHealthDecilesResponse = { + data: { + health_decile: string + collateral: number + stablecoin: number + debt: number + }[] +} diff --git a/packages/prices-api/src/llamalend/api.ts b/packages/prices-api/src/llamalend/api.ts new file mode 100644 index 000000000..40364c0b8 --- /dev/null +++ b/packages/prices-api/src/llamalend/api.ts @@ -0,0 +1,74 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getChains(options?: Options): Promise { + const host = getHost(options) + + return fetch(`${host}/v1/lending/chains`).then((resp) => resp.data) +} + +export async function getMarkets(chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/markets/${chain}?fetch_on_chain=true&page=1&per_page=100`, + ) + + return resp.data.map(Parsers.parseMarket) +} + +export async function getSnapshots(chain: Chain, marketController: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/markets/${chain}/${marketController}/snapshots?fetch_on_chain=true&agg=day`, + ) + + return resp.data.map(Parsers.parseSnapshot) +} + +export async function getUserMarkets(userAddr: string, chain: Chain, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/users/${chain}/${userAddr}?page=1&per_page=100`, + ) + + return Parsers.parseUserMarkets(resp) +} + +export async function getUserMarketStats(userAddr: string, chain: Chain, marketController: string, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/users/${chain}/${userAddr}/${marketController}/stats?page=1&per_page=100`, + ) + + return Parsers.parseUserMarketStats(resp) +} + +export async function getUserMarketSnapshots( + userAddr: string, + chain: Chain, + marketController: string, + options?: Options, +) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/users/${chain}/${userAddr}/${marketController}/snapshots?page=1&per_page=100`, + ) + + return Parsers.parseUserMarketSnapshots(resp) +} + +export async function getUserMarketCollateralEvents( + userAddr: string, + chain: Chain, + marketController: string, + options?: Options, +) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/lending/collateral_events/${chain}/${marketController}/${userAddr}`, + ) + + return Parsers.parseUserCollateralEvents(resp) +} diff --git a/packages/prices-api/src/llamalend/index.ts b/packages/prices-api/src/llamalend/index.ts new file mode 100644 index 000000000..31d31e774 --- /dev/null +++ b/packages/prices-api/src/llamalend/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export * from './util' +export * from './api' diff --git a/packages/prices-api/src/llamalend/models.ts b/packages/prices-api/src/llamalend/models.ts new file mode 100644 index 000000000..1fbe42935 --- /dev/null +++ b/packages/prices-api/src/llamalend/models.ts @@ -0,0 +1,141 @@ +import type { Address } from '..' + +/* + * Note that collateral can be two tokens due to soft-liquidations. + * You can have a crvUSD borrow (partially) being collateralized by crvUSD. + */ +export type Market = { + name: Address + controller: Address + vault: Address + llamma: Address + policy: Address + oracle: Address + rate: number + apyBorrow: number + apyLend: number + nLoans: number + priceOracle: number + ammPrice: number + totalDebt: number // Borrowed + totalAssets: number // Supplied + totalDebtUsd: number + totalAssetsUsd: number + minted: number + mintedUsd: number + redeemed: number + redeemedUsd: number + collateralBalance: number // Collateral (like CRV) + collateralBalanceUsd: number + borrowedBalance: number // Collateral (like crvUSD) + borrowedBalanceUsd: number + tokenCollateral: { + symbol: string + address: Address + } + tokenBorrowed: { + symbol: string + address: Address + } +} + +export type MarketPair = { long?: Market; short?: Market } + +export type Snapshot = { + rate: number + borrowApy: number + lendApy: number + numLoans: number + priceOracle: number + ammPrice: number + totalDebt: number + totalDebtUsd: number + totalAssets: number + totalAssetsUsd: number + minted: number + redeemed: number + collateralBalance: number + collateralBalanceUsd: number + borrowedBalance: number + borrowedBalanceUsd: number + timestamp: Date + discountLiquidation: number + discountLoan: number +} + +/** More specifically, the markets where a user holds a position */ +export type UserMarkets = { + name: string + controller: Address + snapshotFirst: Date + snapshotLast: Date +}[] + +export type UserMarketStats = { + health: number + healthFull: number + n1: number + n2: number + n: number + debt: number + collateral: number + borrowed: number + softLiquidation: boolean + totalDeposited: number + loss: number + lossPct: number + collateralUp: number + oraclePrice: number + blockNumber: number + timestamp: Date +} + +export type UserMarketSnapshots = UserMarketStats[] + +export type UserCollateralEvents = { + controller: Address + user: Address + totalDeposit: number + totalDepositUsd: number + totalDepositFromUser: number + totalDepositFromUserPrecise: string + totalDepositPrecise: string + totalBorrowed: number + totalBorrowedPrecise: string + events: { + timestamp: Date + txHash: Address + type: 'Borrow' | 'Deposit' + user: Address + collateralChange: number + collateralChangeUsd?: number + loanChange: number + loanChangeUsd?: number + liquidation?: { + user: Address + liquidator: Address + collateralReceived: number + collateralReceivedUsd: number + stablecoinReceived: number + stablecoinReceivedUsd: number + debt: number + debtUsd: number + } + leverage?: { + type: string + user: Address + userCollateral: number + userCollateralFromBorrowed: number + userCollateralUsed: number + userBorrowed: number + debt: number + leverageCollateral: number + stateCollateralUsed: number + borrowedFromStateCollateral: number + borrowedFromUserCollateral: number + } + n1: number + n2: number + oraclePrice: number + }[] +} diff --git a/packages/prices-api/src/llamalend/parsers.ts b/packages/prices-api/src/llamalend/parsers.ts new file mode 100644 index 000000000..4dad07a29 --- /dev/null +++ b/packages/prices-api/src/llamalend/parsers.ts @@ -0,0 +1,146 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseMarket = (x: Responses.GetMarketsResponse['data'][number]): Models.Market => ({ + name: x.name, + controller: x.controller, + vault: x.vault, + llamma: x.llamma, + policy: x.policy, + oracle: x.oracle, + rate: parseFloat(x.rate), + apyBorrow: parseFloat(x.borrow_apy), + apyLend: parseFloat(x.lend_apy), + nLoans: x.n_loans, + priceOracle: parseFloat(x.price_oracle), + ammPrice: parseFloat(x.amm_price), + totalDebt: parseFloat(x.total_debt), + totalAssets: parseFloat(x.total_assets), + totalDebtUsd: parseFloat(x.total_debt_usd), + totalAssetsUsd: parseFloat(x.total_assets_usd), + minted: parseFloat(x.minted), + redeemed: parseFloat(x.redeemed), + mintedUsd: parseFloat(x.minted_usd), + redeemedUsd: parseFloat(x.redeemed_usd), + collateralBalance: parseFloat(x.collateral_balance), + borrowedBalance: parseFloat(x.borrowed_balance), + collateralBalanceUsd: parseFloat(x.collateral_balance_usd), + borrowedBalanceUsd: parseFloat(x.borrowed_balance_usd), + tokenCollateral: { + symbol: x.collateral_token.symbol, + address: x.collateral_token.address, + }, + tokenBorrowed: { + symbol: x.borrowed_token.symbol, + address: x.borrowed_token.address, + }, +}) + +export const parseSnapshot = (x: Responses.GetSnapshotsResponse['data'][number]): Models.Snapshot => ({ + rate: parseFloat(x.rate), + borrowApy: parseFloat(x.borrow_apy) / 100, + lendApy: parseFloat(x.lend_apy) / 100, + numLoans: x.n_loans, + priceOracle: parseFloat(x.price_oracle), + ammPrice: parseFloat(x.amm_price), + totalDebt: parseFloat(x.total_debt), + totalDebtUsd: parseFloat(x.total_debt_usd), + totalAssets: parseFloat(x.total_assets), + totalAssetsUsd: parseFloat(x.total_assets_usd), + minted: parseFloat(x.minted), + redeemed: parseFloat(x.redeemed), + collateralBalance: parseFloat(x.collateral_balance), + collateralBalanceUsd: parseFloat(x.collateral_balance_usd), + borrowedBalance: parseFloat(x.borrowed_balance), + borrowedBalanceUsd: parseFloat(x.borrowed_balance_usd), + timestamp: toUTC(x.timestamp), + discountLiquidation: x.liquidation_discount, + discountLoan: x.loan_discount, +}) + +export const parseUserMarkets = (x: Responses.GetUserMarketsResponse): Models.UserMarkets => + x.markets.map((market) => ({ + name: market.market_name, + controller: market.controller, + snapshotFirst: toUTC(market.first_snapshot), + snapshotLast: toUTC(market.last_snapshot), + })) + +export const parseUserMarketStats = (x: Responses.GetUserMarketStatsResponse) => ({ + health: x.health, + healthFull: x.health_full, + n: x.n, + n1: x.n1, + n2: x.n2, + debt: x.debt, + collateral: x.collateral, + collateralUp: x.collateral_up, + borrowed: x.borrowed, + softLiquidation: x.soft_liquidation, + totalDeposited: x.total_deposited, + loss: x.loss, + lossPct: x.loss_pct, + oraclePrice: x.oracle_price, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), +}) + +export const parseUserMarketSnapshots = (x: Responses.GetUserMarketSnapshotsResponse): Models.UserMarketSnapshots => + x.data.map(parseUserMarketStats) + +export const parseUserCollateralEvents = ( + x: Responses.GetUserCollateralEventsResponse, +): Models.UserCollateralEvents => ({ + controller: x.controller, + user: x.user, + totalDeposit: x.total_deposit, + totalDepositUsd: x.total_deposit_usd_value, + totalDepositFromUser: x.total_deposit_from_user, + totalDepositFromUserPrecise: x.total_deposit_from_user_precise, + totalDepositPrecise: x.total_deposit_precise, + totalBorrowed: x.total_borrowed, + totalBorrowedPrecise: x.total_borrowed_precise, + events: x.data.map((y) => ({ + timestamp: toUTC(y.dt), + txHash: y.transaction_hash, + type: y.type, + user: y.user, + collateralChange: y.collateral_change, + collateralChangeUsd: y.collateral_change_usd ?? undefined, + loanChange: y.loan_change, + loanChangeUsd: y.loan_change_usd ?? undefined, + liquidation: + y.liquidation === null + ? undefined + : { + user: y.liquidation.user, + liquidator: y.liquidation.liquidator, + collateralReceived: y.liquidation.collateral_received, + collateralReceivedUsd: y.liquidation.collateral_received_usd, + stablecoinReceived: y.liquidation.stablecoin_received, + stablecoinReceivedUsd: y.liquidation.stablecoin_received_usd, + debt: y.liquidation.debt, + debtUsd: y.liquidation.debt_usd, + }, + leverage: + y.leverage === null + ? undefined + : { + type: y.leverage.event_type, + user: y.leverage.user, + userCollateral: y.leverage.user_collateral, + userCollateralFromBorrowed: y.leverage.user_collateral_from_borrowed, + userCollateralUsed: y.leverage.user_collateral_used, + userBorrowed: y.leverage.user_borrowed, + debt: y.leverage.debt, + leverageCollateral: y.leverage.leverage_collateral, + stateCollateralUsed: y.leverage.state_collateral_used, + borrowedFromStateCollateral: y.leverage.borrowed_from_state_collateral, + borrowedFromUserCollateral: y.leverage.borrowed_from_user_collateral, + }, + n1: y.n1, + n2: y.n2, + oraclePrice: y.oracle_price, + })), +}) diff --git a/packages/prices-api/src/llamalend/responses.ts b/packages/prices-api/src/llamalend/responses.ts new file mode 100644 index 000000000..97a35cff2 --- /dev/null +++ b/packages/prices-api/src/llamalend/responses.ts @@ -0,0 +1,162 @@ +import type { Address, Chain } from '..' + +export type GetChainsResponse = { + data: Chain[] +} + +export type GetMarketsResponse = { + data: { + name: Address + controller: Address + vault: Address + llamma: Address + policy: Address + oracle: Address + rate: string + borrow_apy: string + lend_apy: string + n_loans: 0 + price_oracle: string + amm_price: string + total_debt: string + total_assets: string + total_debt_usd: string + total_assets_usd: string + minted: string + redeemed: string + minted_usd: string + redeemed_usd: string + collateral_balance: string + borrowed_balance: string + collateral_balance_usd: string + borrowed_balance_usd: string + collateral_token: { + symbol: string + address: Address + } + borrowed_token: { + symbol: string + address: Address + } + }[] +} + +export type GetSnapshotsResponse = { + data: [ + { + rate: string + borrow_apy: string + lend_apy: string + n_loans: number + price_oracle: string + amm_price: string + total_debt: string + total_debt_usd: string + total_assets: string + total_assets_usd: string + minted: string + redeemed: string + collateral_balance: string + collateral_balance_usd: string + borrowed_balance: string + borrowed_balance_usd: string + timestamp: string + liquidation_discount: number + loan_discount: number + }, + ] +} + +export type GetUserMarketsResponse = { + user: Address + page: number + per_page: number + count: number + markets: { + market_name: string + controller: Address + first_snapshot: string + last_snapshot: string + }[] +} + +type UserMarketStats = { + health: number + health_full: number + n1: number + n2: number + n: number + debt: number + collateral: number + borrowed: number + soft_liquidation: boolean + total_deposited: number + loss: number + loss_pct: number + collateral_up: number + oracle_price: number + block_number: number + timestamp: string +} + +export type GetUserMarketStatsResponse = UserMarketStats + +export type GetUserMarketSnapshotsResponse = { + user: Address + page: number + per_page: number + count: number + data: UserMarketStats[] +} + +export type GetUserCollateralEventsResponse = { + chain: string + controller: Address + user: Address + total_deposit: number + total_deposit_from_user: number + total_borrowed: number + total_deposit_precise: string + total_borrowed_precise: string + total_deposit_from_user_precise: string + total_deposit_usd_value: number + count: number + pagination: number + page: number + data: { + dt: string + transaction_hash: Address + type: 'Borrow' | 'Deposit' + user: Address + collateral_change: number + collateral_change_usd: number | null + loan_change: number + loan_change_usd: number | null + liquidation: { + user: Address + liquidator: Address + collateral_received: number + stablecoin_received: number + collateral_received_usd: number + stablecoin_received_usd: number + debt: number + debt_usd: number + } | null + leverage: { + event_type: string + user: Address + user_borrowed: number + user_collateral: number + user_collateral_from_borrowed: number + user_collateral_used: number + debt: number + leverage_collateral: number + state_collateral_used: number + borrowed_from_state_collateral: number + borrowed_from_user_collateral: number + } | null + n1: number + n2: number + oracle_price: number + }[] +} diff --git a/packages/prices-api/src/llamalend/util.ts b/packages/prices-api/src/llamalend/util.ts new file mode 100644 index 000000000..d18967bc8 --- /dev/null +++ b/packages/prices-api/src/llamalend/util.ts @@ -0,0 +1,5 @@ +import type { Market } from './models' + +export function tvl(market?: Market) { + return market ? market.totalAssetsUsd + market.collateralBalanceUsd : 0 +} diff --git a/packages/prices-api/src/llamma/api.ts b/packages/prices-api/src/llamma/api.ts new file mode 100644 index 000000000..4b43607df --- /dev/null +++ b/packages/prices-api/src/llamma/api.ts @@ -0,0 +1,43 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export type Endpoint = 'crvusd' | 'lending' + +export async function getEvents(endpoint: Endpoint, chain: Chain, llamma: string, page: number, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/llamma_events/${chain}/${llamma}?page=${page}&per_page=10`, + ) + + return { + events: resp.data.map(Parsers.parseEvents), + count: resp.count, + } +} + +export async function getTrades(endpoint: Endpoint, chain: Chain, llamma: string, page: number, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/${endpoint}/llamma_trades/${chain}/${llamma}?page=${page}&per_page=10`, + ) + + return { + trades: resp.data.map(Parsers.parseTrades), + count: resp.count, + } +} + +export async function getOHLC(endpoint: Endpoint, chain: Chain, llamma: string, options?: Options) { + const host = getHost(options) + + const end = Math.floor(new Date().getTime() / 1000) + const start = end - 10 * 24 * 60 * 60 // Subtract 1 month worth of seconds. + + const resp = await fetch( + `${host}/v1/${endpoint}/llamma_ohlc/${chain}/${llamma}?agg_number=1&agg_units=hour&start=${start}&end=${end}`, + ) + + return resp.data.map(Parsers.parseOHLC) +} diff --git a/packages/prices-api/src/llamma/index.ts b/packages/prices-api/src/llamma/index.ts new file mode 100644 index 000000000..caf2ce16d --- /dev/null +++ b/packages/prices-api/src/llamma/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export type { Endpoint } from './api' +export * from './api' diff --git a/packages/prices-api/src/llamma/models.ts b/packages/prices-api/src/llamma/models.ts new file mode 100644 index 000000000..3d6998f23 --- /dev/null +++ b/packages/prices-api/src/llamma/models.ts @@ -0,0 +1,54 @@ +import type { Address } from '..' + +type Deposit = { + amount: number + n1: number + n2: number +} + +type Withdrawal = { + amountBorrowed: number + amountCollateral: number +} + +export type LlammaEvent = { + provider: Address + deposit?: Deposit | null + withdrawal?: Withdrawal | null + blockNumber: number + timestamp: Date + txHash: Address +} + +export type LlammaTrade = { + idSold: number + idBought: number + tokenSold: { + symbol: string + address: Address + } + tokenBought: { + symbol: string + address: Address + } + amountSold: number + amountBought: number + price: number + buyer: Address + feeX: number + feeY: number + blockNumber: number + timestamp: Date + txHash: Address +} + +export type LlammaOHLC = { + time: Date + open: number + close: number + high: number + low: number + priceBase: number + priceOracle: number + volume: number +} diff --git a/packages/prices-api/src/llamma/parsers.ts b/packages/prices-api/src/llamma/parsers.ts new file mode 100644 index 000000000..50fea2bd9 --- /dev/null +++ b/packages/prices-api/src/llamma/parsers.ts @@ -0,0 +1,56 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseEvents = (x: Responses.GetLlammaEventsResponse['data'][number]): Models.LlammaEvent => ({ + provider: x.provider, + deposit: x.deposit + ? { + amount: x.deposit.amount, + n1: x.deposit.n1, + n2: x.deposit.n2, + } + : null, + withdrawal: x.withdrawal + ? { + amountBorrowed: x.withdrawal.amount_borrowed, + amountCollateral: x.withdrawal.amount_collateral, + } + : null, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), + txHash: x.transaction_hash, +}) + +export const parseTrades = (x: Responses.GetLlammaTradesResponse['data'][number]): Models.LlammaTrade => ({ + idSold: x.sold_id, + idBought: x.bought_id, + tokenSold: { + symbol: x.token_sold.symbol, + address: x.token_sold.address, + }, + tokenBought: { + symbol: x.token_bought.symbol, + address: x.token_bought.address, + }, + amountSold: x.amount_sold, + amountBought: x.amount_bought, + price: x.price, + buyer: x.buyer, + feeX: x.fee_x, + feeY: x.fee_y, + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), + txHash: x.transaction_hash, +}) + +export const parseOHLC = (x: Responses.GetLlammaOHLCResponse['data'][number]): Models.LlammaOHLC => ({ + time: toUTC(x.time), + open: x.open, + close: x.close, + high: x.high, + low: x.low, + priceBase: x.base_price, + priceOracle: x.oracle_price, + volume: x.volume, +}) diff --git a/packages/prices-api/src/llamma/responses.ts b/packages/prices-api/src/llamma/responses.ts new file mode 100644 index 000000000..7a6ecd88c --- /dev/null +++ b/packages/prices-api/src/llamma/responses.ts @@ -0,0 +1,58 @@ +import type { Address } from '..' + +export type GetLlammaEventsResponse = { + count: number + data: { + provider: Address + deposit: { + amount: number + n1: number + n2: number + } | null + withdrawal?: { + amount_borrowed: number + amount_collateral: number + } | null + block_number: number + timestamp: number + transaction_hash: Address + }[] +} + +export type GetLlammaTradesResponse = { + count: number + data: { + sold_id: number + bought_id: number + token_sold: { + symbol: string + address: Address + } + token_bought: { + symbol: string + address: Address + } + amount_sold: number + amount_bought: number + price: number + buyer: Address + fee_x: number + fee_y: number + block_number: number + timestamp: number + transaction_hash: Address + }[] +} + +export type GetLlammaOHLCResponse = { + data: { + time: number + open: number + close: number + high: number + low: number + base_price: number + oracle_price: number + volume: number + }[] +} diff --git a/packages/prices-api/src/ohlc/api.ts b/packages/prices-api/src/ohlc/api.ts new file mode 100644 index 000000000..6feedf8ca --- /dev/null +++ b/packages/prices-api/src/ohlc/api.ts @@ -0,0 +1,27 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getOHLC(chain: Chain, poolAddr: string, tokenMain: string, tokenRef: string, options?: Options) { + const host = getHost(options) + + const range = 120 * 60 * 1000 + const end = Math.floor(new Date().getTime() / 1000) + const start = Math.floor(end - range) + + const url = + `${host}/v1/ohlc` + + `/${chain}` + + `/${poolAddr}?` + + `main_token=${tokenMain}&` + + `reference_token=${tokenRef}&` + + `agg_number=1&` + + `agg_units=day&` + + `start=${start}&` + + `end=${end}` + + const resp = await fetch(url) + + return resp.data.map(Parsers.parseOHLC) +} diff --git a/packages/prices-api/src/ohlc/index.ts b/packages/prices-api/src/ohlc/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/ohlc/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/ohlc/models.ts b/packages/prices-api/src/ohlc/models.ts new file mode 100644 index 000000000..1a252da6e --- /dev/null +++ b/packages/prices-api/src/ohlc/models.ts @@ -0,0 +1,7 @@ +export type OHLC = { + time: Date + open: number + close: number + high: number + low: number +} diff --git a/packages/prices-api/src/ohlc/parsers.ts b/packages/prices-api/src/ohlc/parsers.ts new file mode 100644 index 000000000..e2eb96763 --- /dev/null +++ b/packages/prices-api/src/ohlc/parsers.ts @@ -0,0 +1,11 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseOHLC = (x: Responses.GetOHLCResponse['data'][number]): Models.OHLC => ({ + time: toUTC(x.time), + open: x.open, + high: x.high, + low: x.low, + close: x.close, +}) diff --git a/packages/prices-api/src/ohlc/responses.ts b/packages/prices-api/src/ohlc/responses.ts new file mode 100644 index 000000000..98a502718 --- /dev/null +++ b/packages/prices-api/src/ohlc/responses.ts @@ -0,0 +1,11 @@ +export type GetOHLCResponse = { + chain: string + address: string + data: { + time: number + open: number + close: number + high: number + low: number + }[] +} diff --git a/packages/prices-api/src/paginate.ts b/packages/prices-api/src/paginate.ts new file mode 100644 index 000000000..d4d6fa4ff --- /dev/null +++ b/packages/prices-api/src/paginate.ts @@ -0,0 +1,10 @@ +export async function paginate( + f: (page: number, offset: number) => Promise, + page = 0, + offset = 1000, +): Promise { + const xs = await f(page, offset) + const next = await (xs.length >= offset ? paginate(f, page + 1, offset) : Promise.resolve([])) + + return xs.concat(next) +} diff --git a/packages/prices-api/src/pools/api.ts b/packages/prices-api/src/pools/api.ts new file mode 100644 index 000000000..ce2b2bbe9 --- /dev/null +++ b/packages/prices-api/src/pools/api.ts @@ -0,0 +1,50 @@ +import { getHost, type Options, type Chain } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getPools(chain: Chain, page: number = 1, perPage: number = 9999, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/chains/${chain}?page=${page}&per_page=${perPage}`) + + return { + chain: resp.chain, + totals: Parsers.parsePoolTotals(resp.total), + pools: resp.data.map(Parsers.parsePool), + } +} + +export async function getPool(chain: Chain, poolAddr: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/pools/${chain}/${poolAddr}`) + + return Parsers.parsePool(resp) +} + +export async function getVolume(chain: Chain, poolAddr: string, options?: Options) { + const host = getHost(options) + + const range = 120 * 60 * 1000 + const end = Math.floor(new Date().getTime() / 1000) + const start = Math.floor(end - range) + + const resp = await fetch( + `${host}/v1/volume/usd/${chain}/${poolAddr}?` + `interval=day&` + `start=${start}&` + `end=${end}`, + ) + + return resp.data.map(Parsers.parseVolume) +} + +export async function getTvl(chain: Chain, poolAddr: string, options?: Options) { + const host = getHost(options) + + const range = 120 * 60 * 1000 + const end = Math.floor(new Date().getTime() / 1000) + const start = Math.floor(end - range) + + const resp = await fetch( + `${host}/v1/snapshots/${chain}/${poolAddr}/tvl?` + `interval=day&` + `start=${start}&` + `end=${end}`, + ) + + return resp.data.map(Parsers.parseTvl) +} diff --git a/packages/prices-api/src/pools/index.ts b/packages/prices-api/src/pools/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/pools/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/pools/models.ts b/packages/prices-api/src/pools/models.ts new file mode 100644 index 000000000..83dba2fa6 --- /dev/null +++ b/packages/prices-api/src/pools/models.ts @@ -0,0 +1,44 @@ +import type { Address } from '..' + +type Coin = { + poolIndex: number + symbol: string + address: Address +} + +export type PoolsTotals = { + tvl: number + tradingVolume24h: number + tradingFee24h: number + liquidityVolume24h: number + liquidityFee24h: number +} + +export type Pool = { + name: string + address: Address + numCoins: number + tvlUsd: number + tradingVolume24h: number + tradingFee24h: number + liquidityVolume24h: number + liquidityFee24h: number + coins: Coin[] + baseDailyApr: number + baseWeeklyApr: number + virtualPrice: number + poolMethods: string[] +} + +export type Volume = { + timestamp: Date + volume: number + fees: number +} + +export type Tvl = { + timestamp: Date + tvlUSD: number + balances: number[] + tokenPrices: number[] +} diff --git a/packages/prices-api/src/pools/parsers.ts b/packages/prices-api/src/pools/parsers.ts new file mode 100644 index 000000000..3dfe8f9ca --- /dev/null +++ b/packages/prices-api/src/pools/parsers.ts @@ -0,0 +1,45 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parsePoolTotals = (x: Responses.GetPoolsResponse['total']): Models.PoolsTotals => ({ + tvl: x.total_tvl, + tradingVolume24h: x.trading_volume_24h, + tradingFee24h: x.trading_fee_24h, + liquidityVolume24h: x.liquidity_volume_24h, + liquidityFee24h: x.liquidity_fee_24h, +}) + +export const parsePool = (x: Responses.GetPoolsResponse['data'][number]): Models.Pool => ({ + name: x.name, + address: x.address, + numCoins: x.n_coins, + tvlUsd: x.tvl_usd, + tradingVolume24h: x.trading_volume_24h, + tradingFee24h: x.trading_fee_24h, + liquidityVolume24h: x.liquidity_volume_24h, + liquidityFee24h: x.liquidity_fee_24h, + coins: + x.coins?.map((coin) => ({ + poolIndex: coin.pool_index, + symbol: coin.symbol, + address: coin.address, + })) ?? [], + baseDailyApr: x.base_daily_apr, + baseWeeklyApr: x.base_weekly_apr, + virtualPrice: x.virtual_price, + poolMethods: x.pool_methods?.map((x) => x) ?? [], +}) + +export const parseVolume = (x: Responses.GetVolumeResponse['data'][number]): Models.Volume => ({ + timestamp: toUTC(x.timestamp), + volume: x.volume, + fees: x.fees, +}) + +export const parseTvl = (x: Responses.GetTvlResponse['data'][number]): Models.Tvl => ({ + timestamp: toUTC(x.timestamp), + tvlUSD: x.tvl_usd ?? 0, + balances: [...x.balances], + tokenPrices: [...x.token_prices], +}) diff --git a/packages/prices-api/src/pools/responses.ts b/packages/prices-api/src/pools/responses.ts new file mode 100644 index 000000000..097ff4cb4 --- /dev/null +++ b/packages/prices-api/src/pools/responses.ts @@ -0,0 +1,54 @@ +import type { Address } from '..' + +type Coin = { + pool_index: number + symbol: string + address: Address +} + +type Pool = { + name: string + address: Address + n_coins: number + tvl_usd: number + trading_volume_24h: number + trading_fee_24h: number + liquidity_volume_24h: number + liquidity_fee_24h: number + coins?: Coin[] + base_daily_apr: number + base_weekly_apr: number + virtual_price: number + pool_methods?: string[] +} + +export type GetPoolsResponse = { + chain: string + total: { + total_tvl: number + trading_volume_24h: number + trading_fee_24h: number + liquidity_volume_24h: number + liquidity_fee_24h: number + } + data: Pool[] +} + +export type GetPoolResponse = Pool + +export type GetVolumeResponse = { + data: { + timestamp: number + volume: number + fees: number + }[] +} + +export type GetTvlResponse = { + data: { + timestamp: number + tvl_usd?: number + balances: number[] + token_prices: number[] + }[] +} diff --git a/packages/prices-api/src/proposal/api.ts b/packages/prices-api/src/proposal/api.ts new file mode 100644 index 000000000..0c9844d80 --- /dev/null +++ b/packages/prices-api/src/proposal/api.ts @@ -0,0 +1,47 @@ +import { getHost, type Options } from '..' +import { fetchJson as fetch, FetchError } from '../fetch' +import type * as Responses from './responses' +import type * as Models from './models' +import * as Parsers from './parsers' + +export async function getProposals( + page: number, + search: string, + type: Models.ProposalType, + status: Models.ProposalStatus, + options?: Options, +) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/dao/proposals?pagination=10&page=${page}&search_string=${search}&type_filter=${type}&status_filter=${status}`, + ) + + return { + proposals: resp.proposals.map(Parsers.parseProposal), + count: resp.count, + } +} + +export async function getProposal(proposalId: number, proposalType: Models.ProposalType, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/dao/proposals/details/${proposalType}/${proposalId}`, + ) + + return Parsers.parseProposalDetails(resp) +} + +export async function getUserProposalVotes(user: string, options?: Options) { + try { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/dao/proposals/votes/user/${user}?pagination=100&page=1`, + ) + + return resp.data.map(Parsers.parseUserProposalVote) + } catch (err) { + if (err instanceof FetchError) { + return [] + } else throw err + } +} diff --git a/packages/prices-api/src/proposal/index.ts b/packages/prices-api/src/proposal/index.ts new file mode 100644 index 000000000..31d31e774 --- /dev/null +++ b/packages/prices-api/src/proposal/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export * from './util' +export * from './api' diff --git a/packages/prices-api/src/proposal/models.ts b/packages/prices-api/src/proposal/models.ts new file mode 100644 index 000000000..252cf66e0 --- /dev/null +++ b/packages/prices-api/src/proposal/models.ts @@ -0,0 +1,46 @@ +import type { Address } from '..' + +export const proposalTypes = ['all', 'ownership', 'parameter', 'other'] as const +export type ProposalType = (typeof proposalTypes)[number] + +export const proposalStatusses = ['all', 'active', 'denied', 'passed', 'executed'] as const +export type ProposalStatus = (typeof proposalStatusses)[number] + +export type Proposal = { + timestamp: Date + id: number + type: ProposalType + metadata: string + proposer: Address + block: number + start: number + end: number + quorum: number // Voting power in veCRV. + support: number // Voting power in veCRV. + voteCount: number // An actual vote *count* + votesFor: number // Voting power in veCRV. + votesAgainst: number // Voting power in veCRV. + executed: boolean + totalSupply: number // Voting power in veCRV. + txCreation: Address +} + +export type ProposalDetails = { + txExecution?: Address + script: string + votes: { + voter: Address + supports: boolean + votingPower: number + }[] +} + +export type UserProposalVote = { + proposal: Proposal + votes: { + voter: Address + supports: boolean + weight: bigint + txHash: Address + }[] +} diff --git a/packages/prices-api/src/proposal/parsers.ts b/packages/prices-api/src/proposal/parsers.ts new file mode 100644 index 000000000..c3235a9dc --- /dev/null +++ b/packages/prices-api/src/proposal/parsers.ts @@ -0,0 +1,47 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseProposal = (x: Responses.GetProposalsResponse['proposals'][number]): Models.Proposal => ({ + timestamp: toUTC(x.dt), + id: x.vote_id, + type: x.vote_type.toLocaleLowerCase() === 'parameter' ? 'parameter' : 'ownership', + metadata: x.metadata?.startsWith('"') // Remove weird starting quote, if present. + ? x.metadata.substring(1) + : (x.metadata ?? ''), + proposer: x.creator, + block: x.snapshot_block, + start: x.start_date, + end: x.start_date + 604800, + quorum: Number(BigInt(x.min_accept_quorum)) / 10 ** 18, + support: Number(BigInt(x.support_required)) / 10 ** 18, + voteCount: x.vote_count, + votesFor: Number(BigInt(x.votes_for)) / 10 ** 18, + votesAgainst: Number(BigInt(x.votes_against)) / 10 ** 18, + executed: x.executed, + totalSupply: Number(BigInt(x.total_supply)) / 10 ** 18, + txCreation: x.transaction_hash, +}) + +export const parseProposalDetails = ( + x: Responses.GetProposalDetailsResponse, +): Models.Proposal & Models.ProposalDetails => ({ + ...parseProposal(x), + txExecution: x.execution_tx ? x.execution_tx : undefined, + script: x.script, + votes: x.votes.map((vote) => ({ + voter: vote.voter, + supports: vote.supports, + votingPower: Number(BigInt(vote.voting_power)) / 10 ** 18, + })), +}) + +export const parseUserProposalVote = (x: Responses.GetUserProposalVotes['data'][number]): Models.UserProposalVote => ({ + proposal: parseProposal(x.proposal), + votes: x.votes.map((vote) => ({ + voter: vote.voter, + supports: vote.supports, + weight: BigInt(Math.round(parseFloat(vote.voting_power))), + txHash: vote.transaction_hash, + })), +}) diff --git a/packages/prices-api/src/proposal/responses.ts b/packages/prices-api/src/proposal/responses.ts new file mode 100644 index 000000000..1e04b7cc0 --- /dev/null +++ b/packages/prices-api/src/proposal/responses.ts @@ -0,0 +1,50 @@ +import type { Address } from '..' +import type { ProposalType } from './models' + +type Proposal = { + vote_id: number + vote_type: ProposalType + creator: Address + start_date: number + snapshot_block: number + ipfs_metadata: string + metadata?: string + votes_for: string + votes_against: string + vote_count: number + support_required: string + min_accept_quorum: string + total_supply: string + executed: boolean + transaction_hash: Address + dt: string +} + +export type GetProposalsResponse = { + proposals: Proposal[] + count: number +} + +export type GetProposalDetailsResponse = Proposal & { + execution_tx: Address | null + script: string + votes: { + voter: Address + supports: boolean + voting_power: string + }[] +} + +export type GetUserProposalVotes = { + page: number + count: number + data: { + proposal: Proposal + votes: { + voter: Address + supports: boolean + voting_power: string + transaction_hash: Address + }[] + }[] +} diff --git a/packages/prices-api/src/proposal/util.ts b/packages/prices-api/src/proposal/util.ts new file mode 100644 index 000000000..1b7286299 --- /dev/null +++ b/packages/prices-api/src/proposal/util.ts @@ -0,0 +1,30 @@ +import type { Proposal, ProposalStatus } from './models' + +export function getStatus(proposal: Proposal): ProposalStatus { + if (proposal.executed) { + return 'executed' + } + + if (proposal.end > new Date().getTime() / 1000) { + return 'active' + } + + if (hasWon(proposal) && hasReachedQuorum(proposal) && hasReachedSupport(proposal)) { + return 'passed' + } + + return 'denied' +} + +export function hasReachedSupport(proposal: Proposal): boolean { + return proposal.votesFor > proposal.support * proposal.quorum * proposal.totalSupply +} + +export function hasReachedQuorum(proposal: Proposal): boolean { + return proposal.votesFor > proposal.quorum * proposal.totalSupply +} + +export function hasWon(proposal: Proposal): boolean { + const totalVotes = proposal.votesFor + proposal.votesAgainst + return proposal.votesFor > totalVotes * 0.51 +} diff --git a/packages/prices-api/src/revenue/api.ts b/packages/prices-api/src/revenue/api.ts new file mode 100644 index 000000000..d31c83958 --- /dev/null +++ b/packages/prices-api/src/revenue/api.ts @@ -0,0 +1,79 @@ +import { getHost, type Options } from '..' +import { fetchJson as fetch } from '../fetch' +import { paginate } from '../paginate' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +const API_URL_OLD = 'https://api-py.llama.airforce/curve/v1' + +export async function getByChain(options?: Options) { + const host = getHost(options?.host ? options : { host: API_URL_OLD }) + const resp = await fetch(`${host}/protocol/revenue/chains`, undefined, options?.signal) + + return resp.revenue.map(Parsers.parseChainRevenue) +} + +export async function getTopPools(chain: string, numPools = 10, options?: Options) { + const chainStr = chain === 'ethereum' ? 'mainnet' : chain + const host = getHost(options?.host ? options : { host: API_URL_OLD }) + + const resp = await fetch(`${host}/protocol/revenue/${chainStr}/toppools/${numPools}`) + + return resp.revenue.map(Parsers.parseTopPools) +} + +export async function getCrvUsdWeekly(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/fees/crvusd/weekly`) + + return resp.fees.map(Parsers.parseCrvUsdWeekly) +} + +export async function getPoolsWeekly(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/fees/pools/weekly`) + + return resp.fees.map(Parsers.parsePoolsWeekly) +} + +export async function getCushions(chain: string, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/fees/${chain}/pending`) + + return resp.data.map(Parsers.parseCushion) +} + +export async function getDistributions(options?: Options) { + const host = getHost(options) + const fs = (page: number) => + fetch(`${host}/v1/dao/fees/distributions?page=${page}&per_page=100`).then( + (resp) => resp.distributions.map(Parsers.parseDistribution), + ) + + const distributions = await paginate(fs, 1, 100) + + return distributions +} + +export async function getCowSwapSettlements(timestamp?: number, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/v1/dao/fees/settlements${timestamp ? '?timestamp=' + timestamp.toString() : ''}`, + ) + + return resp.data.map(Parsers.parseCowSwapSettlement) +} + +export async function getFeesCollected(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/fees/collected`) + + return resp.data.map(Parsers.parseFeesCollected) +} + +export async function getFeesStaged(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/dao/fees/staged`) + + return resp.data.map(Parsers.parseFeesStaged) +} diff --git a/packages/prices-api/src/revenue/index.ts b/packages/prices-api/src/revenue/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/revenue/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/revenue/models.ts b/packages/prices-api/src/revenue/models.ts new file mode 100644 index 000000000..1f6547aee --- /dev/null +++ b/packages/prices-api/src/revenue/models.ts @@ -0,0 +1,67 @@ +import type { Address, Chain } from '..' + +export type ChainRevenue = { + chain: string + totalDailyFeesUSD: number +} + +export type ChainTopPoolRevenue = { + name: string + totalDailyFeesUSD: number +} + +export type CrvUsdWeekly = { + timestamp: Date + controller: Address + collateral: string + feesUsd: number +} + +export type PoolsWeekly = { + timestamp: Date + chain: Chain + feesUsd: number +} + +export type Cushion = { + pool: Address + name: string + adminFees: number[] + usdValue: number +} + +export type Distribution = { + timestamp: Date + feesUsd: number +} + +export type CowSwapSettlement = { + timestamp: Date + coin: { + lpToken: boolean + symbol: string + address: Address + precision: number + } + amount: bigint + amountFee: bigint + amountReceived: number + routerReceived: number + epoch: number + txHash: Address + blockNumber: number +} + +type Fees = { + coin: { + lpToken: boolean + symbol: string + address: Address + decimals: number + } + amount: number + amountUsd: number +} + +export type FeesCollected = Fees +export type FeesStaged = Fees diff --git a/packages/prices-api/src/revenue/parsers.ts b/packages/prices-api/src/revenue/parsers.ts new file mode 100644 index 000000000..1d4baab62 --- /dev/null +++ b/packages/prices-api/src/revenue/parsers.ts @@ -0,0 +1,82 @@ +import type { Chain } from '..' +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseChainRevenue = (x: Responses.GetByChainResponse['revenue'][number]): Models.ChainRevenue => ({ + chain: x.chain, + totalDailyFeesUSD: x.totalDailyFeesUSD, +}) + +export const parseTopPools = (x: Responses.GetTopPoolsResponse['revenue'][number]): Models.ChainTopPoolRevenue => ({ + name: x.name, + totalDailyFeesUSD: x.totalDailyFeesUSD, +}) + +export const parseCrvUsdWeekly = (x: Responses.GetCrvUsdWeeklyResponse['fees'][number]): Models.CrvUsdWeekly => ({ + timestamp: toUTC(x.timestamp), + controller: x.controller, + collateral: x.collateral, + feesUsd: x.fees_usd, +}) + +export const parsePoolsWeekly = (x: Responses.GetPoolsWeeklyResponse['fees'][number]): Models.PoolsWeekly => ({ + timestamp: toUTC(x.timestamp), + chain: x.chain as Chain, + feesUsd: x.fees_usd, +}) + +export const parseCushion = (x: Responses.GetCushionsResponse['data'][number]): Models.Cushion => ({ + pool: x.pool, + name: x.name, + adminFees: x.admin_fees, + usdValue: x.usd_value, +}) + +export const parseDistribution = ( + x: Responses.GetDistributionsResponse['distributions'][number], +): Models.Distribution => ({ + timestamp: toUTC(x.timestamp), + feesUsd: x.fees_usd, +}) + +export const parseCowSwapSettlement = ( + x: Responses.GetCowSwapSettlementsResponse['data'][number], +): Models.CowSwapSettlement => ({ + timestamp: toUTC(x.dt), + coin: { + lpToken: x.coin.lp_token, + symbol: x.coin.symbol, + address: x.coin.address, + precision: x.coin.precision, + }, + amount: BigInt(x.amount), + amountFee: BigInt(x.fee_amount), + amountReceived: x.amount_received, + routerReceived: x.router_received, + epoch: x.epoch, + txHash: x.tx_hash, + blockNumber: x.block_number, +}) + +export const parseFeesCollected = (x: Responses.GetFeesCollectedResponse['data'][number]): Models.FeesCollected => ({ + coin: { + lpToken: x.coin.lp_token, + symbol: x.coin.symbol, + address: x.coin.address, + decimals: x.coin.precision, + }, + amount: parseFloat(x.amount), + amountUsd: parseFloat(x.usd_amount), +}) + +export const parseFeesStaged = (x: Responses.GetFeesStagedResponse['data'][number]): Models.FeesStaged => ({ + coin: { + lpToken: x.coin.lp_token, + symbol: x.coin.symbol, + address: x.coin.address, + decimals: x.coin.precision, + }, + amount: parseFloat(x.amount), + amountUsd: parseFloat(x.usd_amount), +}) diff --git a/packages/prices-api/src/revenue/responses.ts b/packages/prices-api/src/revenue/responses.ts new file mode 100644 index 000000000..f38a303d0 --- /dev/null +++ b/packages/prices-api/src/revenue/responses.ts @@ -0,0 +1,86 @@ +import type { Address } from '..' + +export type GetCushionsResponse = { + data: { + pool: Address + name: string + admin_fees: number[] + usd_value: number + }[] +} + +export type GetByChainResponse = { + revenue: { + chain: string + totalDailyFeesUSD: number + }[] +} + +export type GetTopPoolsResponse = { + revenue: { + name: string + totalDailyFeesUSD: number + }[] +} + +export type GetCrvUsdWeeklyResponse = { + fees: { + controller: Address + collateral: string + fees_usd: number + timestamp: string + }[] +} + +export type GetPoolsWeeklyResponse = { + fees: { + chain: string + fees_usd: number + timestamp: string + }[] +} + +export type GetDistributionsResponse = { + distributions: { + timestamp: string + fees_usd: number + }[] +} + +export type GetCowSwapSettlementsResponse = { + data: { + coin: { + lp_token: boolean + symbol: string + address: Address + precision: number + } + amount: string + fee_amount: string + amount_received: number + router_received: number + epoch: number + tx_hash: Address + block_number: number + dt: string + }[] +} + +type GetFeesResponse = { + coin: { + lp_token: boolean + symbol: string + address: Address + precision: number + } + amount: string + usd_amount: string +} + +export type GetFeesCollectedResponse = { + data: GetFeesResponse[] +} + +export type GetFeesStagedResponse = { + data: GetFeesResponse[] +} diff --git a/packages/prices-api/src/savings/api.ts b/packages/prices-api/src/savings/api.ts new file mode 100644 index 000000000..ce5474330 --- /dev/null +++ b/packages/prices-api/src/savings/api.ts @@ -0,0 +1,44 @@ +import { getHost, type Options } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getStatistics(options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/crvusd/savings/statistics`) + + return Parsers.parseStatistics(resp) +} + +export async function getEvents(page: number, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/crvusd/savings/events?page=${page}&per_page=10`) + + return { + count: resp.count, + events: resp.events.map(Parsers.parseEvent), + } +} + +export async function getYield(options?: Options) { + const host = getHost(options) + + const end = Math.floor(new Date().getTime() / 1000) + const start = end - 10 * 24 * 60 * 60 // Subtract 1 month worth of seconds. + + const resp = await fetch( + `${host}/v1/crvusd/savings/yield?agg_number=1&agg_units=hour&start=${start}&end=${end}`, + ) + + return resp.data.map(Parsers.parseYield) +} + +export async function getRevenue(page: number, options?: Options) { + const host = getHost(options) + const resp = await fetch(`${host}/v1/crvusd/savings/revenue${page}&per_page=100`) + + return { + totalDistributed: resp.total_distributed, + history: resp.history.map(Parsers.parseRevenue), + } +} diff --git a/packages/prices-api/src/savings/index.ts b/packages/prices-api/src/savings/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/savings/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/savings/models.ts b/packages/prices-api/src/savings/models.ts new file mode 100644 index 000000000..5b0cf48fd --- /dev/null +++ b/packages/prices-api/src/savings/models.ts @@ -0,0 +1,39 @@ +import type { Address } from '..' + +export type Event = { + type: 'deposit' | 'withdraw' + sender: Address + owner: Address + receiver?: Address + assets: bigint + supply: bigint + blockNumber: number + timestamp: Date + txHash: Address +} + +export type Yield = { + timestamp: Date + assets: number + supply: number + apyProjected: string +} + +export type Revenue = { + strategy: Address + gain: bigint + loss: bigint + currentDebt: bigint + totalRefunds: bigint + feesTotal: bigint + feesProtocol: bigint + txHash: Address + timestamp: Date +} + +export type Statistics = { + lastUpdated: Date + lastUpdatedBlock: number + aprProjected: number + supply: number +} diff --git a/packages/prices-api/src/savings/parsers.ts b/packages/prices-api/src/savings/parsers.ts new file mode 100644 index 000000000..9fbcb5d6d --- /dev/null +++ b/packages/prices-api/src/savings/parsers.ts @@ -0,0 +1,41 @@ +import { toUTC } from '../timestamp' +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseEvent = (x: Responses.GetEventsResponse['events'][number]): Models.Event => ({ + type: x.action_type, + sender: x.sender, + owner: x.owner, + receiver: x.receiver ? x.receiver : undefined, + assets: BigInt(x.assets), + supply: BigInt(x.shares), + blockNumber: x.block_number, + timestamp: toUTC(x.timestamp), + txHash: x.transaction_hash, +}) + +export const parseYield = (x: Responses.GetYieldResponse['data'][number]): Models.Yield => ({ + timestamp: toUTC(x.timestamp), + assets: x.assets, + supply: x.supply, + apyProjected: x.proj_apy, +}) + +export const parseRevenue = (x: Responses.GetRevenueResponse['history'][number]): Models.Revenue => ({ + strategy: x.strategy, + gain: BigInt(x.gain), + loss: BigInt(x.loss), + currentDebt: BigInt(x.current_debt), + totalRefunds: BigInt(x.total_refunds), + feesTotal: BigInt(x.total_fees), + feesProtocol: BigInt(x.protocol_fees), + txHash: x.tx_hash, + timestamp: toUTC(x.dt), +}) + +export const parseStatistics = (x: Responses.GetStatisticsResponse): Models.Statistics => ({ + lastUpdated: toUTC(x.last_updated), + lastUpdatedBlock: x.last_updated_block, + aprProjected: x.proj_apr, + supply: x.supply, +}) diff --git a/packages/prices-api/src/savings/responses.ts b/packages/prices-api/src/savings/responses.ts new file mode 100644 index 000000000..928c05a43 --- /dev/null +++ b/packages/prices-api/src/savings/responses.ts @@ -0,0 +1,48 @@ +import type { Address } from '..' + +export type GetEventsResponse = { + count: number + events: { + action_type: 'deposit' | 'withdraw' + sender: Address + owner: Address + receiver?: Address + assets: string + shares: string + block_number: number + timestamp: string + transaction_hash: Address + }[] +} + +export type GetYieldResponse = { + data: { + timestamp: number + assets: number + supply: number + proj_apy: string + }[] +} + +export type GetRevenueResponse = { + count: number + total_distributed: string + history: { + strategy: Address + gain: string + loss: string + current_debt: string + total_refunds: string + total_fees: string + protocol_fees: string + tx_hash: Address + dt: string + }[] +} + +export type GetStatisticsResponse = { + last_updated: string + last_updated_block: number + proj_apr: number + supply: number +} diff --git a/packages/prices-api/src/solver/api.ts b/packages/prices-api/src/solver/api.ts new file mode 100644 index 000000000..9b14fab8e --- /dev/null +++ b/packages/prices-api/src/solver/api.ts @@ -0,0 +1,13 @@ +import { getHost, type Options, type Address } from '..' +import { fetchJson as fetch } from '../fetch' +import type * as Responses from './responses' +import * as Parsers from './parsers' + +export async function getCompetition(tx: Address, options?: Options) { + const host = getHost(options) + const resp = await fetch( + `${host}/mainnet/api/v1/solver_competition/by_tx_hash/${tx}`, + ) + + return Parsers.parseSolverCompetition(resp) +} diff --git a/packages/prices-api/src/solver/index.ts b/packages/prices-api/src/solver/index.ts new file mode 100644 index 000000000..36e449977 --- /dev/null +++ b/packages/prices-api/src/solver/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './api' diff --git a/packages/prices-api/src/solver/models.ts b/packages/prices-api/src/solver/models.ts new file mode 100644 index 000000000..220109724 --- /dev/null +++ b/packages/prices-api/src/solver/models.ts @@ -0,0 +1,19 @@ +import type { Address } from '..' + +export type SolverCompetition = { + auctionStartBlock: number + orders: Address[] + prices: Record + solutions: { + solver: string + solverAddress: Address + score: bigint + ranking: number + clearingPrices: Record + orders: { + id: Address + sellAmount: bigint + buyAmount: bigint + }[] + }[] +} diff --git a/packages/prices-api/src/solver/parsers.ts b/packages/prices-api/src/solver/parsers.ts new file mode 100644 index 000000000..2e7aa49ba --- /dev/null +++ b/packages/prices-api/src/solver/parsers.ts @@ -0,0 +1,20 @@ +import type * as Responses from './responses' +import type * as Models from './models' + +export const parseSolverCompetition = (x: Responses.GetSolverCompetitionResponse): Models.SolverCompetition => ({ + auctionStartBlock: x.auctionStartBlock, + orders: x.auction.orders.map((x) => x), + prices: Object.fromEntries(Object.entries(x.auction.prices).map(([key, value]) => [key, BigInt(value)])), + solutions: x.solutions.map((sol) => ({ + solver: sol.solver, + solverAddress: sol.solverAddress, + score: BigInt(sol.score), + ranking: sol.ranking, + clearingPrices: Object.fromEntries(Object.entries(sol.clearingPrices).map(([k, v]) => [k, BigInt(v)])), + orders: sol.orders.map((order) => ({ + id: order.id, + sellAmount: BigInt(order.sellAmount), + buyAmount: BigInt(order.buyAmount), + })), + })), +}) diff --git a/packages/prices-api/src/solver/responses.ts b/packages/prices-api/src/solver/responses.ts new file mode 100644 index 000000000..8dff62dea --- /dev/null +++ b/packages/prices-api/src/solver/responses.ts @@ -0,0 +1,21 @@ +import type { Address } from '..' + +export type GetSolverCompetitionResponse = { + auctionStartBlock: number + auction: { + orders: Address[] + prices: Record + } + solutions: { + solver: string + solverAddress: Address + score: string + ranking: number + clearingPrices: Record + orders: { + id: Address + sellAmount: number + buyAmount: number + }[] + }[] +} diff --git a/packages/prices-api/src/timestamp.ts b/packages/prices-api/src/timestamp.ts new file mode 100644 index 000000000..1f40665f1 --- /dev/null +++ b/packages/prices-api/src/timestamp.ts @@ -0,0 +1,30 @@ +const TZ_OFFSET = /[+-]\d{2}:?\d{2}$/ + +/** + * Converts a timestamp string or number to UTC Date object + * @param timestamp - Timestamp as string (ISO format) or number (unix seconds) + * @returns UTC Date object + * @example + * toUTC(1234567890) + * toUTC("2024-01-01T00:00:00.000Z") + * toUTC("2024-01-01T00:00:00.000Z+01:00") + * toUTC("2024-01-01T00:00:00") // (assumes UTC) + */ +export function toUTC(timestamp: string | number): Date { + // Convert actual unix timestamp numbers to Date. + if (typeof timestamp === 'number') { + return new Date(timestamp * 1000) + } + + // Convert actual unix timestamp strings to Date. + const parsed = Number(timestamp) + if (!Number.isNaN(parsed)) { + return new Date(parsed * 1000) + } + + // Append 'Z' only if no timezone info is present, assuming UTC (Z or +00:00 for example). + const hasTimezone = timestamp.endsWith('Z') || TZ_OFFSET.test(timestamp) + const utcTimestamp = hasTimezone ? timestamp : `${timestamp}Z` + + return new Date(utcTimestamp) +} diff --git a/packages/prices-api/tsconfig.json b/packages/prices-api/tsconfig.json new file mode 100644 index 000000000..aa62a4bc9 --- /dev/null +++ b/packages/prices-api/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "node", + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "exclude": ["node_modules"], + "include": ["src/**/*.ts"] +} diff --git a/packages/ui/src/Accordion/Accordion.tsx b/packages/ui/src/Accordion/Accordion.tsx index 4563f9d15..a48c19640 100644 --- a/packages/ui/src/Accordion/Accordion.tsx +++ b/packages/ui/src/Accordion/Accordion.tsx @@ -1,9 +1,7 @@ import type { AriaButtonProps } from 'react-aria' - import * as React from 'react' import { useButton } from 'react-aria' import styled from 'styled-components' - import Icon from 'ui/src/Icon/Icon' function Button( diff --git a/packages/ui/src/ConnectWalletPrompt/index.tsx b/packages/ui/src/ConnectWalletPrompt/index.tsx deleted file mode 100644 index 760fb2b41..000000000 --- a/packages/ui/src/ConnectWalletPrompt/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import styled from 'styled-components' -import React from 'react' -import Image from 'next/image' -import Button from '@mui/material/Button' -import Spinner from '../Spinner' - -import { LogoImg } from '../images' -import { getBackgroundUrl } from 'ui/src/utils' - -type ConnectWalletPromptProps = { - connectWallet: () => void - isLoading: boolean - description: string - connectText: string - loadingText: string - theme: 'light' | 'dark' -} - -const ConnectWalletPrompt: React.FC = ({ - connectWallet, - isLoading, - description, - connectText, - loadingText, - theme = 'light', -}) => { - const BackgroundSvg = getBackgroundUrl(theme) - return ( - - - - - - Enter Curve - - - -

{description}

- {isLoading ? ( - - ) : ( - - )} -
-
- ) -} - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - padding: var(--spacing-4); - background-color: var(--table--background-color); - max-width: 50rem; - width: 100%; - flex: 1; -` - -const CurveLogo = styled(Image)` - width: 3rem; - height: 3rem; - margin: 0 auto; - @media (min-width: 43.75rem) { - width: 5.5rem; - height: 5.5rem; - } -` - -const ImageWrapper = styled.div` - display: flex; - position: relative; - width: 100%; - flex: 1; -` - -const OverlayWrapper = styled.div` - position: absolute; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -` - -const StyledBackground = styled.img` - width: 1400px; - max-width: 100%; - height: 100%; - object-fit: contain; -` - -const OverlayText = styled.p` - font-size: 2.5rem; - font-weight: bold; - color: inherit; - @media (min-width: 43.75rem) { - font-size: 4rem; - } -` - -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - gap: var(--spacing-2); - width: 100%; - margin: 0 auto; -` - -export default ConnectWalletPrompt diff --git a/packages/ui/src/globals.d.ts b/packages/ui/src/globals.d.ts index cfddf799c..e5fb854ff 100644 --- a/packages/ui/src/globals.d.ts +++ b/packages/ui/src/globals.d.ts @@ -15,9 +15,11 @@ declare module '*.webp' { export default content } +/// declare module '*.svg' { - import React = require('react') - export const ReactComponent: React.FC> - const src: string - export default src + const ReactComponent: React.FC> + const content: string + + export { ReactComponent } + export default content } diff --git a/packages/ui/src/images/crv-logo/webp/CRV-sm.webp b/packages/ui/src/images/crv-logo/webp/CRV-sm.webp deleted file mode 100644 index e7b3bde2c..000000000 Binary files a/packages/ui/src/images/crv-logo/webp/CRV-sm.webp and /dev/null differ diff --git a/packages/ui/src/images/crv-logo/webp/CRV-xs.webp b/packages/ui/src/images/crv-logo/webp/CRV-xs.webp deleted file mode 100644 index c3aa41a4b..000000000 Binary files a/packages/ui/src/images/crv-logo/webp/CRV-xs.webp and /dev/null differ diff --git a/packages/ui/src/images/curve-white.svg b/packages/ui/src/images/curve-white.svg deleted file mode 100644 index 9bc96819b..000000000 --- a/packages/ui/src/images/curve-white.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui/src/images/discord.svg b/packages/ui/src/images/discord.svg deleted file mode 100644 index 38db359e2..000000000 --- a/packages/ui/src/images/discord.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui/src/images/github.svg b/packages/ui/src/images/github.svg deleted file mode 100644 index 40ce7072e..000000000 --- a/packages/ui/src/images/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui/src/images/index.ts b/packages/ui/src/images/index.ts index fc5eb0b29..39406264d 100644 --- a/packages/ui/src/images/index.ts +++ b/packages/ui/src/images/index.ts @@ -1,22 +1,13 @@ // LOGO export { default as LogoImg } from './curve-logo.png' -export { ReactComponent as RCLogoWhite } from './curve-white.svg' export { ReactComponent as RCLogoSM } from './curve-logo-sm.svg' export { ReactComponent as RCLogoText } from './curve-logo-text.svg' export { ReactComponent as RCCrossCurve } from './logo/crosscurve.svg' -export { default as RCCrvLogoSM } from './crv-logo/webp/CRV-sm.webp' export { default as RCScrvUSDLogoSM } from './scrvusd-logo/webp/scrvUSD-sm.webp' export { default as RCCrvUSDLogoSM } from './crvusd-logo/webp/crvUSD-sm.webp' -export { default as RCCrvLogoXS } from './crv-logo/webp/CRV-xs.webp' export { default as RCScrvUSDLogoXS } from './scrvusd-logo/webp/scrvUSD-xs.webp' export { default as RCCrvUSDLogoXS } from './crvusd-logo/webp/crvUSD-xs.webp' -// SOCIAL -export { ReactComponent as RCDiscordLogo } from './discord.svg' -export { ReactComponent as RCGithubLogo } from './github.svg' -export { ReactComponent as RCTelegramLogo } from './telegram.svg' -export { ReactComponent as RCTwitterLogo } from './twitter.svg' - // ICON export { ReactComponent as RCPointsIcon } from './points-icon.svg' export { ReactComponent as RCPinBottom } from './pin-bottom.svg' diff --git a/packages/ui/src/images/telegram.svg b/packages/ui/src/images/telegram.svg deleted file mode 100644 index cb913deee..000000000 --- a/packages/ui/src/images/telegram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui/src/images/twitter.svg b/packages/ui/src/images/twitter.svg deleted file mode 100644 index e6e6059f2..000000000 --- a/packages/ui/src/images/twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui/src/utils/utilsConstants.ts b/packages/ui/src/utils/utilsConstants.ts index b7d935bf0..b1c3dfc3f 100644 --- a/packages/ui/src/utils/utilsConstants.ts +++ b/packages/ui/src/utils/utilsConstants.ts @@ -1,3 +1,5 @@ +import { ThemeKey } from 'curve-ui-kit/src/themes/basic-theme' + export const CDN_ROOT_URL = 'https://cdn.jsdelivr.net' export const CURVE_CDN_URL = `${CDN_ROOT_URL}/gh/curvefi` export const CURVE_ASSETS_URL = `${CURVE_CDN_URL}/curve-assets` @@ -12,5 +14,5 @@ export const getImageBaseUrl = (blockchainId: string) => export const getBlockchainIconUrl = (blockchainId: string) => `${CURVE_ASSETS_URL}/chains/${blockchainId}.png` -export const getBackgroundUrl = (theme: 'light' | 'dark') => - `${CURVE_ASSETS_URL}/branding/curve_illustration-${theme === 'light' ? 'light' : 'dark'}.svg` +export const getBackgroundUrl = (theme: ThemeKey) => + `${CURVE_ASSETS_URL}/branding/curve_illustration-${theme === 'dark' ? 'dark' : 'light'}.svg` diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 47bd17345..ae62e109e 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -6,6 +6,6 @@ "@/*": ["*"] } }, - "include": ["src", "../../packages/curve-ui-kit/**/*.d.ts"], + "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts", "../../packages/curve-ui-kit/**/*.d.ts"], "exclude": ["dist", "build", "node_modules"] } diff --git a/tests/cypress/support/ui.ts b/tests/cypress/support/ui.ts index e4c12123a..056f291e3 100644 --- a/tests/cypress/support/ui.ts +++ b/tests/cypress/support/ui.ts @@ -22,12 +22,8 @@ export const isInViewport = ($el: JQuery) => { const height = Cypress.$(cy.state('window')).height()! const width = Cypress.$(cy.state('window')).width()! const rect = $el[0].getBoundingClientRect() - return ( - rect.top + rect.height / 2 > 0 && - rect.top + rect.height / 2 < height && - rect.left + rect.width / 2 > 0 && - rect.left + rect.width / 2 < width - ) + const [x, y] = [rect.left + rect.width / 2, rect.top + rect.height / 2] + return y > 0 && y < height && x > 0 && x < width } export const checkIsDarkMode = (win: Cypress.AUTWindow) => win.matchMedia('(prefers-color-scheme: dark)').matches diff --git a/yarn.lock b/yarn.lock index 9f38b594f..00b502d1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2873,7 +2873,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.25.7": +"@babel/runtime@npm:^7.24.4": version: 7.25.7 resolution: "@babel/runtime@npm:7.25.7" dependencies: @@ -3365,6 +3365,18 @@ __metadata: languageName: node linkType: hard +"@curvefi/prices-api@workspace:packages/prices-api": + version: 0.0.0-use.local + resolution: "@curvefi/prices-api@workspace:packages/prices-api" + dependencies: + eslint: "npm:*" + eslint-config-custom: "npm:*" + typescript: "npm:*" + peerDependencies: + typescript: "*" + languageName: unknown + linkType: soft + "@curvefi/stablecoin-api@npm:^1.5.8": version: 1.5.8 resolution: "@curvefi/stablecoin-api@npm:1.5.8" @@ -3443,7 +3455,7 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^11.13.0, @emotion/cache@npm:^11.13.1": +"@emotion/cache@npm:^11.13.0": version: 11.13.1 resolution: "@emotion/cache@npm:11.13.1" dependencies: @@ -3456,6 +3468,19 @@ __metadata: languageName: node linkType: hard +"@emotion/cache@npm:^11.13.5": + version: 11.14.0 + resolution: "@emotion/cache@npm:11.14.0" + dependencies: + "@emotion/memoize": "npm:^0.9.0" + "@emotion/sheet": "npm:^1.4.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" + stylis: "npm:4.2.0" + checksum: 10c0/3fa3e7a431ab6f8a47c67132a00ac8358f428c1b6c8421d4b20de9df7c18e95eec04a5a6ff5a68908f98d3280044f247b4965ac63df8302d2c94dba718769724 + languageName: node + linkType: hard + "@emotion/hash@npm:^0.9.2": version: 0.9.2 resolution: "@emotion/hash@npm:0.9.2" @@ -3513,16 +3538,16 @@ __metadata: languageName: node linkType: hard -"@emotion/serialize@npm:^1.3.2": - version: 1.3.2 - resolution: "@emotion/serialize@npm:1.3.2" +"@emotion/serialize@npm:^1.3.3": + version: 1.3.3 + resolution: "@emotion/serialize@npm:1.3.3" dependencies: "@emotion/hash": "npm:^0.9.2" "@emotion/memoize": "npm:^0.9.0" "@emotion/unitless": "npm:^0.10.0" - "@emotion/utils": "npm:^1.4.1" + "@emotion/utils": "npm:^1.4.2" csstype: "npm:^3.0.2" - checksum: 10c0/b4873b643721d28b4450f9d77b71e6c8d0109e6825c54fc79e649d2fa438fe4080d2fa696ec8fda421b8e713fcd42306d6197b6121ddd2486ffab8e4b6311ce0 + checksum: 10c0/b28cb7de59de382021de2b26c0c94ebbfb16967a1b969a56fdb6408465a8993df243bfbd66430badaa6800e1834724e84895f5a6a9d97d0d224de3d77852acb4 languageName: node linkType: hard @@ -3590,10 +3615,10 @@ __metadata: languageName: node linkType: hard -"@emotion/utils@npm:^1.4.1": - version: 1.4.1 - resolution: "@emotion/utils@npm:1.4.1" - checksum: 10c0/f4704e0bdf48062fd6eb9c64771c88f521aab1e108a48cb23d65b6438597c63a6945301cef4c43611e79e0e76a304ec5481c31025ea8f573d7ad5423d747602c +"@emotion/utils@npm:^1.4.2": + version: 1.4.2 + resolution: "@emotion/utils@npm:1.4.2" + checksum: 10c0/7d0010bf60a2a8c1a033b6431469de4c80e47aeb8fd856a17c1d1f76bbc3a03161a34aeaa78803566e29681ca551e7bf9994b68e9c5f5c796159923e44f78d9a languageName: node linkType: hard @@ -6317,49 +6342,49 @@ __metadata: languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/core-downloads-tracker@npm:6.1.4" - checksum: 10c0/bfd84a726c883dd681b2cda13b977eb6646c1da66ea59905dbe7b857ec4ac86576a318eb116d54633e7c479aecb52fe0c75fb957f1b6081a6c8857dd5ce4c5b9 +"@mui/core-downloads-tracker@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/core-downloads-tracker@npm:6.4.1" + checksum: 10c0/5c35592eecb6b9b64b23003975a8ce20fadb9e9b712a78dd552b5ea835d06e8e4d7b7cb84f471f5658b5285de317a860b40597ad3c5e494e84f0ae0daf35fd6b languageName: node linkType: hard -"@mui/icons-material@npm:^6.1.4": - version: 6.1.6 - resolution: "@mui/icons-material@npm:6.1.6" +"@mui/icons-material@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/icons-material@npm:6.4.1" dependencies: "@babel/runtime": "npm:^7.26.0" peerDependencies: - "@mui/material": ^6.1.6 + "@mui/material": ^6.4.1 "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/a6eb10be3cc356fd404febf29a3b26fa63b6b09d3148736fb05279954905186e9804869ff18220840ae92dbdeddfd407c2d0c72b9e165e01fd6bbc620b6b39d7 + checksum: 10c0/f551aea11f0c3a6ff0336333e1cf28b69d7965e5984643067e5f8563eabf1434dee910c9b4beb530f785f191b0ec14520d9c41fd8ee6649f4f8d26f831f02ba8 languageName: node linkType: hard -"@mui/material@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/material@npm:6.1.4" +"@mui/material@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/material@npm:6.4.1" dependencies: - "@babel/runtime": "npm:^7.25.7" - "@mui/core-downloads-tracker": "npm:^6.1.4" - "@mui/system": "npm:^6.1.4" - "@mui/types": "npm:^7.2.18" - "@mui/utils": "npm:^6.1.4" + "@babel/runtime": "npm:^7.26.0" + "@mui/core-downloads-tracker": "npm:^6.4.1" + "@mui/system": "npm:^6.4.1" + "@mui/types": "npm:^7.2.21" + "@mui/utils": "npm:^6.4.1" "@popperjs/core": "npm:^2.11.8" - "@types/react-transition-group": "npm:^4.4.11" + "@types/react-transition-group": "npm:^4.4.12" clsx: "npm:^2.1.1" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" - react-is: "npm:^18.3.1" + react-is: "npm:^19.0.0" react-transition-group: "npm:^4.4.5" peerDependencies: "@emotion/react": ^11.5.0 "@emotion/styled": ^11.3.0 - "@mui/material-pigment-css": ^6.1.4 + "@mui/material-pigment-css": ^6.4.1 "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -6372,16 +6397,16 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/3729e56129b27706ebfab67428035dc7f0136dfd745281b467c7da07e78248cc8e2f36939cd6c33967fd565c5b5eff336629e3f7a2d877e9b176d24df67c9c81 + checksum: 10c0/c5ad8ab4339e145cac815cd9fdd9c909d5e2e5edf4997b8afe5edc320f3a020ddb857b5939f6a4453a34d996e3f334804c3858c5dc5f0bfede087e7d6b5e574e languageName: node linkType: hard -"@mui/private-theming@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/private-theming@npm:6.1.4" +"@mui/private-theming@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/private-theming@npm:6.4.1" dependencies: - "@babel/runtime": "npm:^7.25.7" - "@mui/utils": "npm:^6.1.4" + "@babel/runtime": "npm:^7.26.0" + "@mui/utils": "npm:^6.4.1" prop-types: "npm:^15.8.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -6389,17 +6414,17 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/37c81ca4529afbec2a4710d4dfa1597a5f9a33ff3ae775fa68474f86b223ed4ab953526e49883d77638ad62f0c944716c2b0dc58a105624ca91fe60a633e3b4a + checksum: 10c0/5b6936e6dff6218373579f901aabba01eea1953e97a5b4b695736aeebca2d39fc7c2b4d0e54ab064d1ba99a6ffce0d907098791ecec28795f7da25c1bd0d33cb languageName: node linkType: hard -"@mui/styled-engine@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/styled-engine@npm:6.1.4" +"@mui/styled-engine@npm:^6.4.0": + version: 6.4.0 + resolution: "@mui/styled-engine@npm:6.4.0" dependencies: - "@babel/runtime": "npm:^7.25.7" - "@emotion/cache": "npm:^11.13.1" - "@emotion/serialize": "npm:^1.3.2" + "@babel/runtime": "npm:^7.26.0" + "@emotion/cache": "npm:^11.13.5" + "@emotion/serialize": "npm:^1.3.3" "@emotion/sheet": "npm:^1.4.0" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -6412,19 +6437,19 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 10c0/ce8f53a704aa607152ef5ef25d95bdd5c4399f7dbf240acd6e4057c24d84536bb3c884fcb13c5e88d87dbfff42935c82af2e63800ba033ad481847b1967ccf52 + checksum: 10c0/bff35147ca9ef586679b53786d840e98837f0c1e5bf10a3510d4b2b68c340ae4ab69befe94b69ef25f6846bada5b3c355d25fa3a179d1598499e28c51f28d5d2 languageName: node linkType: hard -"@mui/system@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/system@npm:6.1.4" +"@mui/system@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/system@npm:6.4.1" dependencies: - "@babel/runtime": "npm:^7.25.7" - "@mui/private-theming": "npm:^6.1.4" - "@mui/styled-engine": "npm:^6.1.4" - "@mui/types": "npm:^7.2.18" - "@mui/utils": "npm:^6.1.4" + "@babel/runtime": "npm:^7.26.0" + "@mui/private-theming": "npm:^6.4.1" + "@mui/styled-engine": "npm:^6.4.0" + "@mui/types": "npm:^7.2.21" + "@mui/utils": "npm:^6.4.1" clsx: "npm:^2.1.1" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -6440,39 +6465,39 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/766f1c695254df54b06de1af231cdf30752f71a79181edd4c24044405144b7a72c4c23248155f4041999993aa4f568878eccd130c100b4b332e7dd6b322dfd75 + checksum: 10c0/34d731fb5faf1a6242c9faca5672a4b8722ac97cf5d1fd58cf85cce093cfbd2249ed57dd4c33e8f3b879c611e0fab56bb9e0e8fc1b7c8a49cbe4dd39ba32f98d languageName: node linkType: hard -"@mui/types@npm:^7.2.18": - version: 7.2.18 - resolution: "@mui/types@npm:7.2.18" +"@mui/types@npm:^7.2.21": + version: 7.2.21 + resolution: "@mui/types@npm:7.2.21" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/338404bdef7c7f9ebcd389ebbf429c44d2cc9c25c65d8669dc900a24b2c8718240482273bf6cd953578965e3838ad40a8e7376c71d3d9146be3afb88bff1b67a + checksum: 10c0/c0038ae402a3cfb2805a19167362fb5ac2ca1403f0ef3dad688d1e2276afe757b69d5fb1e3af4cd0e985b9221d287fd863c5b00f29fd07a276c7de9e3423a0f3 languageName: node linkType: hard -"@mui/utils@npm:^6.1.4": - version: 6.1.4 - resolution: "@mui/utils@npm:6.1.4" +"@mui/utils@npm:^6.4.1": + version: 6.4.1 + resolution: "@mui/utils@npm:6.4.1" dependencies: - "@babel/runtime": "npm:^7.25.7" - "@mui/types": "npm:^7.2.18" - "@types/prop-types": "npm:^15.7.13" + "@babel/runtime": "npm:^7.26.0" + "@mui/types": "npm:^7.2.21" + "@types/prop-types": "npm:^15.7.14" clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" - react-is: "npm:^18.3.1" + react-is: "npm:^19.0.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/cac4f05904897c28444e7ef2a6891678d36c0a8fae921ccacebcd7b53da99e4c6cab05669328fdabff624313de68f1b5bcd427cba36024b6c3505b5ee945eb05 + checksum: 10c0/c0cfb8737db7c5709ce35ebdf739c5014914a1344e1b305d13a75b061b970656777eaf01f6fbdb8f209da91c086ab36dd3f5ddc1c94237c5868a383cdd1aad81 languageName: node linkType: hard @@ -10668,10 +10693,10 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:^15.7.13": - version: 15.7.13 - resolution: "@types/prop-types@npm:15.7.13" - checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61 +"@types/prop-types@npm:^15.7.14": + version: 15.7.14 + resolution: "@types/prop-types@npm:15.7.14" + checksum: 10c0/1ec775160bfab90b67a782d735952158c7e702ca4502968aa82565bd8e452c2de8601c8dfe349733073c31179116cf7340710160d3836aa8a1ef76d1532893b1 languageName: node linkType: hard @@ -10698,12 +10723,12 @@ __metadata: languageName: node linkType: hard -"@types/react-transition-group@npm:^4.4.11": - version: 4.4.11 - resolution: "@types/react-transition-group@npm:4.4.11" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/8fbf0dcc1b81985cdcebe3c59d769fe2ea3f4525f12c3a10a7429a59f93e303c82b2abb744d21cb762879f4514969d70a7ab11b9bf486f92213e8fe70e04098d +"@types/react-transition-group@npm:^4.4.12": + version: 4.4.12 + resolution: "@types/react-transition-group@npm:4.4.12" + peerDependencies: + "@types/react": "*" + checksum: 10c0/0441b8b47c69312c89ec0760ba477ba1a0808a10ceef8dc1c64b1013ed78517332c30f18681b0ec0b53542731f1ed015169fed1d127cc91222638ed955478ec7 languageName: node linkType: hard @@ -15105,9 +15130,9 @@ __metadata: "@emotion/styled": "npm:^11.13.0" "@lingui/detect-locale": "npm:^4.6.0" "@lingui/react": "npm:^4.6.0" - "@mui/icons-material": "npm:^6.1.4" - "@mui/material": "npm:^6.1.4" - "@mui/utils": "npm:^6.1.4" + "@mui/icons-material": "npm:^6.4.1" + "@mui/material": "npm:^6.4.1" + "@mui/utils": "npm:^6.4.1" "@safe-global/safe-apps-provider": "npm:^0.18.3" "@safe-global/safe-apps-sdk": "npm:^9.1.0" "@storybook/addon-a11y": "npm:^8.3.5" @@ -23819,10 +23844,10 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.3.1": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 +"react-is@npm:^19.0.0": + version: 19.0.0 + resolution: "react-is@npm:19.0.0" + checksum: 10c0/d1be8e8500cf04f76df71942a21ef3a71266397a383d7ec8885f35190df818d35c65efd35aed7be47a89ad99aaff2c52e0c4e39e8930844a6b997622e50625a8 languageName: node linkType: hard