diff --git a/projects/dex-ui/src/utils/query/queryKeys.ts b/projects/dex-ui/src/utils/query/queryKeys.ts index e03e60cd3..87708114c 100644 --- a/projects/dex-ui/src/utils/query/queryKeys.ts +++ b/projects/dex-ui/src/utils/query/queryKeys.ts @@ -19,6 +19,7 @@ export const queryKeys = { wellImplementations: (addresses: string[]) => ["wells", "implementations", addresses], // well Function + wellFunctions: (addresses: string[]) => ["wellFunctions", addresses], wellFunctionValid: (address: string, data: string) => ["wellFunction", "isValid", address, data], wellFunctionNames: (addresses: string[] | undefined) => ["wellFunctions", "names", addresses], diff --git a/projects/dex-ui/src/wells/pump/utils.ts b/projects/dex-ui/src/wells/pump/utils.ts index 9637b96cf..b19d9e971 100644 --- a/projects/dex-ui/src/wells/pump/utils.ts +++ b/projects/dex-ui/src/wells/pump/utils.ts @@ -1,29 +1,45 @@ +import { useCallback, useMemo } from "react"; + import { Well } from "@beanstalk/sdk-wells"; -import { MULTI_FLOW_PUMP_ADDRESS, MULTI_FLOW_PUMP_V_1PT1_ADDRESS } from "src/utils/addresses"; +import useSdk from "src/utils/sdk/useSdk"; -export const getIsMultiPumpWell = (well: Well | undefined) => { - let isMultiFlowPumpV1 = false; - let isMultiFlowPumpV1_1 = false; +export const useIsMultiFlowPump = (well: Well | undefined = undefined) => { + const sdk = useSdk(); - for (const pump of well?.pumps || []) { - if (!isMultiFlowPumpV1 && pump.address.toLowerCase() === MULTI_FLOW_PUMP_ADDRESS) { - isMultiFlowPumpV1 = true; - } + const getIsMultiFlow = useCallback( + (_well: Well | undefined) => { + let isMultiFlowPumpV1 = false; + let isMultiFlowPumpV1_1 = false; - if (!isMultiFlowPumpV1_1 && pump.address.toLowerCase() === MULTI_FLOW_PUMP_V_1PT1_ADDRESS) { - isMultiFlowPumpV1_1 = true; - } - } + const mfPumpV1Address = sdk.wells.addresses.MULTI_FLOW_PUMP_V1.get(sdk.chainId); + const mfPumpV1_1Address = sdk.wells.addresses.MULTI_FLOW_PUMP_V1_1.get(sdk.chainId); - return { - isV1: isMultiFlowPumpV1, - isV1_1: isMultiFlowPumpV1_1, - isMultiFlow: isMultiFlowPumpV1 || isMultiFlowPumpV1_1 - }; -}; + for (const pump of _well?.pumps || []) { + const pumpAddress = pump.address.toLowerCase(); + + if (pumpAddress === mfPumpV1Address) { + isMultiFlowPumpV1 = true; + } + + if (pumpAddress === mfPumpV1_1Address) { + isMultiFlowPumpV1_1 = true; + } + } + + return { + isV1: isMultiFlowPumpV1, + isV1_1: isMultiFlowPumpV1_1, + isMultiFlow: isMultiFlowPumpV1 || isMultiFlowPumpV1_1 + }; + }, + [sdk] + ); -export const getIsMultiFlowPumpV1pt1 = (well: Well | undefined) => { - if (!well?.pumps) return false; - return !!well.pumps.find((pump) => pump.address.toLowerCase() === MULTI_FLOW_PUMP_V_1PT1_ADDRESS); + return useMemo(() => { + return { + ...getIsMultiFlow(well), + getIsMultiFlow + }; + }, [well, getIsMultiFlow]); }; diff --git a/projects/dex-ui/src/wells/useMultiFlowPumpTWAReserves.tsx b/projects/dex-ui/src/wells/useMultiFlowPumpTWAReserves.tsx index e9dbfc8a2..e882b27a8 100644 --- a/projects/dex-ui/src/wells/useMultiFlowPumpTWAReserves.tsx +++ b/projects/dex-ui/src/wells/useMultiFlowPumpTWAReserves.tsx @@ -10,7 +10,7 @@ import { useChainScopedQuery } from "src/utils/query/useChainScopedQuery"; import useSdk from "src/utils/sdk/useSdk"; import { config } from "src/utils/wagmi/config"; -import { getIsMultiPumpWell } from "./pump/utils"; +import { useIsMultiFlowPump } from "./pump/utils"; import { useBeanstalkSiloWhitelist } from "./useBeanstalkSiloWhitelist"; import { useWells } from "./useWells"; @@ -18,13 +18,14 @@ export const useMultiFlowPumpTWAReserves = () => { const { data: wells } = useWells(); const { getIsWhitelisted } = useBeanstalkSiloWhitelist(); const sdk = useSdk(); + const { getIsMultiFlow } = useIsMultiFlowPump(); const query = useChainScopedQuery({ queryKey: ["wells", "multiFlowPumpTWAReserves"], queryFn: async () => { const whitelistedWells = (wells || []).filter( - (well) => getIsMultiPumpWell(well).isMultiFlow && getIsWhitelisted(well) + (well) => getIsMultiFlow(well).isMultiFlow && getIsWhitelisted(well) ); const [{ timestamp: seasonTimestamp }, ...wellOracleSnapshots] = await Promise.all([ diff --git a/projects/dex-ui/src/wells/wellFunction/useWellFunctions.ts b/projects/dex-ui/src/wells/wellFunction/useWellFunctions.ts index 8a1bcee60..baeb1b69c 100644 --- a/projects/dex-ui/src/wells/wellFunction/useWellFunctions.ts +++ b/projects/dex-ui/src/wells/wellFunction/useWellFunctions.ts @@ -1,26 +1,106 @@ -import { useMemo } from "react"; +import { useCallback, useMemo } from "react"; -import { WellFunction } from "@beanstalk/sdk-wells"; +import { multicall } from "@wagmi/core"; +import { ContractFunctionParameters } from "viem"; +import { Well, WellFunction } from "@beanstalk/sdk-wells"; + +import { chunkArray } from "src/utils/array"; +import { queryKeys } from "src/utils/query/queryKeys"; +import { useChainScopedQuery } from "src/utils/query/useChainScopedQuery"; +import { config } from "src/utils/wagmi/config"; import { useWells } from "src/wells/useWells"; +type WellFunctionDataMap = Record< + string, + { + name: string; + symbol: string; + } +>; + export const useWellFunctions = () => { const { data: wells } = useWells(); - return useMemo(() => { - if (!wells || !wells.length) return []; + const addresses = getWFAddresses(wells); - const wellFunctionMap: Record = {}; + const selectData = useCallback( + (data: WellFunctionDataMap) => { + if (!data) return []; - for (const well of wells) { - if (!well.wellFunction) continue; - const address = well.wellFunction.address.toLowerCase(); + // apply the changes to all wells + for (const well of wells) { + const wf = well.wellFunction; + if (!wf || !!(wf.name && wf.symbol)) continue; - if (!(address in wellFunctionMap)) { - wellFunctionMap[address] = well.wellFunction; + const { name, symbol } = data[wf.address.toLowerCase()]; + if (name && symbol) { + wf.name = name; + wf.symbol = symbol; + } } + + return Object.values(mapWellFunctions(wells)); + }, + [wells] + ); + + const query = useChainScopedQuery({ + queryKey: queryKeys.wellFunctions(addresses), + queryFn: async () => { + const { contracts, chunkSize } = buildMulticall(addresses); + + const results = await multicall(config, { contracts, allowFailure: false }).then((r) => + chunkArray(r, chunkSize) + ); + + return addresses.reduce>( + (prev, wfAddress, i) => { + const [name, symbol] = results[i]; + prev[wfAddress] = { name, symbol }; + return prev; + }, + {} + ); + }, + select: selectData, + enabled: !!wells.length + }); + + return useMemo(() => query.data || [], [query.data]); +}; + +const mapWellFunctions = (wells: Well[]) => { + return wells.reduce>((prev, well) => { + if (!well.wellFunction) return prev; + const address = well.wellFunction.address.toLowerCase(); + + if (!prev[address]) { + prev[address] = well.wellFunction; + } + return prev; + }, {}); +}; + +const getWFAddresses = (wells: Well[]) => { + const set = new Set(wells.map((well) => well.wellFunction?.address?.toLowerCase() || "")); + set.delete(""); + return Array.from(set); +}; + +const buildMulticall = (addresses: string[]) => { + const calls: ContractFunctionParameters[][] = addresses.map( + (address) => { + const contract = { + address: address as `0x${string}`, + abi: WellFunction.abi + }; + return [ + { ...contract, functionName: "name", args: [] }, + { ...contract, functionName: "symbol", args: [] } + ]; } + ); - return Object.values(wellFunctionMap); - }, [wells]); + return { contracts: calls.flat(), chunkSize: 2 }; }; diff --git a/projects/dex-ui/src/wells/wellFunction/utils.ts b/projects/dex-ui/src/wells/wellFunction/utils.ts index d0ec804a9..a32a26fc9 100644 --- a/projects/dex-ui/src/wells/wellFunction/utils.ts +++ b/projects/dex-ui/src/wells/wellFunction/utils.ts @@ -1,15 +1,28 @@ +import { useMemo } from "react"; + import { Well, WellFunction } from "@beanstalk/sdk-wells"; -import { CONSTANT_PRODUCT_2_ADDRESS, CONSTANT_PRODUCT_2_V2_ADDRESS } from "src/utils/addresses"; -const cp2Addresses = [CONSTANT_PRODUCT_2_V2_ADDRESS, CONSTANT_PRODUCT_2_ADDRESS]; +import useSdk from "src/utils/sdk/useSdk"; + +export const useIsConstantProduct2 = (param: Well | WellFunction | undefined | null) => { + const sdk = useSdk(); + + return useMemo(() => { + const addresses = sdk.wells.addresses; + const cp2V1 = addresses.CONSTANT_PRODUCT_2_V1; + const cp2V2 = addresses.CONSTANT_PRODUCT_2_V2; + const cp2 = [cp2V1, cp2V2]; -export const isConstantProduct2 = (param: Well | WellFunction | undefined | null) => { - if (!param) return false; + if (!param) return false; - if (param instanceof Well) { - const wf = param.wellFunction?.address; - return Boolean(wf && cp2Addresses.includes(wf.toLowerCase())); - } + const wf = param instanceof Well ? param.wellFunction : param; - return cp2Addresses.includes(param.address.toLowerCase()); + return ( + wf && + cp2.some((_address) => { + const address = _address.get(sdk.chainId) || ""; + return address.toLowerCase() === wf.address.toLowerCase(); + }) + ); + }, [param, sdk.chainId, sdk.wells.addresses]); };