diff --git a/src/components/Page.tsx b/src/components/Page.tsx index bce3d23ef..74f762fc9 100644 --- a/src/components/Page.tsx +++ b/src/components/Page.tsx @@ -17,7 +17,7 @@ export const Page = ({
{title && (
-

{title}

+

{title}

{trailingTitle}
)} diff --git a/src/elements/fiat/FiatBox.tsx b/src/elements/fiat/FiatBox.tsx index 0eacef584..b6e83739b 100644 --- a/src/elements/fiat/FiatBox.tsx +++ b/src/elements/fiat/FiatBox.tsx @@ -51,7 +51,7 @@ export const FiatBox = ({ fiat }: { fiat: Fiat }) => { const fiatOut = fiat.operations.includes(Operations.fiatOut); return ( -
+
Available Operations @@ -66,7 +66,7 @@ export const FiatBox = ({ fiat }: { fiat: Fiat }) => {
)}
- {fiat.text} +

{fiat.text}

{fiatIn && ( + ) : ( + 'Ethereum Network' + )}
+ +
+
+ ); }; diff --git a/src/services/observables/apiData.ts b/src/services/observables/apiData.ts index 352d3209c..8f6804485 100644 --- a/src/services/observables/apiData.ts +++ b/src/services/observables/apiData.ts @@ -17,7 +17,7 @@ export const apiData$ = combineLatest([oneMinute$]).pipe( shareReplay(1) ); -export const apiTokens$ = apiData$.pipe( +export const apiTokensV2$ = apiData$.pipe( pluck('tokens'), distinctUntilChanged(isEqual), shareReplay(1) diff --git a/src/services/observables/pools.ts b/src/services/observables/pools.ts index e55542dd0..9d45e895b 100644 --- a/src/services/observables/pools.ts +++ b/src/services/observables/pools.ts @@ -1,4 +1,9 @@ -import { allTokensV2$, Token, tokensV3$ } from 'services/observables/tokens'; +import { + allTokensV2$, + allTokensV3$, + Token, + tokensV3$, +} from 'services/observables/tokens'; import BigNumber from 'bignumber.js'; import { combineLatest } from 'rxjs'; import { switchMapIgnoreThrow } from 'services/observables/customOperators'; @@ -263,7 +268,6 @@ const buildPoolV3Object = ( .minus(1) .times(100) .toString(); - return { ...apiPool, liquidity, @@ -396,3 +400,49 @@ export const poolsV3$ = combineLatest([ distinctUntilChanged(isEqual), shareReplay(1) ); + +export const allpoolsV3$ = combineLatest([ + apiPoolsV3$, + allTokensV3$, + standardRewardPrograms$, +]).pipe( + switchMapIgnoreThrow( + async ([apiPoolsV3, allTokens, standardRewardPrograms]) => { + const tokensMap = new Map(allTokens.map((t) => [t.address, t])); + const poolIds = apiPoolsV3.map((pool) => pool.poolDltId); + + const [ + latestProgramIds, + masterVaultBalances, + extVaultBalances, + extVaultIsPaused, + ] = await Promise.all([ + fetchLatestProgramIdsMulticall(apiPoolsV3), + fetchTokenBalanceMulticall(poolIds, bancorMasterVault), + fetchTokenBalanceMulticall( + poolIds, + ContractsApi.ExternalProtectionVault.contractAddress + ), + ContractsApi.ExternalProtectionVault.read.isPaused(), + ]); + + const pools = await Promise.all( + apiPoolsV3.map( + async (pool) => + await buildPoolV3Object( + tokensMap, + pool, + tokensMap.get(pool.poolDltId), + latestProgramIds, + standardRewardPrograms, + masterVaultBalances, + !extVaultIsPaused ? extVaultBalances : undefined + ) + ) + ); + return pools.filter((pool) => !!pool) as PoolV3[]; + } + ), + distinctUntilChanged(isEqual), + shareReplay(1) +); diff --git a/src/services/observables/portfolio.ts b/src/services/observables/portfolio.ts index f111647ae..ee1758d24 100644 --- a/src/services/observables/portfolio.ts +++ b/src/services/observables/portfolio.ts @@ -11,7 +11,7 @@ import { WithdrawalSettings } from 'store/portfolio/v3Portfolio.types'; import { fetchStandardRewardsByUser } from 'services/web3/v3/portfolio/standardStaking'; import { oneMinute$ } from 'services/observables/timers'; import { apiPoolsV3$ } from 'services/observables/apiData'; -import { poolsV3$ } from 'services/observables/pools'; +import { allpoolsV3$ } from 'services/observables/pools'; export const portfolioHoldings$ = combineLatest([ apiPoolsV3$, @@ -28,7 +28,7 @@ export const portfolioHoldings$ = combineLatest([ export const portfolioStandardRewards$ = combineLatest([ user$, - poolsV3$, + allpoolsV3$, oneMinute$, ]).pipe( switchMapIgnoreThrow(async ([user, poolsV3]) => { diff --git a/src/services/observables/tokens.ts b/src/services/observables/tokens.ts index b6609e372..677404069 100644 --- a/src/services/observables/tokens.ts +++ b/src/services/observables/tokens.ts @@ -10,7 +10,7 @@ import { tokenListTokens$ } from 'services/observables/tokenLists'; import { apiPools$, apiPoolsV3$, - apiTokens$, + apiTokensV2$, apiTokensV3$, } from 'services/observables/apiData'; import { utils } from 'ethers'; @@ -210,7 +210,7 @@ export const updateUserBalances = async () => { export const allTokenBalances$ = combineLatest([ user$, - apiTokens$, + apiTokensV2$, apiTokensV3$, userBalancesReceiver$, oneMinute$, @@ -251,7 +251,7 @@ const minNetworkTokenLiquidityForMinting$ = combineLatest([ ); export const tokensV2$ = combineLatest([ - apiTokens$, + apiTokensV2$, apiPools$, tokenListTokens$, allTokenBalances$, @@ -292,7 +292,7 @@ export const tokensV2$ = combineLatest([ ); export const tokensV3$ = combineLatest([ - apiTokens$, + apiTokensV2$, apiTokensV3$, apiPoolsV3$, tokenListTokens$, @@ -355,7 +355,7 @@ export const tokensForTradeWithExternal$ = combineLatest([ ); export const allTokensV2$ = combineLatest([ - apiTokens$, + apiTokensV2$, apiPools$, tokenListTokens$, allTokenBalances$, @@ -388,6 +388,52 @@ export const allTokensV2$ = combineLatest([ shareReplay(1) ); +export const allTokensV3$ = combineLatest([ + apiTokensV2$, + apiTokensV3$, + apiPoolsV3$, + tokenListTokens$, + allTokenBalances$, +]).pipe( + switchMapIgnoreThrow( + async ([ + apiTokensV2, + apiTokensV3, + apiPoolsV3, + tokenListTokens, + balances, + ]) => { + const userPreferredTokenListTokensMap = new Map( + tokenListTokens.userPreferredTokenListTokens.map((t) => [t.address, t]) + ); + const apiPoolsMap = new Map(apiPoolsV3.map((p) => [p.poolDltId, p])); + const apiTokensV2Map = new Map(apiTokensV2.map((t) => [t.dlt_id, t])); + + return apiTokensV3 + .map((apiToken) => { + const tokenListToken = userPreferredTokenListTokensMap.get( + apiToken.dltId + ); + + const apiPool = apiPoolsMap.get(apiToken.dltId); + if (!apiPool) { + return undefined; + } + const v2Token = apiTokensV2Map.get(apiToken.dltId); + return buildTokenObjectV3( + apiToken, + apiPool, + balances, + tokenListToken, + v2Token + ); + }) + .filter((token) => !!token) as Token[]; + } + ), + distinctUntilChanged(isEqual), + shareReplay(1) +); export const keeperDaoTokens$ = from(fetchKeeperDaoTokens()).pipe( shareReplay(1) ); diff --git a/src/services/observables/triggers.ts b/src/services/observables/triggers.ts index b9c2a6430..b05a576ed 100644 --- a/src/services/observables/triggers.ts +++ b/src/services/observables/triggers.ts @@ -19,7 +19,7 @@ import { import { getTokenListLS, setTokenListLS } from 'utils/localStorage'; import { loadingLockedBnt$, loadingPositions$, loadingRewards$ } from './user'; import { statisticsV3$ } from 'services/observables/statistics'; -import { setv2Pools, setv3Pools } from 'store/bancor/pool'; +import { setAllV3Pools, setv2Pools, setv3Pools } from 'store/bancor/pool'; import { setLoadingLockedBnt, setLoadingPositions, @@ -56,7 +56,7 @@ import { tokenListTokens$, userPreferredListIds$, } from 'services/observables/tokenLists'; -import { poolsV2$, poolsV3$ } from 'services/observables/pools'; +import { allpoolsV3$, poolsV2$, poolsV3$ } from 'services/observables/pools'; import { poolTokens$ } from 'services/observables/poolTokensV1'; import { setStakedAmount, setUnstakeTimer } from 'store/gov/gov'; import { stakedAmount$, unstakeTimer$ } from './gov'; @@ -67,6 +67,10 @@ export const subscribeToObservables = (dispatch: any) => { dispatch(setv3Pools(pools)); }); + allpoolsV3$.subscribe((pools) => { + dispatch(setAllV3Pools(pools)); + }); + allTokensV2$.subscribe((tokens) => { dispatch(setAllTokensV2(tokens)); }); diff --git a/src/services/web3/v3/portfolio/helpers.ts b/src/services/web3/v3/portfolio/helpers.ts index 0d43d6121..66f98f9ff 100644 --- a/src/services/web3/v3/portfolio/helpers.ts +++ b/src/services/web3/v3/portfolio/helpers.ts @@ -11,11 +11,11 @@ import { fetchStandardRewardsByUser } from 'services/web3/v3/portfolio/standardS import { take } from 'rxjs/operators'; import { apiPoolsV3$ } from 'services/observables/apiData'; import { user$ } from 'services/observables/user'; -import { poolsV3$ } from 'services/observables/pools'; +import { allpoolsV3$ } from 'services/observables/pools'; export const updatePortfolioData = async (dispatch: (data: any) => void) => { const account = await user$.pipe(take(1)).toPromise(); - const poolsV3 = await poolsV3$.pipe(take(1)).toPromise(); + const poolsV3 = await allpoolsV3$.pipe(take(1)).toPromise(); if (!account) { return; diff --git a/src/store/bancor/pool.ts b/src/store/bancor/pool.ts index 15c30e7a4..c842a37b3 100644 --- a/src/store/bancor/pool.ts +++ b/src/store/bancor/pool.ts @@ -9,12 +9,14 @@ import { bntToken } from 'services/web3/config'; interface PoolState { v2Pools: Pool[]; v3Pools: PoolV3[]; + allV3Pools: PoolV3[]; isLoadingV3Pools: boolean; } const initialState: PoolState = { v2Pools: [], v3Pools: [], + allV3Pools: [], isLoadingV3Pools: true, }; @@ -29,6 +31,9 @@ const poolSlice = createSlice({ state.v3Pools = action.payload; state.isLoadingV3Pools = false; }, + setAllV3Pools: (state, action) => { + state.allV3Pools = action.payload; + }, }, }); @@ -61,6 +66,13 @@ export const getPoolsV3Map = createSelector( } ); +export const getAllPoolsV3Map = createSelector( + [(state: RootState) => state.pool.allV3Pools], + (pools: PoolV3[]): Map => { + return new Map(pools.map((pool) => [pool.poolDltId, pool])); + } +); + export const getV2PoolsWithoutV3 = createSelector( (state: RootState) => state.pool.v2Pools, (state: RootState) => state.pool.v3Pools, @@ -124,7 +136,7 @@ export const getTopPoolsV3 = createSelector( ); export const getIsV3Exist = createSelector( - [(state: RootState) => getPoolsV3Map(state), (_: any, id: string) => id], + [(state: RootState) => getAllPoolsV3Map(state), (_: any, id: string) => id], (pools: Map, id): boolean => { return !!pools.get(id); } @@ -172,6 +184,6 @@ export const getPoolByIdWithoutV3 = (id: string) => return { status: 'ready', pool } as SelectedPool; }); -export const { setv2Pools, setv3Pools } = poolSlice.actions; +export const { setv2Pools, setv3Pools, setAllV3Pools } = poolSlice.actions; export const pool = poolSlice.reducer; diff --git a/src/store/portfolio/v3Portfolio.ts b/src/store/portfolio/v3Portfolio.ts index e51fef289..aa823c0c5 100644 --- a/src/store/portfolio/v3Portfolio.ts +++ b/src/store/portfolio/v3Portfolio.ts @@ -12,7 +12,7 @@ import { utils } from 'ethers'; import { RewardsProgramStake } from 'services/web3/v3/portfolio/standardStaking'; import BigNumber from 'bignumber.js'; import { orderBy, uniqBy } from 'lodash'; -import { getPoolsV3Map } from 'store/bancor/pool'; +import { getAllPoolsV3Map, getPoolsV3Map } from 'store/bancor/pool'; import { PoolV3 } from 'services/observables/pools'; import { shrinkToken } from 'utils/formulas'; import { toBigNumber } from 'utils/helperFunctions'; @@ -84,7 +84,7 @@ export const getIsLoadingHoldings = createSelector( export const getPortfolioHoldings = createSelector( (state: RootState) => state.v3Portfolio.holdingsRaw, (state: RootState) => state.v3Portfolio.standardRewards, - (state: RootState) => getPoolsV3Map(state), + (state: RootState) => getAllPoolsV3Map(state), ( holdingsRaw: HoldingRaw[], standardRewards: RewardsProgramStake[], @@ -255,7 +255,7 @@ export const getIsLoadingStandardRewards = createSelector( export const getStandardRewards = createSelector( (state: RootState) => state.v3Portfolio.standardRewards, - (state: RootState) => getPoolsV3Map(state), + (state: RootState) => getAllPoolsV3Map(state), ( standardRewards: RewardsProgramStake[], allPoolsMap: Map diff --git a/src/styles/base/index.css b/src/styles/base/index.css index daca8468e..f7db4c6b4 100644 --- a/src/styles/base/index.css +++ b/src/styles/base/index.css @@ -3,8 +3,8 @@ @layer base { body, #root { - @apply bg-white md:bg-fog text-charcoal text-14 font-normal font-euclid; - @apply dark:bg-charcoal dark:md:bg-black dark:text-fog; + @apply bg-fog dark:bg-black text-charcoal text-14 font-normal font-euclid; + @apply dark:bg-black dark:text-fog; } h1 { diff --git a/src/styles/utilities/index.css b/src/styles/utilities/index.css index 9bdda7162..86d259ba1 100644 --- a/src/styles/utilities/index.css +++ b/src/styles/utilities/index.css @@ -10,7 +10,7 @@ } .page { - @apply pt-[100px] pb-[80px] px-10 md:px-20 max-w-[1140px] mx-auto bg-fog dark:bg-black; + @apply pt-[100px] pb-[80px] px-10 md:px-20 max-w-[1140px] mx-auto; } .page h1 { diff --git a/src/utils/helperFunctions.ts b/src/utils/helperFunctions.ts index 0c8ee2b2a..aeaf05375 100644 --- a/src/utils/helperFunctions.ts +++ b/src/utils/helperFunctions.ts @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js'; -import { BigNumber as BigNumberEther } from 'ethers'; +import { BigNumber as BigNumberEther, utils } from 'ethers'; import numeral from 'numeral'; import { EthNetworks } from 'services/web3/types'; import { shrinkToken } from 'utils/formulas'; @@ -104,6 +104,15 @@ export const isUnsupportedNetwork = ( return !!network && !EthNetworks[network]; }; +export const requestSwitchChain = async ( + network: EthNetworks = EthNetworks.Mainnet +) => { + await window.ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: utils.hexValue(network) }], + }); +}; + export const calculateBntNeededToOpenSpace = ( bntBalance: string, tknBalance: string,