{'No data, reeeeeeeeeeee'}
{'There doesn’t seem to be anything here. It might be because you searched for a token in the wrong category, or because there’s a rodent infestation in our server room. You check the search box, we’ll check the rodents. Deal?'}
diff --git a/apps/vaults/contexts/useActionFlow.tsx b/apps/vaults/contexts/useActionFlow.tsx
index cc56b988b..40e956ad1 100644
--- a/apps/vaults/contexts/useActionFlow.tsx
+++ b/apps/vaults/contexts/useActionFlow.tsx
@@ -9,7 +9,7 @@ import {setZapOption} from '@vaults/utils/zapOptions';
import {useSettings} from '@yearn-finance/web-lib/contexts/useSettings';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
import VAULT_ABI from '@yearn-finance/web-lib/utils/abi/vault.abi';
-import {isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {isZeroAddress, toAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, OPT_WETH_TOKEN_ADDRESS, WETH_TOKEN_ADDRESS, WFTM_TOKEN_ADDRESS, YVWETH_ADDRESS, YVWETH_OPT_ADDRESS, YVWFTM_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import performBatchedUpdates from '@yearn-finance/web-lib/utils/performBatchedUpdates';
@@ -135,7 +135,7 @@ function ActionFlowContextApp({children, currentVault}: {children: ReactNode, cu
const [possibleOptionsTo, set_possibleOptionsTo] = useState([]);
const [possibleZapOptionsTo, set_possibleZapOptionsTo] = useState([]);
const {data: depositLimit} = useContractRead({
- address: toWagmiAddress(currentVault.address),
+ address: currentVault.address,
abi: VAULT_ABI,
chainId: chainID,
functionName: 'depositLimit'
diff --git a/apps/vaults/contexts/useSolver.tsx b/apps/vaults/contexts/useSolver.tsx
index 5127f71d4..dbaf6687a 100644
--- a/apps/vaults/contexts/useSolver.tsx
+++ b/apps/vaults/contexts/useSolver.tsx
@@ -1,4 +1,5 @@
-import React, {createContext, useCallback, useContext, useState} from 'react';
+import React, {createContext, useCallback, useContext, useRef, useState} from 'react';
+import {serialize} from 'wagmi';
import {useDebouncedEffect, useDeepCompareMemo} from '@react-hookz/web';
import {useActionFlow} from '@vaults/contexts/useActionFlow';
import {useSolverChainCoin} from '@vaults/hooks/useSolverChainCoin';
@@ -35,14 +36,16 @@ export const isSolverDisabled = {
[Solver.PARTNER_CONTRACT]: false,
[Solver.CHAIN_COIN]: false,
[Solver.INTERNAL_MIGRATION]: false,
- [Solver.COWSWAP]: false,
[Solver.OPTIMISM_BOOSTER]: false,
+
+ [Solver.COWSWAP]: false,
[Solver.WIDO]: false,
[Solver.PORTALS]: false,
[Solver.NONE]: false
};
type TUpdateSolverHandler = {
+ currentNonce: number;
request: TInitSolverArgs;
quote: PromiseSettledResult;
solver: Solver;
@@ -55,7 +58,6 @@ const DefaultWithSolverContext: TWithSolver = {
expectedOut: toNormalizedBN(0),
hash: undefined,
isLoadingExpectedOut: false,
- onRetrieveExpectedOut: async (): Promise => toNormalizedBN(0),
onRetrieveAllowance: async (): Promise => toNormalizedBN(0),
onApprove: async (): Promise => Promise.resolve(),
onExecuteDeposit: async (): Promise => Promise.resolve(),
@@ -66,6 +68,7 @@ const WithSolverContext = createContext(DefaultWithSolverContext);
function WithSolverContextApp({children}: { children: React.ReactElement }): React.ReactElement {
const {address} = useWeb3();
const {currentVault, actionParams, currentSolver, isDepositing} = useActionFlow();
+ const executionNonce = useRef(0);
const cowswap = useSolverCowswap();
const wido = useSolverWido();
const vanilla = useSolverVanilla();
@@ -77,22 +80,24 @@ function WithSolverContextApp({children}: { children: React.ReactElement }): Rea
const [currentSolverState, set_currentSolverState] = useState(vanilla);
const [isLoading, set_isLoading] = useState(false);
- async function handleUpdateSolver({request, quote, solver, ctx}: TUpdateSolverHandler): Promise {
+ const handleUpdateSolver = useCallback(async ({currentNonce, request, quote, solver, ctx}: TUpdateSolverHandler): Promise => {
if (quote.status !== 'fulfilled') {
return;
}
-
- const requestHash = await hash(JSON.stringify({...request, solver, expectedOut: quote.value.raw.toString()}));
+ if (currentNonce !== executionNonce.current) {
+ return;
+ }
+ const requestHash = await hash(serialize({...request, solver, expectedOut: quote.value.raw}));
performBatchedUpdates((): void => {
set_currentSolverState({...ctx, quote: quote.value, hash: requestHash});
set_isLoading(false);
});
- }
+ }, [executionNonce]);
/* 🔵 - Yearn Finance **************************************************************************
** Based on the currentSolver, we initialize the solver with the required parameters.
**********************************************************************************************/
- const onUpdateSolver = useCallback(async (): Promise => {
+ const onUpdateSolver = useCallback(async (currentNonce: number): Promise => {
if (!actionParams?.selectedOptionFrom || !actionParams?.selectedOptionTo || !actionParams?.amount.raw) {
return;
}
@@ -137,7 +142,6 @@ function WithSolverContextApp({children}: { children: React.ReactElement }): Rea
solvers[Solver.NONE] = {quote: {status: 'fulfilled', value: toNormalizedBN(0)}, ctx: vanilla};
const solverPriority = [Solver.WIDO, Solver.COWSWAP, Solver.PORTALS, Solver.NONE];
-
const newSolverPriority = [currentSolver, ...solverPriority.filter((solver): boolean => solver !== currentSolver)];
for (const solver of newSolverPriority) {
@@ -146,47 +150,42 @@ function WithSolverContextApp({children}: { children: React.ReactElement }): Rea
}
const {quote, ctx} = solvers[solver] ?? solvers[Solver.NONE];
- await handleUpdateSolver({request, quote, solver, ctx});
+ await handleUpdateSolver({currentNonce, request, quote, solver, ctx});
return;
}
-
break;
}
case Solver.OPTIMISM_BOOSTER: {
- const quote = await optimismBooster.init(request);
- const requestHash = await hash(JSON.stringify({...request, solver: Solver.OPTIMISM_BOOSTER, expectedOut: quote.raw.toString()}));
- performBatchedUpdates((): void => {
- set_currentSolverState({...optimismBooster, quote, hash: requestHash});
- set_isLoading(false);
- });
+ const [quote] = await Promise.allSettled([optimismBooster.init(request)]);
+ await handleUpdateSolver({currentNonce, request, quote, solver: Solver.OPTIMISM_BOOSTER, ctx: chainCoin});
break;
}
case Solver.CHAIN_COIN: {
const [quote] = await Promise.allSettled([chainCoin.init(request)]);
- await handleUpdateSolver({request, quote, solver: Solver.CHAIN_COIN, ctx: chainCoin});
+ await handleUpdateSolver({currentNonce, request, quote, solver: Solver.CHAIN_COIN, ctx: chainCoin});
break;
}
case Solver.PARTNER_CONTRACT: {
const [quote] = await Promise.allSettled([partnerContract.init(request)]);
- await handleUpdateSolver({request, quote, solver: Solver.PARTNER_CONTRACT, ctx: partnerContract});
+ await handleUpdateSolver({currentNonce, request, quote, solver: Solver.PARTNER_CONTRACT, ctx: partnerContract});
break;
}
case Solver.INTERNAL_MIGRATION: {
request.migrator = currentVault.migration.contract;
const [quote] = await Promise.allSettled([internalMigration.init(request)]);
- await handleUpdateSolver({request, quote, solver: Solver.INTERNAL_MIGRATION, ctx: internalMigration});
+ await handleUpdateSolver({currentNonce, request, quote, solver: Solver.INTERNAL_MIGRATION, ctx: internalMigration});
break;
}
default: {
const [quote] = await Promise.allSettled([vanilla.init(request)]);
- await handleUpdateSolver({request, quote, solver: Solver.VANILLA, ctx: vanilla});
+ await handleUpdateSolver({currentNonce, request, quote, solver: Solver.VANILLA, ctx: vanilla});
}
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [address, actionParams, currentSolver, cowswap.init, vanilla.init, wido.init, internalMigration.init, optimismBooster.init, isDepositing, currentVault.migration.contract]); //Ignore the warning, it's a false positive
+ }, [actionParams.selectedOptionFrom, actionParams.selectedOptionTo, actionParams.amount.raw, address, isDepositing, currentSolver, wido, cowswap, portals, vanilla, handleUpdateSolver, optimismBooster, chainCoin, partnerContract, currentVault.migration.contract, internalMigration]);
useDebouncedEffect((): void => {
- onUpdateSolver();
+ const currentNonce = ++executionNonce.current;
+ onUpdateSolver(currentNonce);
}, [onUpdateSolver], 0);
const contextValue = useDeepCompareMemo((): TWithSolver => ({
@@ -195,7 +194,6 @@ function WithSolverContextApp({children}: { children: React.ReactElement }): Rea
expectedOut: currentSolverState?.quote || toNormalizedBN(0),
hash: currentSolverState?.hash,
isLoadingExpectedOut: isLoading,
- onRetrieveExpectedOut: currentSolverState.onRetrieveExpectedOut,
onRetrieveAllowance: currentSolverState.onRetrieveAllowance,
onApprove: currentSolverState.onApprove,
onExecuteDeposit: currentSolverState.onExecuteDeposit,
diff --git a/apps/vaults/contexts/useStakingRewards.tsx b/apps/vaults/contexts/useStakingRewards.tsx
index 26eafd58e..9de1a9ba3 100644
--- a/apps/vaults/contexts/useStakingRewards.tsx
+++ b/apps/vaults/contexts/useStakingRewards.tsx
@@ -6,7 +6,7 @@ import STAKING_REWARDS_REGISTRY_ABI from '@vaults/utils/abi/stakingRewardsRegist
import {multicall, readContract} from '@wagmi/core';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {STAKING_REWARDS_REGISTRY_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {decodeAsBigInt, decodeAsString} from '@yearn-finance/web-lib/utils/decoder';
import {keyBy} from '@common/utils';
@@ -54,7 +54,7 @@ export const StakingRewardsContextApp = memo(function StakingRewardsContextApp({
** Base wagmi contract struct ready to use in the viem functions call
******************************************************************************************/
const baseContract = {
- address: toWagmiAddress(STAKING_REWARDS_REGISTRY_ADDRESS),
+ address: STAKING_REWARDS_REGISTRY_ADDRESS,
abi: STAKING_REWARDS_REGISTRY_ABI,
chainId: chainID
} as const;
@@ -90,7 +90,7 @@ export const StakingRewardsContextApp = memo(function StakingRewardsContextApp({
if (stakingRewardsAddress.status === 'success') {
const address = decodeAsString(stakingRewardsAddress);
const baseStackingContract = {
- address: toWagmiAddress(address),
+ address: toAddress(address),
abi: STAKING_REWARDS_ABI,
chainId: chainID
};
@@ -130,7 +130,7 @@ export const StakingRewardsContextApp = memo(function StakingRewardsContextApp({
const calls = [];
for (const {address} of stakingRewards) {
const baseContract = {
- address: toWagmiAddress(address),
+ address,
abi: STAKING_REWARDS_ABI,
chainId: chainID
} as const;
diff --git a/apps/vaults/hooks/usePortalsApi.ts b/apps/vaults/hooks/usePortalsApi.ts
index 61bbff711..097e834f9 100644
--- a/apps/vaults/hooks/usePortalsApi.ts
+++ b/apps/vaults/hooks/usePortalsApi.ts
@@ -1,4 +1,3 @@
-import {useState} from 'react';
import axios from 'axios';
import type {AxiosInstance, AxiosResponse} from 'axios';
@@ -96,67 +95,46 @@ const NETWORK = new Map([
[56, 'bsc']
]);
-type TUsePortalsApi = {
- getEstimate: (props: TGetEstimateProps) => Promise;
- getTransaction: (
- props: TGetTransactionProps
- ) => Promise;
- getApproval: (props: TGetApprovalProps) => Promise;
- error: unknown;
-};
+type TPortalsEstimateResp = {
+ data: TPortalEstimate | undefined,
+ error?: Error | undefined
+}
+export async function getPortalsEstimate({network, params}: TGetEstimateProps): Promise {
+ const baseURL = 'https://api.portals.fi/v1';
+ const axiosInstance: AxiosInstance = axios.create({baseURL});
-const usePortalsApi = (): TUsePortalsApi => {
- const [error, set_error] = useState(null);
+ try {
+ const path = `/portal/${NETWORK.get(network)}/estimate`;
+ const response: AxiosResponse = await axiosInstance.get(path, {params});
+ return {data: response.data};
+ } catch (err) {
+ return {data: undefined, error: err as Error};
+ }
+}
+export async function getPortalsTx({network, params}: TGetTransactionProps): Promise {
const baseURL = 'https://api.portals.fi/v1';
const axiosInstance: AxiosInstance = axios.create({baseURL});
- const getEstimate = async ({
- network,
- params
- }: TGetEstimateProps): Promise => {
- try {
- const endpoint = `/portal/${NETWORK.get(network)}/estimate`;
- const response: AxiosResponse =
- await axiosInstance.get(endpoint, {params});
- return response.data;
- } catch (err) {
- set_error(err);
- return null;
- }
- };
-
- const getTransaction = async ({
- network,
- params
- }: TGetTransactionProps): Promise => {
- try {
- const endpoint = `/portal/${NETWORK.get(network)}`;
- const response: AxiosResponse =
- await axiosInstance.get(endpoint, {params});
- return response.data;
- } catch (err) {
- set_error(err);
- return null;
- }
- };
+ try {
+ const path = `/portal/${NETWORK.get(network)}`;
+ const response: AxiosResponse = await axiosInstance.get(path, {params});
+ return response.data;
+ } catch (err) {
+ return null;
+ }
+}
- const getApproval = async ({
- network,
- params
- }: TGetApprovalProps): Promise => {
- try {
- const endpoint = `/approval/${NETWORK.get(network)}`;
- const response: AxiosResponse =
- await axiosInstance.get(endpoint, {params});
- return response.data;
- } catch (err) {
- set_error(err);
- return null;
- }
- };
- return {getEstimate, getTransaction, getApproval, error};
-};
+export async function getPortalsApproval({network, params}: TGetApprovalProps): Promise {
+ const baseURL = 'https://api.portals.fi/v1';
+ const axiosInstance: AxiosInstance = axios.create({baseURL});
-export default usePortalsApi;
+ try {
+ const path = `/approval/${NETWORK.get(network)}`;
+ const response: AxiosResponse = await axiosInstance.get(path, {params});
+ return response.data;
+ } catch (err) {
+ return null;
+ }
+}
diff --git a/apps/vaults/hooks/useSolverChainCoin.ts b/apps/vaults/hooks/useSolverChainCoin.ts
index e287046c3..feb3834c8 100644
--- a/apps/vaults/hooks/useSolverChainCoin.ts
+++ b/apps/vaults/hooks/useSolverChainCoin.ts
@@ -1,11 +1,10 @@
import {useCallback, useMemo, useRef} from 'react';
-import useSWRMutation from 'swr/mutation';
import {Solver} from '@vaults/contexts/useSolver';
-import {useVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
import {getEthZapperContract} from '@vaults/utils';
+import getVaultEstimateOut from '@vaults/utils/getVaultEstimateOut';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, toAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {approvedERC20Amount, approveERC20, depositETH, withdrawETH} from '@common/utils/actions';
@@ -14,48 +13,12 @@ import {assert} from '@common/utils/assert';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {TVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
-import type {TInitSolverArgs, TSolverContext, TVanillaLikeResult} from '@vaults/types/solvers';
-
-function useQuote(): [TVanillaLikeResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const retrieveExpectedOut = useVaultEstimateOutFetcher();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'chainCoinQuote',
- async (_: string, data: {arg: TVaultEstimateOutFetcher}): Promise => retrieveExpectedOut(data.arg)
- );
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false //do nothing, for consistency with other solvers
- ): Promise => {
- shouldPreventErrorToast;
-
- const canExecuteFetch = (
- !request.inputToken || !request.outputToken || !request.inputAmount ||
- !(isZeroAddress(request.inputToken.value) || isZeroAddress(request.outputToken.value) || request.inputAmount === 0n)
- );
-
- if (canExecuteFetch) {
- const result = await trigger([request.inputToken, request.outputToken, request.inputAmount, request.isDepositing]);
- return result || toNormalizedBN(0);
- }
- return toNormalizedBN(0);
- }, [trigger]);
-
- return [
- useMemo((): TVanillaLikeResult => ({
- result: data || toNormalizedBN(0),
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
-}
+import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
export function useSolverChainCoin(): TSolverContext {
const {provider} = useWeb3();
- const {safeChainID} = useChainID();
- const [latestQuote, getQuote] = useQuote();
+ const {chainID, safeChainID} = useChainID();
+ const latestQuote = useRef();
const request = useRef();
const existingAllowances = useRef>({});
@@ -66,28 +29,18 @@ export function useSolverChainCoin(): TSolverContext {
**********************************************************************************************/
const init = useCallback(async (_request: TInitSolverArgs): Promise => {
request.current = _request;
- return await getQuote(_request);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (request: TInitSolverArgs): Promise => {
- const quoteResult = await getQuote(request, true);
- return quoteResult;
- }, [getQuote]);
+ const estimateOut = await getVaultEstimateOut({
+ inputToken: toAddress(_request.inputToken.value),
+ outputToken: toAddress(_request.outputToken.value),
+ inputDecimals: _request.inputToken.decimals,
+ outputDecimals: _request.outputToken.decimals,
+ inputAmount: _request.inputAmount,
+ isDepositing: _request.isDepositing,
+ chainID: chainID
+ });
+ latestQuote.current = estimateOut;
+ return latestQuote.current;
+ }, [chainID]);
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
@@ -132,8 +85,8 @@ export function useSolverChainCoin(): TSolverContext {
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(getEthZapperContract(safeChainID)),
+ contractAddress: toAddress(request.current.inputToken.value),
+ spenderAddress: getEthZapperContract(safeChainID),
amount: amount,
statusHandler: txStatusSetter
});
@@ -156,7 +109,7 @@ export function useSolverChainCoin(): TSolverContext {
const result = await depositETH({
connector: provider,
- contractAddress: toWagmiAddress(getEthZapperContract(safeChainID)),
+ contractAddress: getEthZapperContract(safeChainID),
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -179,7 +132,7 @@ export function useSolverChainCoin(): TSolverContext {
const result = await withdrawETH({
connector: provider,
- contractAddress: toWagmiAddress(getEthZapperContract(safeChainID)),
+ contractAddress: getEthZapperContract(safeChainID),
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -190,14 +143,11 @@ export function useSolverChainCoin(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.CHAIN_COIN,
- quote: latestQuote?.result || toNormalizedBN(0),
- getQuote: getQuote,
- refreshQuote: refreshQuote,
+ quote: latestQuote?.current || toNormalizedBN(0),
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit,
onExecuteWithdraw
- }), [latestQuote?.result, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [latestQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverCowswap.ts b/apps/vaults/hooks/useSolverCowswap.ts
index 7d07f6bee..f3622ce57 100644
--- a/apps/vaults/hooks/useSolverCowswap.ts
+++ b/apps/vaults/hooks/useSolverCowswap.ts
@@ -1,98 +1,73 @@
import {useCallback, useMemo, useRef} from 'react';
import {ethers} from 'ethers';
import axios from 'axios';
-import useSWRMutation from 'swr/mutation';
-import {domain, OrderKind, SigningScheme, signOrder} from '@gnosis.pm/gp-v2-contracts';
+import {OrderBookApi, OrderQuoteSide, OrderSigningUtils} from '@cowprotocol/cow-sdk';
import {isSolverDisabled, Solver} from '@vaults/contexts/useSolver';
-import {yToast} from '@yearn-finance/web-lib/components/yToast';
+import {toast} from '@yearn-finance/web-lib/components/yToast';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, isZeroAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, MAX_UINT_256, SOLVER_COW_VAULT_RELAYER_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
-import {Transaction} from '@yearn-finance/web-lib/utils/web3/transaction';
+import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {useYearn} from '@common/contexts/useYearn';
import {approvedERC20Amount, approveERC20, isApprovedERC20} from '@common/utils/actions';
import {assert} from '@common/utils/assert';
-import type {AxiosError} from 'axios';
-import type {BaseError} from 'viem';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxResponse, TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {ApiError, Order, QuoteQuery, Timestamp} from '@gnosis.pm/gp-v2-contracts';
+import type {Order, OrderCreation, OrderQuoteResponse, SigningResult, SigningScheme, UnsignedOrder} from '@cowprotocol/cow-sdk';
import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
-import type {TCowAPIResult, TCowResult} from '@vaults/types/solvers.cowswap';
-function useQuote(): [TCowResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const {toast} = yToast();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'https://api.cow.fi/mainnet/api/v1/quote',
- async (url: string, data: {arg: unknown}): Promise => {
- const req = await axios.post(url, data.arg);
- return req.data;
- }
- );
+const orderBookApi = new OrderBookApi({chainId: 1});
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false
- ): Promise => {
- const YEARN_APP_DATA = '0x5d22bf49b708de1d2d9547a6cca9faccbdc2b162012e8573811c07103b163d4b';
- const quote: QuoteQuery = ({
- from: request.from, // receiver
- sellToken: toAddress(request.inputToken.value), // token to spend
- buyToken: toAddress(request.outputToken.value), // token to receive
- receiver: request.from, // always the same as from
- appData: YEARN_APP_DATA, // Always this
- kind: OrderKind.SELL, // always sell
- partiallyFillable: false, // always false
- validTo: 0,
- sellAmountBeforeFee: toBigInt(request?.inputAmount || 0).toString() // amount to sell, in wei
- });
+async function getQuote(
+ request: TInitSolverArgs
+): Promise<{data: OrderQuoteResponse | undefined, error: Error | undefined}> {
+ const YEARN_APP_DATA = '0x5d22bf49b708de1d2d9547a6cca9faccbdc2b162012e8573811c07103b163d4b';
+ const quoteRequest = {
+ from: request.from, // receiver
+ sellToken: request.inputToken.value, // token to spend
+ buyToken: request.outputToken.value, // token to receive
+ receiver: request.from, // always the same as from
+ appData: YEARN_APP_DATA, // Always this
+ kind: OrderQuoteSide.kind.SELL, // always sell
+ partiallyFillable: false, // always false
+ validTo: 0,
+ sellAmountBeforeFee: toBigInt(request?.inputAmount).toString() // amount to sell, in wei
+ };
- const canExecuteFetch = (
- !(isZeroAddress(quote.from) || isZeroAddress(quote.sellToken) || isZeroAddress(quote.buyToken))
- && toBigInt(request?.inputAmount) > 0n
- );
- if (canExecuteFetch) {
- quote.validTo = Math.round((new Date().setMinutes(new Date().getMinutes() + 10) / 1000));
- try {
- const result = await trigger(quote, {revalidate: false});
- return (result);
- } catch (error) {
- const _error = error as AxiosError;
- console.error(error);
- if (shouldPreventErrorToast) {
- return undefined;
- }
- const message = `Zap not possible. Try again later or pick another token. ${_error?.response?.data?.description ? `(Reason: [${_error?.response?.data?.description}])` : ''}`;
- toast({type: 'error', content: message});
- return undefined;
- }
- }
- return undefined;
- }, [trigger]); // eslint-disable-line react-hooks/exhaustive-deps
+ if (isZeroAddress(quoteRequest.from)) {
+ return {data: undefined, error: new Error('Invalid from address')};
+ }
+ if (isZeroAddress(quoteRequest.sellToken)) {
+ return {data: undefined, error: new Error('Invalid sell token')};
+ }
+ if (isZeroAddress(quoteRequest.buyToken)) {
+ return {data: undefined, error: new Error('Invalid buy token')};
+ }
+ if (toBigInt(request?.inputAmount) <= 0n) {
+ return {data: undefined, error: new Error('Invalid sell amount')};
+ }
- return [
- useMemo((): TCowResult => ({
- result: data,
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
+ quoteRequest.validTo = Math.round((new Date().setMinutes(new Date().getMinutes() + 10) / 1000));
+ try {
+ const result = await orderBookApi.getQuote(quoteRequest);
+ return {data: result, error: undefined};
+ } catch (error) {
+ return {data: undefined, error: error as Error};
+ }
}
export function useSolverCowswap(): TSolverContext {
const {zapSlippage} = useYearn();
- const {address, provider} = useWeb3();
+ const {provider} = useWeb3();
const {safeChainID} = useChainID();
const maxIterations = 1000; // 1000 * up to 3 seconds = 3000 seconds = 50 minutes
const shouldUsePresign = false; //Debug only
- const [, getQuote] = useQuote();
const request = useRef();
- const latestQuote = useRef();
+ const latestQuote = useRef();
const existingAllowances = useRef>({});
const isDisabled = isSolverDisabled[Solver.COWSWAP] || safeChainID !== 1;
@@ -101,7 +76,7 @@ export function useSolverCowswap(): TSolverContext {
** fluctuations. The buyAmountWithSlippage is used to request this amount instead of the
** original buyAmount.
**********************************************************************************************/
- const getBuyAmountWithSlippage = useCallback((currentQuote: TCowAPIResult, decimals: number): string => {
+ const getBuyAmountWithSlippage = useCallback((currentQuote: OrderQuoteResponse, decimals: number): string => {
if (!currentQuote) {
return '0';
}
@@ -157,17 +132,23 @@ export function useSolverCowswap(): TSolverContext {
** to get the current quote for the provided request.current.
******************************************************************************************/
request.current = _request;
- const quote = await getQuote(_request, !shouldLogError);
- if (!quote) {
+ const {data, error} = await getQuote(_request);
+ if (!data) {
+ type TCowRequestError = {body: {description: string}};
+ const err = error as unknown as TCowRequestError;
+ if (error && !shouldLogError) {
+ if (err?.body?.description) {
+ toast({type: 'error', content: err?.body?.description});
+ } else {
+ toast({type: 'error', content: 'Zap not possible. Try again later or pick another token.'});
+ }
+ }
return toNormalizedBN(0);
}
- latestQuote.current = quote;
- getBuyAmountWithSlippage(quote, request?.current?.outputToken?.decimals || 18);
- return toNormalizedBN(
- toBigInt(quote?.quote?.buyAmount?.toString()),
- request?.current?.outputToken?.decimals || 18
- );
- }, [getBuyAmountWithSlippage, getQuote, isDisabled]);
+ latestQuote.current = data;
+ const buyAmountWithSlippage = getBuyAmountWithSlippage(data, _request?.outputToken?.decimals || 18);
+ return toNormalizedBN(buyAmountWithSlippage || 0, _request?.outputToken?.decimals || 18);
+ }, [getBuyAmountWithSlippage, isDisabled]);
/* 🔵 - Yearn Finance **************************************************************************
** signCowswapOrder is used to sign the order with the user's wallet. The signature is used
@@ -175,47 +156,26 @@ export function useSolverCowswap(): TSolverContext {
** If shouldUsePresign is set to true, the signature is not required and the approval is
** skipped. This should only be used for debugging purposes.
**********************************************************************************************/
- const signCowswapOrder = useCallback(async (quote: Order): Promise => {
+ const signCowswapOrder = useCallback(async (quote: Order): Promise => {
if (shouldUsePresign) {
- return toAddress(address || '');
+ await new Promise(async (resolve): Promise => setTimeout(resolve, 1000));
+ return ({signature: '0x', signingScheme: 'presign'} as any);
}
assert(provider, 'Provider is not set');
- const signer = await provider.getWalletClient();
- const rawSignature = await signOrder(
- domain(1, '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'),
- quote,
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- signer as any, //TODO: <--- THIS WILL NOT WORK
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- //TODO: FIX HERE FOR RELEASE
- SigningScheme.EIP712
- );
- return ethers.utils.joinSignature(rawSignature.data);
- }, [provider, shouldUsePresign, address]);
+ const wagmiSigner = await provider.getWalletClient();
+ const wagmiProvider = await provider.getProvider();
+ const ethersProvider = new ethers.providers.Web3Provider(wagmiProvider);
+ const signer = ethersProvider.getSigner(wagmiSigner.account.address);
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.current.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
+ const rawSignature = await OrderSigningUtils.signOrder(
+ {...quote as UnsignedOrder},
+ safeChainID,
+ signer
+ );
+ return rawSignature;
+ }, [shouldUsePresign, provider, safeChainID]);
/* 🔵 - Yearn Finance **************************************************************************
** Cowswap orders have a validity period and the return value on submit is not the execution
@@ -223,7 +183,7 @@ export function useSolverCowswap(): TSolverContext {
** boolean value indicating whether the order was successful or not.
** It will timeout once the order is no longer valid or after 50 minutes (max should be 30mn)
**********************************************************************************************/
- async function checkOrderStatus(orderUID: string, validTo: Timestamp): Promise<{isSuccessful: boolean, error?: Error}> {
+ async function checkOrderStatus(orderUID: string, validTo: number): Promise<{isSuccessful: boolean, error?: Error}> {
for (let i = 0; i < maxIterations; i++) {
const {data: order} = await axios.get(`https://api.cow.fi/mainnet/api/v1/orders/${orderUID}`);
if (order?.status === 'fulfilled') {
@@ -247,33 +207,39 @@ export function useSolverCowswap(): TSolverContext {
** not.
**********************************************************************************************/
const execute = useCallback(async (): Promise => {
- if (!latestQuote?.current || !latestQuote?.current?.quote || !request.current || isDisabled) {
+ if (isDisabled) {
return ({isSuccessful: false});
}
+
+ assert(latestQuote?.current?.quote, 'No quote available');
+ assert(request?.current, 'No request available');
+
const {quote, from, id} = latestQuote.current;
- try {
- const buyAmountWithSlippage = getBuyAmountWithSlippage(latestQuote.current, request.current.outputToken.decimals);
- const signature = await signCowswapOrder({...quote, buyAmount: buyAmountWithSlippage});
- const {data: orderUID} = await axios.post('https://api.cow.fi/mainnet/api/v1/orders', {
- ...quote,
- buyAmount: buyAmountWithSlippage,
- from: from,
- quoteId: id,
- signature: signature,
- signingScheme: shouldUsePresign ? 'presign' : 'eip712'
- });
- if (orderUID) {
- const {isSuccessful, error} = await checkOrderStatus(orderUID, quote.validTo);
- if (error) {
- console.error(error);
- return ({isSuccessful: false, error: error as BaseError || ''});
- }
- return {isSuccessful};
+ const buyAmountWithSlippage = getBuyAmountWithSlippage(
+ latestQuote.current,
+ request.current.outputToken.decimals
+ );
+ quote.buyAmount = buyAmountWithSlippage;
+ const {signature, signingScheme} = await signCowswapOrder(quote as Order);
+ const orderCreation: OrderCreation = {
+ ...quote,
+ buyAmount: buyAmountWithSlippage,
+ from: from,
+ quoteId: id,
+ signature: signature,
+ signingScheme: (shouldUsePresign ? 'presign' : signingScheme) as string as SigningScheme
+ };
+
+ const orderUID = await orderBookApi.sendOrder(orderCreation);
+ if (orderUID) {
+ const {isSuccessful, error} = await checkOrderStatus(orderUID, quote.validTo);
+ if (error) {
+ console.error(error);
+ return ({isSuccessful: false, error: error as any || ''});
}
- } catch (error) {
- console.error(error);
- return ({isSuccessful: false, error: error as BaseError || ''});
+ return {isSuccessful};
}
+
return ({isSuccessful: false});
}, [getBuyAmountWithSlippage, shouldUsePresign, signCowswapOrder, isDisabled]);
@@ -287,29 +253,12 @@ export function useSolverCowswap(): TSolverContext {
}
return (
toNormalizedBN(
- toBigInt(latestQuote?.current?.quote?.buyAmount.toString()),
+ toBigInt(latestQuote?.current?.quote?.buyAmount),
request?.current?.outputToken?.decimals || 18
)
);
}, [latestQuote, isDisabled]);
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (_request: TInitSolverArgs): Promise => {
- if (isDisabled || !_request.inputToken.solveVia?.includes(Solver.COWSWAP)) {
- return (toNormalizedBN(0));
- }
- const quoteResult = await getQuote(_request, true);
- return (
- toNormalizedBN(
- toBigInt(quoteResult?.quote?.buyAmount.toString()),
- _request.outputToken.decimals || 18
- )
- );
- }, [getQuote, isDisabled]);
-
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
** be used to determine if the user should approve the token or not.
@@ -323,17 +272,17 @@ export function useSolverCowswap(): TSolverContext {
const key = allowanceKey(
safeChainID,
- toAddress(request.current.inputToken.value),
- toAddress(request.current.outputToken.value),
- toAddress(request.current.from)
+ request.current.inputToken.value,
+ request.current.outputToken.value,
+ request.current.from
);
if (existingAllowances.current[key] && !shouldForceRefetch) {
return existingAllowances.current[key];
}
const allowance = await approvedERC20Amount(
provider,
- toAddress(request.current.inputToken.value), //Input token
- toAddress(SOLVER_COW_VAULT_RELAYER_ADDRESS) //Spender, aka Cowswap solver
+ request.current.inputToken.value, //Input token
+ SOLVER_COW_VAULT_RELAYER_ADDRESS //Spender, aka Cowswap solver
);
existingAllowances.current[key] = toNormalizedBN(allowance, request.current.inputToken.decimals);
return existingAllowances.current[key];
@@ -352,24 +301,23 @@ export function useSolverCowswap(): TSolverContext {
if (isDisabled) {
return;
}
- assert(latestQuote?.current?.quote, 'Quote is not defined');
assert(request.current, 'Request is not defined');
assert(request?.current?.inputToken?.solveVia?.includes(Solver.COWSWAP), 'Input token is not supported by Cowswap');
const isApproved = await isApprovedERC20(
provider,
- toAddress(request.current.inputToken.value), //token to approve
- toAddress(SOLVER_COW_VAULT_RELAYER_ADDRESS), //Cowswap relayer
- toBigInt(amount.toString())
+ request.current.inputToken.value, //token to approve
+ SOLVER_COW_VAULT_RELAYER_ADDRESS, //Cowswap relayer
+ toBigInt(amount)
);
if (isApproved) {
return onSuccess();
}
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(SOLVER_COW_VAULT_RELAYER_ADDRESS),
- amount: toBigInt(amount.toString()),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: SOLVER_COW_VAULT_RELAYER_ADDRESS,
+ amount: toBigInt(amount),
statusHandler: txStatusSetter
});
if (result.isSuccessful) {
@@ -382,45 +330,38 @@ export function useSolverCowswap(): TSolverContext {
** Cowswap solver. The deposit will be executed by the Cowswap solver by
** simply swapping the input token for the output token.
**************************************************************************/
- const onExecuteDeposit = useCallback(async (
+ const onExecute = useCallback(async (
txStatusSetter: React.Dispatch>,
onSuccess: () => Promise
): Promise => {
assert(provider, 'Provider is not defined');
+ txStatusSetter({...defaultTxStatus, pending: true});
- new Transaction(provider, execute, txStatusSetter)
- .populate()
- .onSuccess(onSuccess)
- .perform();
- }, [execute, provider]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** This execute function is not an actual withdraw, but a swap using the
- ** Cowswap solver. The withdraw will be executed by the Cowswap solver by
- ** simply swapping the input token for the output token.
- **************************************************************************/
- const onExecuteWithdraw = useCallback(async (
- txStatusSetter: React.Dispatch>,
- onSuccess: () => Promise
- ): Promise => {
- assert(provider, 'Provider is not defined');
+ try {
+ const result = await execute();
+ if (result.isSuccessful) {
+ txStatusSetter({...defaultTxStatus, success: true});
+ onSuccess();
+ } else {
+ txStatusSetter({...defaultTxStatus, error: true, errorMessage: result.error?.message || 'Transaction failed'});
+ }
+ } catch (error) {
+ txStatusSetter({...defaultTxStatus, error: true, errorMessage: 'Transaction rejected'});
+ } finally {
+ setTimeout((): void => {
+ txStatusSetter({...defaultTxStatus});
+ }, 3000);
+ }
- new Transaction(provider, execute, txStatusSetter)
- .populate()
- .onSuccess(onSuccess)
- .perform();
}, [execute, provider]);
return useMemo((): TSolverContext => ({
type: Solver.COWSWAP,
quote: expectedOut,
- getQuote: getQuote,
- refreshQuote,
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
- onExecuteDeposit,
- onExecuteWithdraw
- }), [expectedOut, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance, onRetrieveExpectedOut]);
+ onExecuteDeposit: onExecute,
+ onExecuteWithdraw: onExecute
+ }), [expectedOut, init, onApprove, onExecute, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverInternalMigration.ts b/apps/vaults/hooks/useSolverInternalMigration.ts
index 083a5c57c..98e52ba4f 100644
--- a/apps/vaults/hooks/useSolverInternalMigration.ts
+++ b/apps/vaults/hooks/useSolverInternalMigration.ts
@@ -1,10 +1,9 @@
import {useCallback, useMemo, useRef} from 'react';
-import useSWRMutation from 'swr/mutation';
import {Solver} from '@vaults/contexts/useSolver';
-import {useVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
+import getVaultEstimateOut from '@vaults/utils/getVaultEstimateOut';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, toAddress} from '@yearn-finance/web-lib/utils/address';
import {MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {approvedERC20Amount, approveERC20, migrateShares} from '@common/utils/actions';
@@ -13,48 +12,12 @@ import {assert} from '@common/utils/assert';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {TVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
-import type {TInitSolverArgs, TSolverContext, TVanillaLikeResult} from '@vaults/types/solvers';
-
-function useQuote(): [TVanillaLikeResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const retrieveExpectedOut = useVaultEstimateOutFetcher();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'InternalMigration',
- async (_: string, data: {arg: TVaultEstimateOutFetcher}): Promise => retrieveExpectedOut(data.arg)
- );
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false //do nothing, for consistency with other solvers
- ): Promise => {
- shouldPreventErrorToast;
-
- const canExecuteFetch = (
- !request.inputToken || !request.outputToken || !request.inputAmount ||
- !(isZeroAddress(request.inputToken.value) || isZeroAddress(request.outputToken.value) || request.inputAmount === 0n)
- );
-
- if (canExecuteFetch) {
- const result = await trigger([request.inputToken, request.outputToken, request.inputAmount, request.isDepositing]);
- return result || toNormalizedBN(0);
- }
- return toNormalizedBN(0);
- }, [trigger]);
-
- return [
- useMemo((): TVanillaLikeResult => ({
- result: data || toNormalizedBN(0),
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
-}
+import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
export function useSolverInternalMigration(): TSolverContext {
const {provider} = useWeb3();
- const {safeChainID} = useChainID();
- const [latestQuote, getQuote] = useQuote();
+ const {chainID, safeChainID} = useChainID();
+ const latestQuote = useRef();
const request = useRef();
const existingAllowances = useRef>({});
@@ -65,28 +28,18 @@ export function useSolverInternalMigration(): TSolverContext {
**********************************************************************************************/
const init = useCallback(async (_request: TInitSolverArgs): Promise => {
request.current = _request;
- return await getQuote(_request);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (request: TInitSolverArgs): Promise => {
- const quoteResult = await getQuote(request, true);
- return quoteResult;
- }, [getQuote]);
+ const estimateOut = await getVaultEstimateOut({
+ inputToken: toAddress(_request.inputToken.value),
+ outputToken: toAddress(_request.outputToken.value),
+ inputDecimals: _request.inputToken.decimals,
+ outputDecimals: _request.outputToken.decimals,
+ inputAmount: _request.inputAmount,
+ isDepositing: _request.isDepositing,
+ chainID: chainID
+ });
+ latestQuote.current = estimateOut;
+ return latestQuote.current;
+ }, [chainID]);
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
@@ -133,8 +86,8 @@ export function useSolverInternalMigration(): TSolverContext {
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(request.current.migrator),
+ contractAddress: toAddress(request.current.inputToken.value),
+ spenderAddress: request.current.migrator,
amount: amount,
statusHandler: txStatusSetter
});
@@ -152,15 +105,12 @@ export function useSolverInternalMigration(): TSolverContext {
onSuccess: () => Promise
): Promise => {
assert(request.current, 'Request is not set');
- assert(request.current.inputToken, 'Input token is not set');
- assert(request.current.outputToken, 'Output token is not set');
- assert(request.current.migrator, 'Migrator is not defined');
const result = await migrateShares({
connector: provider,
- contractAddress: toWagmiAddress(request.current.migrator),
- fromVault: toWagmiAddress(request.current.inputToken.value),
- toVault: toWagmiAddress(request.current.outputToken.value),
+ contractAddress: request.current.migrator,
+ fromVault: request.current.inputToken.value,
+ toVault: request.current.outputToken.value,
statusHandler: txStatusSetter
});
if (result.isSuccessful) {
@@ -170,14 +120,11 @@ export function useSolverInternalMigration(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.INTERNAL_MIGRATION,
- quote: latestQuote?.result || toNormalizedBN(0),
- getQuote: getQuote,
- refreshQuote: refreshQuote,
+ quote: latestQuote?.current || toNormalizedBN(0),
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit: onExecuteMigration,
onExecuteWithdraw: async (): Promise => Promise.reject()
- }), [latestQuote?.result, getQuote, refreshQuote, init, onApprove, onExecuteMigration, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [latestQuote, init, onApprove, onExecuteMigration, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverOptimismBooster.ts b/apps/vaults/hooks/useSolverOptimismBooster.ts
index 56a2cf23e..d21dc9f0c 100644
--- a/apps/vaults/hooks/useSolverOptimismBooster.ts
+++ b/apps/vaults/hooks/useSolverOptimismBooster.ts
@@ -1,11 +1,10 @@
import {useCallback, useMemo, useRef} from 'react';
-import useSWRMutation from 'swr/mutation';
import {Solver} from '@vaults/contexts/useSolver';
-import {useVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
import {depositAndStake} from '@vaults/utils/actions';
+import getVaultEstimateOut from '@vaults/utils/getVaultEstimateOut';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, toAddress} from '@yearn-finance/web-lib/utils/address';
import {MAX_UINT_256, STAKING_REWARDS_ZAP_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {approvedERC20Amount, approveERC20} from '@common/utils/actions';
@@ -14,48 +13,12 @@ import {assert} from '@common/utils/assert';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {TVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
-import type {TInitSolverArgs, TSolverContext, TVanillaLikeResult} from '@vaults/types/solvers';
-
-function useQuote(): [TVanillaLikeResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const retrieveExpectedOut = useVaultEstimateOutFetcher();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'stakingRewardsBoost',
- async (_: string, data: {arg: TVaultEstimateOutFetcher}): Promise => retrieveExpectedOut(data.arg)
- );
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false //do nothing, for consistency with other solvers
- ): Promise => {
- shouldPreventErrorToast;
-
- const canExecuteFetch = (
- !request.inputToken || !request.outputToken || !request.inputAmount ||
- !(isZeroAddress(request.inputToken.value) || isZeroAddress(request.outputToken.value) || request.inputAmount === 0n)
- );
-
- if (canExecuteFetch) {
- const result = await trigger([request.inputToken, request.outputToken, request.inputAmount, request.isDepositing]);
- return result || toNormalizedBN(0);
- }
- return toNormalizedBN(0);
- }, [trigger]);
-
- return [
- useMemo((): TVanillaLikeResult => ({
- result: data || toNormalizedBN(0),
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
-}
+import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
export function useSolverOptimismBooster(): TSolverContext {
const {provider} = useWeb3();
- const {safeChainID} = useChainID();
- const [latestQuote, getQuote] = useQuote();
+ const {chainID, safeChainID} = useChainID();
+ const latestQuote = useRef();
const request = useRef();
const existingAllowances = useRef>({});
@@ -66,28 +29,18 @@ export function useSolverOptimismBooster(): TSolverContext {
**********************************************************************************************/
const init = useCallback(async (_request: TInitSolverArgs): Promise => {
request.current = _request;
- return await getQuote(_request);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (request: TInitSolverArgs): Promise => {
- const quoteResult = await getQuote(request, true);
- return quoteResult;
- }, [getQuote]);
+ const estimateOut = await getVaultEstimateOut({
+ inputToken: toAddress(_request.inputToken.value),
+ outputToken: toAddress(_request.outputToken.value),
+ inputDecimals: _request.inputToken.decimals,
+ outputDecimals: _request.outputToken.decimals,
+ inputAmount: _request.inputAmount,
+ isDepositing: _request.isDepositing,
+ chainID: chainID
+ });
+ latestQuote.current = estimateOut;
+ return latestQuote.current;
+ }, [chainID]);
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
@@ -132,8 +85,8 @@ export function useSolverOptimismBooster(): TSolverContext {
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(STAKING_REWARDS_ZAP_ADDRESS),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: STAKING_REWARDS_ZAP_ADDRESS,
amount: amount,
statusHandler: txStatusSetter
});
@@ -151,13 +104,12 @@ export function useSolverOptimismBooster(): TSolverContext {
onSuccess: () => Promise
): Promise => {
assert(request.current, 'Request is not set');
- assert(request.current.outputToken, 'Output token is not set');
assert(request.current.inputAmount, 'Input amount is not set');
const result = await depositAndStake({
connector: provider,
- contractAddress: toWagmiAddress(STAKING_REWARDS_ZAP_ADDRESS),
- vaultAddress: toWagmiAddress(request.current.outputToken.value),
+ contractAddress: STAKING_REWARDS_ZAP_ADDRESS,
+ vaultAddress: request.current.outputToken.value,
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -168,14 +120,11 @@ export function useSolverOptimismBooster(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.OPTIMISM_BOOSTER,
- quote: latestQuote?.result || toNormalizedBN(0),
- getQuote: getQuote,
- refreshQuote: refreshQuote,
+ quote: latestQuote?.current || toNormalizedBN(0),
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit,
onExecuteWithdraw: async (): Promise => undefined
- }), [latestQuote?.result, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [latestQuote, init, onApprove, onExecuteDeposit, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverPartnerContract.ts b/apps/vaults/hooks/useSolverPartnerContract.ts
index 0e903ce7b..f195de160 100644
--- a/apps/vaults/hooks/useSolverPartnerContract.ts
+++ b/apps/vaults/hooks/useSolverPartnerContract.ts
@@ -1,64 +1,28 @@
import {useCallback, useMemo, useRef} from 'react';
-import useSWRMutation from 'swr/mutation';
import {Solver} from '@vaults/contexts/useSolver';
-import {useVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
+import getVaultEstimateOut from '@vaults/utils/getVaultEstimateOut';
import {useSettings} from '@yearn-finance/web-lib/contexts/useSettings';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, toAddress} from '@yearn-finance/web-lib/utils/address';
import {MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {useYearn} from '@common/contexts/useYearn';
import {approvedERC20Amount, approveERC20, depositViaPartner, withdrawShares} from '@common/utils/actions';
import {assert} from '@common/utils/assert';
+import {assertAddress} from '@common/utils/toWagmiProvider';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {TVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
-import type {TInitSolverArgs, TSolverContext, TVanillaLikeResult} from '@vaults/types/solvers';
-
-function useQuote(): [TVanillaLikeResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const retrieveExpectedOut = useVaultEstimateOutFetcher();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'partnerContract',
- async (_: string, data: {arg: TVaultEstimateOutFetcher}): Promise => retrieveExpectedOut(data.arg)
- );
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false //do nothing, for consistency with other solvers
- ): Promise => {
- shouldPreventErrorToast;
-
- const canExecuteFetch = (
- !request.inputToken || !request.outputToken || !request.inputAmount ||
- !(isZeroAddress(request.inputToken.value) || isZeroAddress(request.outputToken.value) || request.inputAmount === 0n)
- );
-
- if (canExecuteFetch) {
- const result = await trigger([request.inputToken, request.outputToken, request.inputAmount, request.isDepositing]);
- return result || toNormalizedBN(0);
- }
- return toNormalizedBN(0);
- }, [trigger]);
-
- return [
- useMemo((): TVanillaLikeResult => ({
- result: data || toNormalizedBN(0),
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
-}
+import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
export function useSolverPartnerContract(): TSolverContext {
const {networks} = useSettings();
const {provider} = useWeb3();
- const {safeChainID} = useChainID();
+ const {chainID, safeChainID} = useChainID();
const {currentPartner} = useYearn();
- const [latestQuote, getQuote] = useQuote();
+ const latestQuote = useRef();
const request = useRef();
const existingAllowances = useRef>({});
@@ -69,28 +33,18 @@ export function useSolverPartnerContract(): TSolverContext {
**********************************************************************************************/
const init = useCallback(async (_request: TInitSolverArgs): Promise => {
request.current = _request;
- return await getQuote(_request);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (request: TInitSolverArgs): Promise => {
- const quoteResult = await getQuote(request, true);
- return quoteResult;
- }, [getQuote]);
+ const estimateOut = await getVaultEstimateOut({
+ inputToken: toAddress(_request.inputToken.value),
+ outputToken: toAddress(_request.outputToken.value),
+ inputDecimals: _request.inputToken.decimals,
+ outputDecimals: _request.outputToken.decimals,
+ inputAmount: _request.inputAmount,
+ isDepositing: _request.isDepositing,
+ chainID: chainID
+ });
+ latestQuote.current = estimateOut;
+ return latestQuote.current;
+ }, [chainID]);
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
@@ -132,13 +86,16 @@ export function useSolverPartnerContract(): TSolverContext {
txStatusSetter: React.Dispatch>,
onSuccess: () => Promise
): Promise => {
+ const partnerContract = networks[safeChainID]?.partnerContractAddress;
+
assert(request.current, 'Request is not set');
assert(request.current?.inputToken, 'Input token is not set');
+ assertAddress(partnerContract, 'partnerContract');
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(networks[safeChainID].partnerContractAddress),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: partnerContract,
amount: amount,
statusHandler: txStatusSetter
});
@@ -155,15 +112,16 @@ export function useSolverPartnerContract(): TSolverContext {
txStatusSetter: React.Dispatch>,
onSuccess: () => Promise
): Promise => {
+ const partnerContract = networks[safeChainID]?.partnerContractAddress;
+
assert(request.current, 'Request is not set');
- assert(request.current.outputToken, 'Output token is not set');
assert(request.current.inputAmount, 'Input amount is not set');
const result = await depositViaPartner({
connector: provider,
- contractAddress: toWagmiAddress(networks[safeChainID].partnerContractAddress),
- vaultAddress: toWagmiAddress(request.current.outputToken.value),
- partnerAddress: currentPartner ? toWagmiAddress(currentPartner) : undefined,
+ contractAddress: partnerContract,
+ vaultAddress: request.current.outputToken.value,
+ partnerAddress: currentPartner ? currentPartner : undefined,
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -186,7 +144,7 @@ export function useSolverPartnerContract(): TSolverContext {
const result = await withdrawShares({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
+ contractAddress: request.current.inputToken.value,
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -197,14 +155,11 @@ export function useSolverPartnerContract(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.PARTNER_CONTRACT,
- quote: latestQuote?.result || toNormalizedBN(0),
- getQuote: getQuote,
- refreshQuote: refreshQuote,
+ quote: latestQuote?.current || toNormalizedBN(0),
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit,
onExecuteWithdraw
- }), [latestQuote?.result, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [latestQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverPortals.ts b/apps/vaults/hooks/useSolverPortals.ts
index 54b8451cc..9f41d3f64 100644
--- a/apps/vaults/hooks/useSolverPortals.ts
+++ b/apps/vaults/hooks/useSolverPortals.ts
@@ -1,19 +1,19 @@
-import {useCallback, useMemo, useRef, useState} from 'react';
+import {useCallback, useMemo, useRef} from 'react';
import axios from 'axios';
-import {useAsync} from '@react-hookz/web';
import {isSolverDisabled, Solver} from '@vaults/contexts/useSolver';
-import usePortalsApi from '@vaults/hooks/usePortalsApi';
+import {getPortalsApproval, getPortalsEstimate, getPortalsTx} from '@vaults/hooks/usePortalsApi';
import {waitForTransaction} from '@wagmi/core';
-import {yToast} from '@yearn-finance/web-lib/components/yToast';
+import {toast} from '@yearn-finance/web-lib/components/yToast';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, isZeroAddress, toAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {Transaction} from '@yearn-finance/web-lib/utils/web3/transaction';
import {useYearn} from '@common/contexts/useYearn';
import {approveERC20, isApprovedERC20} from '@common/utils/actions';
import {assert} from '@common/utils/assert';
+import {assertAddress} from '@common/utils/toWagmiProvider';
import type {Hex} from 'viem';
import type {TDict} from '@yearn-finance/web-lib/types';
@@ -28,77 +28,49 @@ export type TPortalsQuoteResult = {
error?: Error;
};
-function useQuote(): [
- TPortalsQuoteResult,
- (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise
-] {
- const {toast} = yToast();
- const {zapSlippage} = useYearn();
- const [err, set_err] = useState();
- const {getEstimate} = usePortalsApi();
- const {safeChainID} = useChainID();
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false
- ): Promise => {
- const params = {
- sellToken: toAddress(request.inputToken.value),
- sellAmount: toBigInt(request.inputAmount || 0).toString(),
- buyToken: toAddress(request.outputToken.value),
- slippagePercentage: String(zapSlippage / 100)
- };
-
- const canExecuteFetch = (
- !(isZeroAddress(params.sellToken) || isZeroAddress(params.buyToken)) &&
- toBigInt(request.inputAmount) !== 0n
- );
-
- if (!canExecuteFetch) {
- return null;
+async function getQuote(
+ request: TInitSolverArgs,
+ safeChainID: number,
+ zapSlippage: number
+): Promise<{data: TPortalEstimate | undefined, error: Error | undefined}> {
+ const params = {
+ sellToken: toAddress(request.inputToken.value),
+ sellAmount: toBigInt(request.inputAmount).toString(),
+ buyToken: toAddress(request.outputToken.value),
+ slippagePercentage: String(zapSlippage / 100)
+ };
+
+ if (isZeroAddress(params.sellToken)) {
+ return {data: undefined, error: new Error('Invalid sell token')};
+ }
+ if (isZeroAddress(params.buyToken)) {
+ return {data: undefined, error: new Error('Invalid buy token')};
+ }
+ if (toBigInt(params.sellAmount) === 0n) {
+ return {data: undefined, error: new Error('Invalid sell amount')};
+ }
+
+ try {
+ const {data, error} = await getPortalsEstimate({network: safeChainID, params});
+ return {data, error};
+ } catch (error) {
+ console.error(error);
+ let errorContent = 'Zap not possible. Try again later or pick another token.';
+ if (axios.isAxiosError(error)) {
+ const description = error.response?.data?.description;
+ errorContent += `${description ? ` (Reason: [${description}])` : ''}`;
}
-
- try {
- return getEstimate({network: safeChainID, params});
- } catch (error) {
- set_err(error instanceof Error ? error : new Error(`Unknown error: ${error}`));
- console.error(error);
-
- if (!shouldPreventErrorToast) {
- let errorContent = 'Zap not possible. Try again later or pick another token.';
- if (axios.isAxiosError(error)) {
- const description = error.response?.data?.description;
- errorContent += `${description ? ` (Reason: [${description}])` : ''}`;
- }
-
- toast({type: 'error', content: errorContent});
- }
-
- return null;
- }
- }, [getEstimate, safeChainID, toast, zapSlippage]);
-
- const [{result: data, status}] = useAsync(getQuote);
-
- return [
- {
- result: data,
- isLoading: status === 'loading',
- error: err
- },
- getQuote
- ];
+ return {data: undefined, error: new Error(errorContent)};
+ }
}
export function useSolverPortals(): TSolverContext {
const {provider} = useWeb3();
- const [, getQuote] = useQuote();
const request = useRef();
const latestQuote = useRef();
const existingAllowances = useRef>({});
const {address} = useWeb3();
const {zapSlippage} = useYearn();
- const {getTransaction, getApproval} = usePortalsApi();
const {safeChainID} = useChainID();
/* 🔵 - Yearn Finance **************************************************************************
@@ -145,24 +117,20 @@ export function useSolverPortals(): TSolverContext {
** to get the current quote for the provided request.current.
******************************************************************************************/
request.current = _request;
- const quote = await getQuote(_request, !shouldLogError);
- if (!quote) {
+ const {data, error} = await getQuote(_request, safeChainID, zapSlippage);
+ if (!data) {
+ if (error && !shouldLogError) {
+ toast({type: 'error', content: 'Zap not possible. Try again later or pick another token.'});
+ }
return toNormalizedBN(0);
}
- latestQuote.current = quote;
- return toNormalizedBN(quote?.minBuyAmount || 0, request?.current?.outputToken?.decimals || 18);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.current.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
+ latestQuote.current = data;
+ return (
+ toNormalizedBN(
+ data?.minBuyAmount || 0,
+ request?.current?.outputToken?.decimals || 18
+ ));
+ }, [safeChainID, zapSlippage]);
/* 🔵 - Yearn Finance **************************************************************************
** execute will send the post request to execute the order and wait for it to be executed, no
@@ -179,12 +147,12 @@ export function useSolverPortals(): TSolverContext {
assert(latestQuote.current, 'Quote is not set');
try {
- const transaction = await getTransaction({
+ const transaction = await getPortalsTx({
network: safeChainID,
params: {
takerAddress: toAddress(address),
sellToken: toAddress(request.current.inputToken.value),
- sellAmount: toBigInt(request.current.inputAmount || 0).toString(),
+ sellAmount: toBigInt(request.current.inputAmount).toString(),
buyToken: toAddress(request.current.outputToken.value),
slippagePercentage: String(zapSlippage / 100),
validate: true
@@ -200,7 +168,7 @@ export function useSolverPortals(): TSolverContext {
const hash = await signer.sendTransaction({
value: toBigInt(value?.hex ?? 0),
gas: gasLimit?.hex ? toBigInt(gasLimit.hex) : undefined,
- to: toWagmiAddress(to),
+ to: toAddress(to),
data: data as Hex,
...rest
});
@@ -214,7 +182,7 @@ export function useSolverPortals(): TSolverContext {
console.error(_error);
return ({isSuccessful: false});
}
- }, [address, getTransaction, provider, safeChainID, zapSlippage]);
+ }, [address, provider, safeChainID, zapSlippage]);
/* 🔵 - Yearn Finance ******************************************************
** Format the quote to a normalized value, which will be used for subsequent
@@ -227,18 +195,6 @@ export function useSolverPortals(): TSolverContext {
return toNormalizedBN(latestQuote?.current?.minBuyAmount, request?.current?.outputToken?.decimals || 18);
}, [latestQuote, request]);
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (_request: TInitSolverArgs): Promise => {
- if (isSolverDisabled[Solver.PORTALS] || !_request.inputToken.solveVia?.includes(Solver.PORTALS)) {
- return toNormalizedBN(0);
- }
- const quoteResult = await getQuote(_request, true);
- return toNormalizedBN(toBigInt(quoteResult?.minBuyAmount), _request.outputToken.decimals);
- }, [getQuote]);
-
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
** be used to determine if the user should approve the token or not.
@@ -259,12 +215,12 @@ export function useSolverPortals(): TSolverContext {
}
try {
- const approval = await getApproval({
+ const approval = await getPortalsApproval({
network: safeChainID,
params: {
takerAddress: toAddress(request.current.from),
sellToken: toAddress(request.current.inputToken.value),
- sellAmount:toBigInt(request.current.inputAmount || 0).toString(),
+ sellAmount:toBigInt(request.current.inputAmount).toString(),
buyToken: toAddress(request.current.outputToken.value)
}
});
@@ -280,7 +236,7 @@ export function useSolverPortals(): TSolverContext {
return ({raw: 0n, normalized: 0});
}
- }, [getApproval, safeChainID]);
+ }, [safeChainID]);
/* 🔵 - Yearn Finance ******************************************************
** Trigger an signature to approve the token to be used by the Portals
@@ -300,12 +256,12 @@ export function useSolverPortals(): TSolverContext {
assert(request.current.inputAmount, 'Input amount is not set');
try {
- const approval = await getApproval({
+ const approval = await getPortalsApproval({
network: safeChainID,
params: {
takerAddress: toAddress(request.current.from),
sellToken: toAddress(request.current.inputToken.value),
- sellAmount:toBigInt(request.current.inputAmount || 0).toString(),
+ sellAmount:toBigInt(request.current.inputAmount).toString(),
buyToken: toAddress(request.current.outputToken.value)
}
});
@@ -322,10 +278,11 @@ export function useSolverPortals(): TSolverContext {
);
if (!isApproved) {
+ assertAddress(approval.context.spender, 'spender');
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(approval.context.spender),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: approval.context.spender,
amount: amount,
statusHandler: txStatusSetter
});
@@ -340,7 +297,7 @@ export function useSolverPortals(): TSolverContext {
console.error(error);
return;
}
- }, [getApproval, provider, safeChainID]);
+ }, [provider, safeChainID]);
/* 🔵 - Yearn Finance ******************************************************
** This execute function is not an actual deposit/withdraw, but a swap using
@@ -362,14 +319,11 @@ export function useSolverPortals(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.PORTALS,
quote: expectedOut,
- getQuote,
- refreshQuote,
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit: onExecute,
onExecuteWithdraw: onExecute
- }), [expectedOut, getQuote, refreshQuote, init, onApprove, onExecute, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [expectedOut, init, onApprove, onExecute, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverVanilla.ts b/apps/vaults/hooks/useSolverVanilla.ts
index 862eda717..0feda3311 100644
--- a/apps/vaults/hooks/useSolverVanilla.ts
+++ b/apps/vaults/hooks/useSolverVanilla.ts
@@ -1,10 +1,9 @@
import {useCallback, useMemo, useRef} from 'react';
-import useSWRMutation from 'swr/mutation';
import {Solver} from '@vaults/contexts/useSolver';
-import {useVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
+import getVaultEstimateOut from '@vaults/utils/getVaultEstimateOut';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, toAddress} from '@yearn-finance/web-lib/utils/address';
import {MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {approvedERC20Amount, approveERC20, deposit, withdrawShares} from '@common/utils/actions';
@@ -13,48 +12,12 @@ import {assert} from '@common/utils/assert';
import type {TDict} from '@yearn-finance/web-lib/types';
import type {TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {TVaultEstimateOutFetcher} from '@vaults/hooks/useVaultEstimateOutFetcher';
-import type {TInitSolverArgs, TSolverContext, TVanillaLikeResult} from '@vaults/types/solvers';
-
-function useQuote(): [TVanillaLikeResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const retrieveExpectedOut = useVaultEstimateOutFetcher();
- const {data, error, trigger, isMutating} = useSWRMutation(
- 'Vanilla',
- async (_: string, data: {arg: TVaultEstimateOutFetcher}): Promise => retrieveExpectedOut(data.arg)
- );
-
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false //do nothing, for consistency with other solvers
- ): Promise => {
- shouldPreventErrorToast;
-
- const canExecuteFetch = (
- !request.inputToken || !request.outputToken || !request.inputAmount ||
- !(isZeroAddress(request.inputToken.value) || isZeroAddress(request.outputToken.value) || request.inputAmount === 0n)
- );
-
- if (canExecuteFetch) {
- const result = await trigger([request.inputToken, request.outputToken, request.inputAmount, request.isDepositing]);
- return result || toNormalizedBN(0);
- }
- return toNormalizedBN(0);
- }, [trigger]);
-
- return [
- useMemo((): TVanillaLikeResult => ({
- result: data || toNormalizedBN(0),
- isLoading: isMutating,
- error
- }), [data, error, isMutating]),
- getQuote
- ];
-}
+import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
export function useSolverVanilla(): TSolverContext {
const {provider} = useWeb3();
- const {safeChainID} = useChainID();
- const [latestQuote, getQuote] = useQuote();
+ const {chainID, safeChainID} = useChainID();
+ const latestQuote = useRef();
const request = useRef();
const existingAllowances = useRef>({});
@@ -65,28 +28,18 @@ export function useSolverVanilla(): TSolverContext {
**********************************************************************************************/
const init = useCallback(async (_request: TInitSolverArgs): Promise => {
request.current = _request;
- return await getQuote(_request);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (request: TInitSolverArgs): Promise => {
- const quoteResult = await getQuote(request, true);
- return quoteResult;
- }, [getQuote]);
+ const estimateOut = await getVaultEstimateOut({
+ inputToken: toAddress(_request.inputToken.value),
+ outputToken: toAddress(_request.outputToken.value),
+ inputDecimals: _request.inputToken.decimals,
+ outputDecimals: _request.outputToken.decimals,
+ inputAmount: _request.inputAmount,
+ isDepositing: _request.isDepositing,
+ chainID: chainID
+ });
+ latestQuote.current = estimateOut;
+ return latestQuote.current;
+ }, [chainID]);
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
@@ -133,8 +86,8 @@ export function useSolverVanilla(): TSolverContext {
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(request.current.outputToken.value),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: request.current.outputToken.value,
amount: amount,
statusHandler: txStatusSetter
});
@@ -157,7 +110,7 @@ export function useSolverVanilla(): TSolverContext {
const result = await deposit({
connector: provider,
- contractAddress: toWagmiAddress(request.current.outputToken.value),
+ contractAddress: request.current.outputToken.value,
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -180,7 +133,7 @@ export function useSolverVanilla(): TSolverContext {
const result = await withdrawShares({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
+ contractAddress: request.current.inputToken.value,
amount: request.current.inputAmount,
statusHandler: txStatusSetter
});
@@ -192,14 +145,11 @@ export function useSolverVanilla(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.VANILLA,
- quote: latestQuote?.result || toNormalizedBN(0),
- getQuote: getQuote,
- refreshQuote: refreshQuote,
+ quote: latestQuote?.current || toNormalizedBN(0),
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
onExecuteDeposit,
onExecuteWithdraw
- }), [latestQuote?.result, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance, onRetrieveExpectedOut]);
+ }), [latestQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSolverWido.ts b/apps/vaults/hooks/useSolverWido.ts
index faae7303c..ebbfc882c 100644
--- a/apps/vaults/hooks/useSolverWido.ts
+++ b/apps/vaults/hooks/useSolverWido.ts
@@ -1,94 +1,71 @@
-import {useCallback, useMemo, useRef, useState} from 'react';
+import {useCallback, useMemo, useRef} from 'react';
import {getTokenAllowance as wiGetTokenAllowance, getWidoSpender, quote as wiQuote} from 'wido';
-import {useAsync} from '@react-hookz/web';
import {isSolverDisabled, Solver} from '@vaults/contexts/useSolver';
import {waitForTransaction} from '@wagmi/core';
-import {yToast} from '@yearn-finance/web-lib/components/yToast';
+import {toast} from '@yearn-finance/web-lib/components/yToast';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
-import {allowanceKey, isZeroAddress, toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
+import {allowanceKey, isZeroAddress, toAddress} from '@yearn-finance/web-lib/utils/address';
import {MAX_UINT_256} from '@yearn-finance/web-lib/utils/constants';
import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {Transaction} from '@yearn-finance/web-lib/utils/web3/transaction';
import {useYearn} from '@common/contexts/useYearn';
import {approveERC20, isApprovedERC20} from '@common/utils/actions';
import {assert} from '@common/utils/assert';
+import {assertAddress} from '@common/utils/toWagmiProvider';
-import type {AxiosError} from 'axios';
import type {Hex} from 'viem';
import type {ChainId, QuoteRequest, QuoteResult} from 'wido';
-import type {TDict} from '@yearn-finance/web-lib/types';
+import type {TAddress, TDict} from '@yearn-finance/web-lib/types';
import type {TTxResponse, TTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TNormalizedBN} from '@common/types/types';
-import type {ApiError} from '@gnosis.pm/gp-v2-contracts';
import type {TInitSolverArgs, TSolverContext} from '@vaults/types/solvers';
-import type {TWidoResult} from '@vaults/types/solvers.wido';
-function useQuote(): [TWidoResult, (request: TInitSolverArgs, shouldPreventErrorToast?: boolean) => Promise] {
- const {toast} = yToast();
- const {zapSlippage, currentPartner} = useYearn();
- const [err, set_err] = useState();
- const {safeChainID} = useChainID();
+async function getQuote(
+ request: TInitSolverArgs,
+ currentPartner: TAddress,
+ safeChainID: number,
+ zapSlippage: number
+): Promise<{data: QuoteResult | undefined, error: Error | undefined}> {
+ const params: QuoteRequest = ({
+ fromChainId: safeChainID as ChainId, // Chain Id of from token
+ fromToken: toAddress(request.inputToken.value), // token to spend
+ toChainId: safeChainID as ChainId, // Chain Id of to token
+ toToken: toAddress(request.outputToken.value), // token to receive
+ amount: toBigInt(request?.inputAmount).toString(), // Token amount of from token
+ slippagePercentage: zapSlippage / 100, // Acceptable max slippage for the swap
+ user: request.from, // receiver
+ partner: currentPartner
+ });
- const getQuote = useCallback(async (
- request: TInitSolverArgs,
- shouldPreventErrorToast = false
- ): Promise => {
- const quoteRequest: QuoteRequest = ({
- fromChainId: safeChainID as ChainId, // Chain Id of from token
- fromToken: toAddress(request.inputToken.value), // token to spend
- toChainId: safeChainID as ChainId, // Chain Id of to token
- toToken: toAddress(request.outputToken.value), // token to receive
- amount: toBigInt(request?.inputAmount || 0).toString(), // Token amount of from token
- slippagePercentage: zapSlippage / 100, // Acceptable max slippage for the swap
- user: request.from, // receiver
- partner: currentPartner
- });
-
- const canExecuteFetch = (
- !(isZeroAddress(quoteRequest.user) || isZeroAddress(quoteRequest.fromToken) || isZeroAddress(quoteRequest.toToken))
- && toBigInt(request?.inputAmount) !== 0n
- );
-
- if (canExecuteFetch) {
- try {
- const result = await wiQuote(quoteRequest);
-
- return result;
- } catch (error) {
- const _error = error as AxiosError;
- set_err(error as Error);
- console.error(error);
- if (shouldPreventErrorToast) {
- return undefined;
- }
- const message = `Zap not possible. Try again later or pick another token. ${_error?.response?.data?.description ? `(Reason: [${_error?.response?.data?.description}])` : ''}`;
- toast({type: 'error', content: message});
- return undefined;
- }
- }
- return undefined;
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- const [{result: data, status}, actions] = useAsync(getQuote, undefined);
+ if (isZeroAddress(params.user)) {
+ return {data: undefined, error: new Error('Invalid user')};
+ }
+ if (isZeroAddress(params.fromToken)) {
+ return {data: undefined, error: new Error('Invalid from token')};
+ }
+ if (isZeroAddress(params.toToken)) {
+ return {data: undefined, error: new Error('Invalid to token')};
+ }
+ if (toBigInt(params.amount) === 0n) {
+ return {data: undefined, error: new Error('Invalid amount')};
+ }
- return [
- {
- result: data,
- isLoading: status === 'loading',
- error: err
- },
- actions.execute
- ];
+ try {
+ const result = await wiQuote(params);
+ return {data: result, error: undefined};
+ } catch (error) {
+ return {data: undefined, error: error as Error};
+ }
}
export function useSolverWido(): TSolverContext {
const {provider} = useWeb3();
- const [, getQuote] = useQuote();
const request = useRef();
const latestQuote = useRef();
const existingAllowances = useRef>({});
const {safeChainID} = useChainID();
+ const {zapSlippage, currentPartner} = useYearn();
/* 🔵 - Yearn Finance **************************************************************************
** init will be called when the Wido solver should be used to perform the desired swap.
@@ -127,24 +104,16 @@ export function useSolverWido(): TSolverContext {
** to get the current quote for the provided request.current.
******************************************************************************************/
request.current = _request;
- const quote = await getQuote(_request, !shouldLogError);
- if (!quote) {
+ const {data, error} = await getQuote(_request, currentPartner, safeChainID, zapSlippage);
+ if (!data) {
+ if (error && !shouldLogError) {
+ toast({type: 'error', content: 'Zap not possible. Try again later or pick another token.'});
+ }
return toNormalizedBN(0);
}
- latestQuote.current = quote;
- return toNormalizedBN(quote?.minToTokenAmount || 0, request?.current?.outputToken?.decimals || 18);
- }, [getQuote]);
-
- /* 🔵 - Yearn Finance **************************************************************************
- ** refreshQuote can be called by the user to refresh the quote. The same parameters are used
- ** as in the initial request and it will fails if request is not set.
- ** init should be called first to initialize the request.current.
- **********************************************************************************************/
- const refreshQuote = useCallback(async (): Promise => {
- if (request.current) {
- getQuote(request.current);
- }
- }, [request, getQuote]);
+ latestQuote.current = data;
+ return toNormalizedBN(data?.minToTokenAmount || 0, request?.current?.outputToken?.decimals || 18);
+ }, [currentPartner, safeChainID, zapSlippage]);
/* 🔵 - Yearn Finance **************************************************************************
** execute will send the post request to execute the order and wait for it to be executed, no
@@ -165,7 +134,7 @@ export function useSolverWido(): TSolverContext {
const {data, to, value} = latestQuote.current;
const hash = await signer.sendTransaction({
data: data as Hex,
- to: toWagmiAddress(to),
+ to: toAddress(to),
value: toBigInt(value)
});
const receipt = await waitForTransaction({chainId: signer.chain.id, hash});
@@ -191,18 +160,6 @@ export function useSolverWido(): TSolverContext {
return toNormalizedBN(latestQuote?.current?.minToTokenAmount, request?.current?.outputToken?.decimals || 18);
}, [latestQuote, request]);
- /* 🔵 - Yearn Finance ******************************************************
- ** Retrieve the current outValue from the quote, which will be used to
- ** display the current value to the user.
- **************************************************************************/
- const onRetrieveExpectedOut = useCallback(async (_request: TInitSolverArgs): Promise => {
- if (isSolverDisabled[Solver.WIDO] || !_request.inputToken.solveVia?.includes(Solver.WIDO)) {
- return toNormalizedBN(0);
- }
- const quoteResult = await getQuote(_request, true);
- return toNormalizedBN(toBigInt(quoteResult?.minToTokenAmount), _request.outputToken.decimals);
- }, [getQuote]);
-
/* 🔵 - Yearn Finance ******************************************************
** Retrieve the allowance for the token to be used by the solver. This will
** be used to determine if the user should approve the token or not.
@@ -211,7 +168,6 @@ export function useSolverWido(): TSolverContext {
if (!latestQuote?.current || !request?.current || isSolverDisabled[Solver.WIDO]) {
return toNormalizedBN(0);
}
-
const key = allowanceKey(
safeChainID,
toAddress(request.current.inputToken.value),
@@ -251,6 +207,7 @@ export function useSolverWido(): TSolverContext {
assert(request.current.inputAmount, 'Input amount is not set');
const widoSpender = await getWidoSpender({
+ toChainId: safeChainID as ChainId,
chainId: safeChainID as ChainId,
fromToken: toAddress(request.current.inputToken.value),
toToken: toAddress(request.current.outputToken.value)
@@ -262,10 +219,11 @@ export function useSolverWido(): TSolverContext {
amount
);
if (!isApproved) {
+ assertAddress(widoSpender, 'spender');
const result = await approveERC20({
connector: provider,
- contractAddress: toWagmiAddress(request.current.inputToken.value),
- spenderAddress: toWagmiAddress(widoSpender),
+ contractAddress: request.current.inputToken.value,
+ spenderAddress: widoSpender,
amount: amount,
statusHandler: txStatusSetter
});
@@ -283,24 +241,7 @@ export function useSolverWido(): TSolverContext {
** Wido solver. The deposit will be executed by the Wido solver by
** simply swapping the input token for the output token.
**************************************************************************/
- const onExecuteDeposit = useCallback(async (
- txStatusSetter: React.Dispatch>,
- onSuccess: () => Promise
- ): Promise => {
- assert(provider, 'Provider is not set');
-
- new Transaction(provider, execute, txStatusSetter)
- .populate()
- .onSuccess(onSuccess)
- .perform();
- }, [execute, provider]);
-
- /* 🔵 - Yearn Finance ******************************************************
- ** This execute function is not an actual withdraw, but a swap using the
- ** Wido solver. The withdraw will be executed by the Wido solver by
- ** simply swapping the input token for the output token.
- **************************************************************************/
- const onExecuteWithdraw = useCallback(async (
+ const onExecute = useCallback(async (
txStatusSetter: React.Dispatch>,
onSuccess: () => Promise
): Promise => {
@@ -315,13 +256,10 @@ export function useSolverWido(): TSolverContext {
return useMemo((): TSolverContext => ({
type: Solver.WIDO,
quote: expectedOut,
- getQuote: getQuote,
- refreshQuote,
init,
- onRetrieveExpectedOut,
onRetrieveAllowance,
onApprove,
- onExecuteDeposit,
- onExecuteWithdraw
- }), [expectedOut, getQuote, refreshQuote, init, onApprove, onExecuteDeposit, onExecuteWithdraw, onRetrieveAllowance, onRetrieveExpectedOut]);
+ onExecuteDeposit: onExecute,
+ onExecuteWithdraw: onExecute
+ }), [expectedOut, init, onApprove, onExecute, onRetrieveAllowance]);
}
diff --git a/apps/vaults/hooks/useSortVaults.ts b/apps/vaults/hooks/useSortVaults.ts
index 2e3cfd55e..2e12dcc6c 100644
--- a/apps/vaults/hooks/useSortVaults.ts
+++ b/apps/vaults/hooks/useSortVaults.ts
@@ -2,7 +2,7 @@ import {useCallback, useMemo} from 'react';
import {useStakingRewards} from '@vaults/contexts/useStakingRewards';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, WETH_TOKEN_ADDRESS, WFTM_TOKEN_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
-import {toNormalizedValue} from '@yearn-finance/web-lib/utils/format.bigNumber';
+import {toBigInt, toNormalizedValue} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {useWallet} from '@common/contexts/useWallet';
import {getVaultName} from '@common/utils';
import {numberSort, stringSort} from '@common/utils/sort';
@@ -38,8 +38,8 @@ function useSortVaults(
vaultList.sort((a, b): number => {
const aDepositedBalance = balances[toAddress(a.address)]?.normalized || 0;
const bDepositedBalance = balances[toAddress(b.address)]?.normalized || 0;
- const aStakedBalance = toNormalizedValue(positionsMap[toAddress(stakingRewardsByVault[a.address])]?.stake || 0, a.decimals);
- const bStakedBalance = toNormalizedValue(positionsMap[toAddress(stakingRewardsByVault[b.address])]?.stake || 0, b.decimals);
+ const aStakedBalance = toNormalizedValue(toBigInt(positionsMap[toAddress(stakingRewardsByVault[a.address])]?.stake), a.decimals);
+ const bStakedBalance = toNormalizedValue(toBigInt(positionsMap[toAddress(stakingRewardsByVault[b.address])]?.stake), b.decimals);
if (sortDirection === 'asc') {
return (aDepositedBalance + aStakedBalance) - (bDepositedBalance + bStakedBalance);
}
diff --git a/apps/vaults/hooks/useVaultEstimateOutFetcher.ts b/apps/vaults/hooks/useVaultEstimateOutFetcher.ts
deleted file mode 100644
index f196ff1d9..000000000
--- a/apps/vaults/hooks/useVaultEstimateOutFetcher.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import {useCallback} from 'react';
-import {readContract} from '@wagmi/core';
-import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
-import VAULT_ABI from '@yearn-finance/web-lib/utils/abi/vault.abi';
-import {isZeroAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
-import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
-
-import type {TDropdownOption, TNormalizedBN} from '@common/types/types';
-
-export type TVaultEstimateOutFetcher = [
- inputToken: TDropdownOption,
- outputToken: TDropdownOption,
- inputAmount: bigint,
- isDepositing: boolean
-]
-
-export function useVaultEstimateOutFetcher(): (args: TVaultEstimateOutFetcher) => Promise {
- const {chainID} = useWeb3();
-
- const retrieveExpectedOut = useCallback(async (args: TVaultEstimateOutFetcher): Promise => {
- const [inputToken, outputToken, inputAmount, isDepositing] = args;
- if (isZeroAddress(inputToken?.value) || isZeroAddress(outputToken?.value) || inputAmount === 0n) {
- return (toNormalizedBN(0));
- }
-
- try {
- const decimals = toBigInt(outputToken?.decimals || 18);
- const powerDecimals = toBigInt(10) ** decimals;
- const contractAddress = toWagmiAddress(isDepositing ? outputToken.value : inputToken.value);
- const pps = await readContract({
- abi: VAULT_ABI,
- address: contractAddress,
- functionName: 'pricePerShare',
- chainId: chainID
- });
-
- if (isDepositing) {
- const expectedOutFetched = inputAmount * powerDecimals / pps;
- return toNormalizedBN(expectedOutFetched, outputToken?.decimals || 18);
- }
- const expectedOutFetched = inputAmount * pps / powerDecimals;
- return toNormalizedBN(expectedOutFetched, outputToken?.decimals || 18);
- } catch (error) {
- console.error(error);
- return (toNormalizedBN(0));
- }
- }, [chainID]);
-
- return retrieveExpectedOut;
-}
diff --git a/apps/vaults/types/solvers.cowswap.ts b/apps/vaults/types/solvers.cowswap.ts
deleted file mode 100644
index 86df99e9d..000000000
--- a/apps/vaults/types/solvers.cowswap.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import type {Order} from '@gnosis.pm/gp-v2-contracts';
-
-/* 🔵 - Yearn Finance ******************************************************
-** Theses types are used to define the request and response of the Cowswap
-** Quote API.
-** TCowRequest is the requirement to execute a quote request.
-** TCowAPIResult is the response from the API.
-** TCowResult is what we will send as response for the use of the
-** useCowQuote hook.
-***************************************************************************/
-export type TCowAPIResult = {
- quote: Order;
- from: string;
- expiration: string;
- id: number;
-}
-export type TCowResult = {
- result: TCowAPIResult | undefined,
- isLoading: boolean,
- error: Error | undefined
-}
diff --git a/apps/vaults/types/solvers.ts b/apps/vaults/types/solvers.ts
index 3ec9ceca9..807bb38d8 100644
--- a/apps/vaults/types/solvers.ts
+++ b/apps/vaults/types/solvers.ts
@@ -14,7 +14,6 @@ export type TWithSolver = {
expectedOut: TNormalizedBN;
hash?: string,
isLoadingExpectedOut: boolean;
- onRetrieveExpectedOut: (request: TInitSolverArgs) => Promise;
onRetrieveAllowance: (shouldForceRefetch?: boolean) => Promise;
onApprove: (
amount: bigint,
@@ -43,10 +42,7 @@ export type TInitSolverArgs = {
export type TSolverContext = {
type: Solver;
quote: TNormalizedBN;
- getQuote: CallableFunction;
- refreshQuote: CallableFunction;
init: (args: TInitSolverArgs, shouldLogError?: boolean) => Promise;
- onRetrieveExpectedOut: (request: TInitSolverArgs) => Promise