Skip to content
Merged

Dev #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/haust-dex/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
REACT_APP_SENTRY_ENABLED=true
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_THE_GRAPH_SCHEMA_ENDPOINT="https://graph.testnet.haust.app/subgraphs/name/haust/uniswap-v3"
REACT_APP_THE_GRAPH_SCHEMA_ENDPOINT="https://graph.stage.haust.app/subgraphs/name/haust/uniswap-v3"
REACT_APP_API_URL="https://haust-v3-stats.rocknblock.io/api/v1/"
REACT_APP_WALLET_CONNECT_PROJECT_ID="4f9a1d1c515fa8f9dcd2c305e2e4e9ee"
2 changes: 1 addition & 1 deletion apps/haust-dex/codegen.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
overrideExisting: false
schema: 'https://graph.testnet.haust.app/subgraphs/name/haust/uniswap-v3'
schema: 'https://graph.stage.haust.app/subgraphs/name/haust/uniswap-v3'
generates:
./src/graphql/thegraph/schema/schema.graphql:
plugins:
Expand Down
193 changes: 140 additions & 53 deletions apps/haust-dex/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,95 +1,182 @@
import { Token } from '@uniswap/sdk-core'
import { AddressMap } from '@uniswap/smart-order-router'
import { useWeb3React } from '@web3-react/core'
import { MULTICALL_ADDRESS, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES as V3NFT_ADDRESSES } from 'constants/addresses'
import { isSupportedChain, SupportedChainId } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers'
import { BaseContract } from 'ethers/lib/ethers'
import { ContractInput, useUniswapPricesQuery } from 'graphql/data/__generated__/types-and-hooks'
import { toContractInput } from 'graphql/data/util'
import useStablecoinPrice from 'hooks/useStablecoinPrice'
import { useMemo } from 'react'
import { NonfungiblePositionManagerJson as NFTPositionManagerABI, UniswapInterfaceMulticallJson as MulticallABI } from 'sdks/v3-periphery'
import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'types/v3'
import { getContract } from 'utils'
import { CurrencyKey, currencyKey, currencyKeyFromGraphQL } from 'utils/currencyKey'

import { PositionInfo } from './cache'

type ContractMap<T extends BaseContract> = { [key: number]: T }
import { JsonRpcProvider } from "@ethersproject/providers";
import { Token } from "@uniswap/sdk-core";
import { AddressMap } from "@uniswap/smart-order-router";
import { useWeb3React } from "@web3-react/core";
import {
MULTICALL_ADDRESS,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES as V3NFT_ADDRESSES,
} from "constants/addresses";
import { isSupportedChain, SupportedChainId } from "constants/chains";
import { RPC_PROVIDERS } from "constants/providers";
import { BaseContract } from "ethers/lib/ethers";
import {
ContractInput,
useUniswapPricesQuery,
} from "graphql/data/__generated__/types-and-hooks";
import { toContractInput } from "graphql/data/util";
import useStablecoinPrice from "hooks/useStablecoinPrice";
import { useMemo } from "react";
import {
NonfungiblePositionManagerJson as NFTPositionManagerABI,
UniswapInterfaceMulticallJson as MulticallABI,
} from "sdks/v3-periphery";
import {
NonfungiblePositionManager,
UniswapInterfaceMulticall,
} from "types/v3";
import { getContract } from "utils";
import {
CurrencyKey,
currencyKey,
currencyKeyFromGraphQL,
} from "utils/currencyKey";

import { PositionInfo } from "./cache";

type ContractMap<T extends BaseContract> = { [key: number]: T };

const FALLBACK_RPC_URL = "https://rpc-testnet.haust.app";

// Constructs a chain-to-contract map, using the wallet's provider when available
function useContractMultichain<T extends BaseContract>(
addressMap: AddressMap,
ABI: any,
chainIds?: SupportedChainId[]
): ContractMap<T> {
const { chainId: walletChainId, provider: walletProvider } = useWeb3React()
const { chainId: walletChainId, provider: walletProvider } = useWeb3React();

return useMemo(() => {
const relevantChains =
chainIds ??
Object.keys(addressMap)
.map((chainId) => parseInt(chainId))
.filter(isSupportedChain)
.filter(isSupportedChain);

return relevantChains.reduce((acc: ContractMap<T>, chainId) => {
const provider = walletProvider && walletChainId === chainId ? walletProvider : RPC_PROVIDERS[chainId]
acc[chainId] = getContract(addressMap[chainId], ABI, provider) as T
return acc
}, {})
}, [ABI, addressMap, chainIds, walletChainId, walletProvider])
let provider =
walletProvider && walletChainId === chainId
? walletProvider
: RPC_PROVIDERS[chainId];

// Проверяем провайдер через простой запрос
if (provider) {
provider
.getBlockNumber()
.then(() => {
console.log(
`[useContractMultichain] Provider healthy for chain ${chainId}`
);
})
.catch((error) => {
console.error(
`[useContractMultichain] Provider failed for chain ${chainId}:`,
error
);
// При ошибке создаем и используем fallback провайдер
const fallbackProvider = new JsonRpcProvider(FALLBACK_RPC_URL);
provider = fallbackProvider;

// Обновляем контракт с новым провайдером
acc[chainId] = getContract(
addressMap[chainId],
ABI,
fallbackProvider
) as T;
});
}

// Изначально создаем контракт с текущим провайдером
acc[chainId] = getContract(addressMap[chainId], ABI, provider) as T;
return acc;
}, {});
}, [ABI, addressMap, chainIds, walletChainId, walletProvider]);
}

export function useV3ManagerContracts(chainIds: SupportedChainId[]): ContractMap<NonfungiblePositionManager> {
return useContractMultichain<NonfungiblePositionManager>(V3NFT_ADDRESSES, NFTPositionManagerABI, chainIds)
export function useV3ManagerContracts(
chainIds: SupportedChainId[]
): ContractMap<NonfungiblePositionManager> {
return useContractMultichain<NonfungiblePositionManager>(
V3NFT_ADDRESSES,
NFTPositionManagerABI,
chainIds
);
}

export function useInterfaceMulticallContracts(chainIds: SupportedChainId[]): ContractMap<UniswapInterfaceMulticall> {
return useContractMultichain<UniswapInterfaceMulticall>(MULTICALL_ADDRESS, MulticallABI, chainIds)
export function useInterfaceMulticallContracts(
chainIds: SupportedChainId[]
): ContractMap<UniswapInterfaceMulticall> {
return useContractMultichain<UniswapInterfaceMulticall>(
MULTICALL_ADDRESS,
MulticallABI,
chainIds
);
}

type PriceMap = { [key: CurrencyKey]: number | undefined }
type PriceMap = { [key: CurrencyKey]: number | undefined };
export function usePoolPriceMap(positions: PositionInfo[] | undefined) {
const contracts = useMemo(() => {
if (!positions || !positions.length) return []
if (!positions || !positions.length) return [];
// Avoids fetching duplicate tokens by placing in map
const contractMap = positions.reduce((acc: { [key: string]: ContractInput }, { pool: { token0, token1 } }) => {
acc[currencyKey(token0)] = toContractInput(token0)
acc[currencyKey(token1)] = toContractInput(token1)
return acc
}, {})
return Object.values(contractMap)
}, [positions])
const contractMap = positions.reduce(
(acc: { [key: string]: ContractInput }, { pool: { token0, token1 } }) => {
acc[currencyKey(token0)] = toContractInput(token0);
acc[currencyKey(token1)] = toContractInput(token1);
return acc;
},
{}
);
return Object.values(contractMap);
}, [positions]);

const { data, loading } = useUniswapPricesQuery({ variables: { contracts }, skip: !contracts.length })
const { data, loading } = useUniswapPricesQuery({
variables: { contracts },
skip: !contracts.length,
});

const priceMap = useMemo(
() =>
data?.tokens?.reduce((acc: PriceMap, current) => {
if (current) acc[currencyKeyFromGraphQL(current)] = current.project?.markets?.[0]?.price?.value
return acc
if (current)
acc[currencyKeyFromGraphQL(current)] =
current.project?.markets?.[0]?.price?.value;
return acc;
}, {}) ?? {},
[data?.tokens]
)
);

return { priceMap, pricesLoading: loading && !data }
return { priceMap, pricesLoading: loading && !data };
}

function useFeeValue(token: Token, fee: number | undefined, queriedPrice: number | undefined) {
const stablecoinPrice = useStablecoinPrice(!queriedPrice ? token : undefined)
function useFeeValue(
token: Token,
fee: number | undefined,
queriedPrice: number | undefined
) {
const stablecoinPrice = useStablecoinPrice(!queriedPrice ? token : undefined);
return useMemo(() => {
// Prefers gql price, as fetching stablecoinPrice will trigger multiple infura calls for each pool position
const price = queriedPrice ?? (stablecoinPrice ? parseFloat(stablecoinPrice.toSignificant()) : undefined)
const feeValue = fee && price ? fee * price : undefined
const price =
queriedPrice ??
(stablecoinPrice
? parseFloat(stablecoinPrice.toSignificant())
: undefined);
const feeValue = fee && price ? fee * price : undefined;

return [price, feeValue]
}, [fee, queriedPrice, stablecoinPrice])
return [price, feeValue];
}, [fee, queriedPrice, stablecoinPrice]);
}

export function useFeeValues(position: PositionInfo) {
const [priceA, feeValueA] = useFeeValue(position.pool.token0, position.fees?.[0], position.prices?.[0])
const [priceB, feeValueB] = useFeeValue(position.pool.token1, position.fees?.[1], position.prices?.[1])
const [priceA, feeValueA] = useFeeValue(
position.pool.token0,
position.fees?.[0],
position.prices?.[0]
);
const [priceB, feeValueB] = useFeeValue(
position.pool.token1,
position.fees?.[1],
position.prices?.[1]
);

return { priceA, priceB, fees: (feeValueA || 0) + (feeValueB || 0) }
return { priceA, priceB, fees: (feeValueA || 0) + (feeValueB || 0) };
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export default function Tokens({ totalBalance }: { totalBalance?: number }) {

const tokensList = useMemo(() => {
const allTokens = [nativeCurrency, ...Object.values(tokens)]

.filter(token => token?.symbol !== 'MYR')

return allTokens
.filter(Boolean)
.sort((a, b) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ const StyledHeaderRow = styled.div`
}
`

const ListNumberCell = styled(Cell)`
const ListNumberCell = styled(Cell)<{ header?: boolean }>`
color: ${({ theme }) => theme.textSecondary};
min-width: 32px;
font-size: 14px;
Expand Down
2 changes: 1 addition & 1 deletion apps/haust-dex/src/constants/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const GEMINI_LIST = 'https://www.gemini.com/uniswap/manifest.json'
const KLEROS_LIST = 't2crtokens.eth'
const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json'
const WRAPPED_LIST = 'wrapped.tokensoft.eth'
export const HAUST_TOKENLIST = 'https://raw.githubusercontent.com/Haust-Labs/haust-dex-tokenlist/refs/heads/master/tokenlist.json'
export const HAUST_TOKENLIST = 'https://raw.githubusercontent.com/Haust-Labs/haust-dex-tokenlist/test/tokenlist.json'

export const UNSUPPORTED_LIST_URLS: string[] = [BA_LIST, UNI_UNSUPPORTED_LIST]

Expand Down
4 changes: 2 additions & 2 deletions apps/haust-dex/src/graphql/thegraph/apollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import store from '../../state/index'

const CHAIN_SUBGRAPH_URL: Record<number, string> = {
// TODO: change subgraph path when mainnet is ready
[SupportedChainId.HAUST]: 'https://graph.testnet.haust.app/subgraphs/name/haust/uniswap-v3',
[SupportedChainId.HAUST]: 'https://graph.stage.haust.app/subgraphs/name/haust/uniswap-v3',
[SupportedChainId.HAUST_TESTNET]:
'https://graph.testnet.haust.app/subgraphs/name/haust/uniswap-v3',
'https://graph.stage.haust.app/subgraphs/name/haust/uniswap-v3',
}

const httpLink = new HttpLink({ uri: CHAIN_SUBGRAPH_URL[SupportedChainId.HAUST_TESTNET] })
Expand Down
Loading