From d4f70d1824c917e2a5519b64cdba688b6556a47c Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 25 Sep 2024 18:35:38 +0200 Subject: [PATCH] fix: add nonces query --- app/(app)/debug/page.tsx | 3 + app/(app)/debug/permit2-allowance/page.tsx | 63 +++++++ lib/config/config.types.ts | 2 + lib/config/networks/sepolia.ts | 1 + .../handlers/AddLiquidity.handler.ts | 10 +- ...edAddLiquidity.handler.integration.spec.ts | 5 +- .../UnbalancedAddLiquidity.handler.ts | 21 ++- .../UnbalancedAddLiquidity.handlerLOCURA.ts | 157 ++++++++++++++++++ .../add-liquidity/useAddLiquiditySteps.tsx | 3 +- .../relayer/signRelayerApproval.hooks.tsx | 4 +- lib/modules/relayer/signRelayerApproval.ts | 5 +- ...ovals.tsx => signPermit2TokenTransfer.tsx} | 14 +- .../approvals/permit2/usePermit2Nonces.tsx | 52 ++++++ ...pproval.tsx => useSignPermit2Transfer.tsx} | 15 +- .../transaction-steps/useSignPermit2Step.tsx | 32 +++- lib/modules/web3/useSdkViemClient.tsx | 11 +- 16 files changed, 355 insertions(+), 43 deletions(-) create mode 100644 app/(app)/debug/permit2-allowance/page.tsx create mode 100644 lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handlerLOCURA.ts rename lib/modules/tokens/approvals/permit2/{signPermit2TokenApprovals.tsx => signPermit2TokenTransfer.tsx} (72%) create mode 100644 lib/modules/tokens/approvals/permit2/usePermit2Nonces.tsx rename lib/modules/tokens/approvals/permit2/{useSignPermit2Approval.tsx => useSignPermit2Transfer.tsx} (88%) diff --git a/app/(app)/debug/page.tsx b/app/(app)/debug/page.tsx index 2a9d617d1..75f8d7ba0 100644 --- a/app/(app)/debug/page.tsx +++ b/app/(app)/debug/page.tsx @@ -114,6 +114,9 @@ export default function Debug() { Remove allowance + + Permit2 allowance + ) diff --git a/app/(app)/debug/permit2-allowance/page.tsx b/app/(app)/debug/permit2-allowance/page.tsx new file mode 100644 index 000000000..7513f5d72 --- /dev/null +++ b/app/(app)/debug/permit2-allowance/page.tsx @@ -0,0 +1,63 @@ +'use client' + +import { useUserAccount } from '@/lib/modules/web3/UserAccountProvider' +import { Center, Input, Text, VStack } from '@chakra-ui/react' +import { useState } from 'react' +import { Address } from 'viem' +import { sepolia } from 'viem/chains' +import { useReadContract } from 'wagmi' +import { fNum } from '@/lib/shared/utils/numbers' +import { getGqlChain, getNetworkConfig } from '@/lib/config/app.config' +import { permit2Abi } from '@balancer/sdk' + +export default function Page() { + const [tokenAddress, setTokenAddress] = useState
('' as Address) + + const { chain, userAddress } = useUserAccount() + + const chainId = chain?.id || sepolia.id + + const { data } = usePermit2Allowance({ chainId, tokenAddress, owner: userAddress }) + + return ( +
+ + + Enter address of token to check permit2 allowance in the current chain:{' '} + {chain ? chain.name : 'None'} + + setTokenAddress(e.target.value as Address)} /> + + {data && ( +
+
Amount: {fNum('integer', data[0])}
+
Expires: {data[1]}
+
Nonce: {data[2]}
+
+ )} +
+
+ ) +} + +type Params = { + chainId: number + tokenAddress: Address + owner: Address +} +function usePermit2Allowance({ chainId, tokenAddress, owner }: Params) { + const permit2Address = '0x000000000022D473030F116dDEE9F6B43aC78BA3' + const balancerRouter = getNetworkConfig(getGqlChain(chainId)).contracts.balancer.router! + const spender = balancerRouter + + return useReadContract({ + chainId, + address: permit2Address, + abi: permit2Abi, + functionName: 'allowance', + args: [owner, tokenAddress, spender], + query: { + enabled: !!tokenAddress && !!owner, + }, + }) +} diff --git a/lib/config/config.types.ts b/lib/config/config.types.ts index 71e273c2d..d8a4d5296 100644 --- a/lib/config/config.types.ts +++ b/lib/config/config.types.ts @@ -37,6 +37,8 @@ export interface ContractsConfig { vaultV2: Address // TODO: make it required when v3 is deployed in all networks vaultV3?: Address + // TODO: make it required when v3 is deployed in all networks + router?: Address relayerV6: Address minter: Address } diff --git a/lib/config/networks/sepolia.ts b/lib/config/networks/sepolia.ts index 97805ca46..c8ace9f12 100644 --- a/lib/config/networks/sepolia.ts +++ b/lib/config/networks/sepolia.ts @@ -32,6 +32,7 @@ const networkConfig: NetworkConfig = { balancer: { vaultV2: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', vaultV3: '0x0EF1c156a7986F394d90eD1bEeA6483Cc435F542', + router: '0xB12FcB422aAe6720f882E22C340964a7723f2387', relayerV6: '0x7852fB9d0895e6e8b3EedA553c03F6e2F9124dF9', minter: '0x1783Cd84b3d01854A96B4eD5843753C2CcbD574A', }, diff --git a/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts b/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts index ad595fb91..6365c4f92 100644 --- a/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts +++ b/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts @@ -1,9 +1,9 @@ import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types' import { TransactionConfig } from '@/lib/modules/web3/contracts/contract.types' -import { SdkClient } from '@/lib/modules/web3/useSdkViemClient' -import { AddLiquidityQueryOutput, Permit2 } from '@balancer/sdk' +import { AddLiquidityQueryOutput, Permit2, PublicWalletClient } from '@balancer/sdk' import { Address } from 'viem' import { BuildAddLiquidityInput, QueryAddLiquidityOutput } from '../add-liquidity.types' +import { NoncesByTokenAddress } from '@/lib/modules/tokens/approvals/permit2/usePermit2Nonces' export interface Permit2AddLiquidityInput { account: Address @@ -40,5 +40,9 @@ export interface AddLiquidityHandler { /* Sign permit2 for adding liquidity (for now only used by v3 pools) TODO: generalize for other handlers using permit2 */ - signPermit2?(input: Permit2AddLiquidityInput, walletClient: SdkClient): Promise + signPermit2?( + input: Permit2AddLiquidityInput, + walletClient: PublicWalletClient, + nonces?: NoncesByTokenAddress + ): Promise } diff --git a/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.integration.spec.ts b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.integration.spec.ts index 4cc1ad0b4..13ea19e42 100644 --- a/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.integration.spec.ts +++ b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.integration.spec.ts @@ -7,6 +7,8 @@ import { UnbalancedAddLiquidityHandler } from './UnbalancedAddLiquidity.handler' import { selectAddLiquidityHandler } from './selectAddLiquidityHandler' import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types' import { Pool } from '../../../PoolProvider' +import { getNetworkConfig } from '@/lib/config/app.config' +import { GqlChain } from '@/lib/shared/services/api/generated/graphql' function selectUnbalancedHandler() { return selectAddLiquidityHandler(aWjAuraWethPoolElementMock()) as UnbalancedAddLiquidityHandler @@ -110,7 +112,8 @@ describe.skip('When adding unbalanced liquidity for a V3 pool', async () => { queryOutput, }) - const sepoliaRouter = '0xB12FcB422aAe6720f882E22C340964a7723f2387' + const sepoliaRouter = getNetworkConfig(GqlChain.Sepolia).contracts.balancer.router + expect(result.to).toBe(sepoliaRouter) expect(result.data).toBeDefined() }) diff --git a/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.ts b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.ts index 29fffa938..74376789b 100644 --- a/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.ts +++ b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handler.ts @@ -2,7 +2,6 @@ import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types' import { TransactionConfig } from '@/lib/modules/web3/contracts/contract.types' import { getRpcUrl } from '@/lib/modules/web3/transports' -import { SdkClient } from '@/lib/modules/web3/useSdkViemClient' import { AddLiquidity, AddLiquidityBaseBuildCallInput, @@ -13,6 +12,7 @@ import { Permit2Helper, PriceImpact, PriceImpactAmount, + PublicWalletClient, Slippage, } from '@balancer/sdk' import { Pool } from '../../../PoolProvider' @@ -23,6 +23,7 @@ import { } from '../../LiquidityActionHelpers' import { SdkBuildAddLiquidityInput, SdkQueryAddLiquidityOutput } from '../add-liquidity.types' import { AddLiquidityHandler, Permit2AddLiquidityInput } from './AddLiquidity.handler' +import { NoncesByTokenAddress } from '@/lib/modules/tokens/approvals/permit2/usePermit2Nonces' /** * UnbalancedAddLiquidityHandler is a handler that implements the @@ -102,18 +103,19 @@ export class UnbalancedAddLiquidityHandler implements AddLiquidityHandler { public async signPermit2( input: Permit2AddLiquidityInput, - sdkClient: SdkClient + sdkClient: PublicWalletClient, + nonces: NoncesByTokenAddress ): Promise { + const baseInput = this.constructBaseBuildCallInput({ + slippagePercent: input.slippagePercent, + sdkQueryOutput: input.sdkQueryOutput as AddLiquidityBaseQueryOutput, + }) const signature = await Permit2Helper.signAddLiquidityApproval({ - ...this.constructBaseBuildCallInput({ - slippagePercent: input.slippagePercent, - sdkQueryOutput: input.sdkQueryOutput as AddLiquidityBaseQueryOutput, - }), + ...baseInput, client: sdkClient, owner: input.account, - // nonces: input.sdkQueryOutput.amountsIn.map(a => (a.amount === 0n ? 0 : 2)), + nonces: baseInput.amountsIn.map(a => nonces[a.token.address]), }) - return signature } @@ -145,6 +147,9 @@ export class UnbalancedAddLiquidityHandler implements AddLiquidityHandler { slippage: Slippage.fromPercentage(`${Number(slippagePercent)}`), wethIsEth: this.helpers.includesNativeAsset(sdkQueryOutput.amountsIn), } + // baseBuildCallParams.amountsIn = baseBuildCallParams.amountsIn.filter( + // amountIn => amountIn.amount > 0n + // ) return baseBuildCallParams } } diff --git a/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handlerLOCURA.ts b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handlerLOCURA.ts new file mode 100644 index 000000000..adb6a2371 --- /dev/null +++ b/lib/modules/pool/actions/add-liquidity/handlers/UnbalancedAddLiquidity.handlerLOCURA.ts @@ -0,0 +1,157 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types' +import { TransactionConfig } from '@/lib/modules/web3/contracts/contract.types' +import { getRpcUrl } from '@/lib/modules/web3/transports' +import { + AddLiquidity, + AddLiquidityBaseBuildCallInput, + AddLiquidityBaseQueryOutput, + AddLiquidityKind, + AddLiquidityUnbalancedInput, + Permit2, + Permit2Helper, + PriceImpact, + PriceImpactAmount, + PublicWalletClient, + Slippage, +} from '@balancer/sdk' +import { Pool } from '../../../PoolProvider' +import { + LiquidityActionHelpers, + areEmptyAmounts, + formatBuildCallParams, +} from '../../LiquidityActionHelpers' +import { SdkBuildAddLiquidityInput, SdkQueryAddLiquidityOutput } from '../add-liquidity.types' +import { AddLiquidityHandler, Permit2AddLiquidityInput } from './AddLiquidity.handler' +import { NoncesByTokenAddress } from '@/lib/modules/tokens/approvals/permit2/usePermit2Nonces' + +/** + * UnbalancedAddLiquidityHandler is a handler that implements the + * AddLiquidityHandler interface for unbalanced adds, e.g. where the user + * specifies the token amounts in. It uses the Balancer SDK to implement it's + * methods. It also handles the case where one of the input tokens is the native + * asset instead of the wrapped native asset. + */ +export class UnbalancedAddLiquidityHandler implements AddLiquidityHandler { + helpers: LiquidityActionHelpers + + constructor(pool: Pool) { + this.helpers = new LiquidityActionHelpers(pool) + } + + public async simulate( + humanAmountsIn: HumanTokenAmountWithAddress[] + ): Promise { + const addLiquidity = new AddLiquidity() + const addLiquidityInput = this.constructSdkInput(humanAmountsIn) + + const sdkQueryOutput = await addLiquidity.query(addLiquidityInput, this.helpers.poolState) + + return { bptOut: sdkQueryOutput.bptOut, sdkQueryOutput } + } + + public async getPriceImpact(humanAmountsIn: HumanTokenAmountWithAddress[]): Promise { + if (areEmptyAmounts(humanAmountsIn)) { + // Avoid price impact calculation when there are no amounts in + return 0 + } + + const addLiquidityInput = this.constructSdkInput(humanAmountsIn) + + const priceImpactABA: PriceImpactAmount = await PriceImpact.addLiquidityUnbalanced( + addLiquidityInput, + this.helpers.poolState + ) + + return priceImpactABA.decimal + } + + public async buildCallData({ + slippagePercent, + queryOutput, + account, + permit2, + }: SdkBuildAddLiquidityInput): Promise { + const addLiquidity = new AddLiquidity() + + const buildCallParams = formatBuildCallParams( + this.constructBaseBuildCallInput({ + sdkQueryOutput: queryOutput.sdkQueryOutput, + slippagePercent: slippagePercent, + }), + this.helpers.isV3Pool(), + account + ) + + if (this.helpers.isV3Pool() && !permit2) { + throw new Error('Permit2 signature is required for V3 pools') + } + + const { callData, to, value } = + this.helpers.isV3Pool() && permit2 + ? addLiquidity.buildCallWithPermit2(buildCallParams, permit2) + : addLiquidity.buildCall(buildCallParams) + + return { + account, + chainId: this.helpers.chainId, + data: callData, + to, + value, + } + } + + public async signPermit2( + input: Permit2AddLiquidityInput, + sdkClient: PublicWalletClient, + nonces?: NoncesByTokenAddress + ): Promise { + // console.log('tengo los nonces super ricos') + const baseInput = this.constructBaseBuildCallInput({ + slippagePercent: input.slippagePercent, + sdkQueryOutput: input.sdkQueryOutput as AddLiquidityBaseQueryOutput, + }) + const signature = await Permit2Helper.signAddLiquidityApproval({ + ...baseInput, + client: sdkClient, + owner: input.account, + nonces: baseInput.amountsIn.map(a => (a.amount === 0n ? 3 : 1)), + }) + + return signature + } + + /** + * PRIVATE METHODS + */ + private constructSdkInput( + humanAmountsIn: HumanTokenAmountWithAddress[] + ): AddLiquidityUnbalancedInput { + const amountsIn = this.helpers.toSdkInputAmounts(humanAmountsIn) + + return { + chainId: this.helpers.chainId, + rpcUrl: getRpcUrl(this.helpers.chainId), + amountsIn, + kind: AddLiquidityKind.Unbalanced, + } + } + + public constructBaseBuildCallInput({ + slippagePercent, + sdkQueryOutput, + }: { + slippagePercent: string + sdkQueryOutput: AddLiquidityBaseQueryOutput + }): AddLiquidityBaseBuildCallInput { + const baseBuildCallParams = { + ...(sdkQueryOutput as AddLiquidityBaseQueryOutput), + slippage: Slippage.fromPercentage(`${Number(slippagePercent)}`), + wethIsEth: this.helpers.includesNativeAsset(sdkQueryOutput.amountsIn), + } + baseBuildCallParams.amountsIn = baseBuildCallParams.amountsIn.filter( + amountIn => amountIn.amount > 0n + ) + return baseBuildCallParams + } +} diff --git a/lib/modules/pool/actions/add-liquidity/useAddLiquiditySteps.tsx b/lib/modules/pool/actions/add-liquidity/useAddLiquiditySteps.tsx index 8b4414aea..ad6a0cc97 100644 --- a/lib/modules/pool/actions/add-liquidity/useAddLiquiditySteps.tsx +++ b/lib/modules/pool/actions/add-liquidity/useAddLiquiditySteps.tsx @@ -52,7 +52,8 @@ export function useAddLiquiditySteps({ slippagePercent: slippage, queryOutput: simulationQuery.data as SdkQueryAddLiquidityOutput, }, - chainId + chainId, + requiresPermit2Approval(pool) ) const addLiquidityStep = useAddLiquidityStep({ diff --git a/lib/modules/relayer/signRelayerApproval.hooks.tsx b/lib/modules/relayer/signRelayerApproval.hooks.tsx index b337ddd0a..21ba1fe51 100644 --- a/lib/modules/relayer/signRelayerApproval.hooks.tsx +++ b/lib/modules/relayer/signRelayerApproval.hooks.tsx @@ -7,7 +7,7 @@ import { RelayerMode } from './useRelayerMode' import { SignRelayerState, useRelayerSignature } from './RelayerSignatureProvider' import { SupportedChainId } from '@/lib/config/config.types' import { Toast } from '@/lib/shared/components/toasts/Toast' -import { useSdkViemClient } from '../web3/useSdkViemClient' +import { useSdkWalletClient } from '../web3/useSdkViemClient' export function useShouldSignRelayerApproval(chainId: SupportedChainId, relayerMode: RelayerMode) { const { hasApprovedRelayer } = useHasApprovedRelayer(chainId) @@ -23,7 +23,7 @@ export function useSignRelayerApproval(chainId: SupportedChainId) { const [error, setError] = useState() - const sdkClient = useSdkViemClient() + const sdkClient = useSdkWalletClient() useEffect(() => { if (sdkClient === undefined) { diff --git a/lib/modules/relayer/signRelayerApproval.ts b/lib/modules/relayer/signRelayerApproval.ts index 86888801f..a808d1208 100644 --- a/lib/modules/relayer/signRelayerApproval.ts +++ b/lib/modules/relayer/signRelayerApproval.ts @@ -1,14 +1,13 @@ import { getNetworkConfig } from '@/lib/config/app.config' import { SupportedChainId } from '@/lib/config/config.types' import { ensureError } from '@/lib/shared/utils/errors' -import { Relayer } from '@balancer/sdk' +import { PublicWalletClient, Relayer } from '@balancer/sdk' import { Address } from 'viem' -import { SdkClient } from '../web3/useSdkViemClient' export async function signRelayerApproval( userAddress: Address, chainId: SupportedChainId, - client?: SdkClient + client?: PublicWalletClient ): Promise
{ if (!client) return undefined diff --git a/lib/modules/tokens/approvals/permit2/signPermit2TokenApprovals.tsx b/lib/modules/tokens/approvals/permit2/signPermit2TokenTransfer.tsx similarity index 72% rename from lib/modules/tokens/approvals/permit2/signPermit2TokenApprovals.tsx rename to lib/modules/tokens/approvals/permit2/signPermit2TokenTransfer.tsx index f78c48172..672d08f64 100644 --- a/lib/modules/tokens/approvals/permit2/signPermit2TokenApprovals.tsx +++ b/lib/modules/tokens/approvals/permit2/signPermit2TokenTransfer.tsx @@ -2,25 +2,27 @@ import { AddLiquidityHandler, Permit2AddLiquidityInput, } from '@/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler' -import { SdkClient } from '@/lib/modules/web3/useSdkViemClient' import { ensureError } from '@/lib/shared/utils/errors' -import { Permit2 } from '@balancer/sdk' +import { Permit2, PublicWalletClient } from '@balancer/sdk' +import { NoncesByTokenAddress } from './usePermit2Nonces' type SignPermit2Params = { - handler: AddLiquidityHandler //TODO: maybe more handlers??? - sdkClient?: SdkClient + handler: AddLiquidityHandler //TODO: generalize to other handlers? + sdkClient?: PublicWalletClient permit2Input: Permit2AddLiquidityInput + nonces: NoncesByTokenAddress } -export async function signPermit2TokenApprovals({ +export async function signPermit2TokenTransfer({ handler, sdkClient, permit2Input, + nonces, }: SignPermit2Params): Promise { if (!sdkClient) return undefined try { if (!handler.signPermit2) throw new Error('Handler does not implement signPermit2 method') - const signature = await handler.signPermit2(permit2Input, sdkClient) + const signature = await handler.signPermit2(permit2Input, sdkClient, nonces) return signature } catch (e: unknown) { const error = ensureError(e) diff --git a/lib/modules/tokens/approvals/permit2/usePermit2Nonces.tsx b/lib/modules/tokens/approvals/permit2/usePermit2Nonces.tsx new file mode 100644 index 000000000..9003de602 --- /dev/null +++ b/lib/modules/tokens/approvals/permit2/usePermit2Nonces.tsx @@ -0,0 +1,52 @@ +import { getGqlChain, getNetworkConfig } from '@/lib/config/app.config' +import { permit2Abi } from '@balancer/sdk' +import { zipObject } from 'lodash' +import { Address } from 'viem' +import { useReadContracts } from 'wagmi' + +export type NoncesByTokenAddress = Record + +type Params = { + chainId: number + tokenAddresses?: Address[] + owner?: Address + enabled: boolean +} +export function usePermit2Nonces({ chainId, tokenAddresses, owner, enabled }: Params) { + const networkConfig = getNetworkConfig(getGqlChain(chainId)) + const permit2Address = networkConfig.contracts.permit2! + const balancerRouter = networkConfig.contracts.balancer.router! + const spender = balancerRouter + + const contracts = tokenAddresses?.map( + tokenAddress => + ({ + chainId, + address: permit2Address, + abi: permit2Abi, + functionName: 'allowance', + args: [owner, tokenAddress, spender], + } as const) + ) + + const { data, isLoading } = useReadContracts({ + contracts, + allowFailure: false, + query: { + enabled: enabled && tokenAddresses && tokenAddresses.length > 0 && !!owner, + }, + }) + + const nonces: NoncesByTokenAddress | undefined = + tokenAddresses && data + ? zipObject( + tokenAddresses, + data.map(result => result[2]) + ) + : undefined + + return { + isLoadingNonces: isLoading, + nonces, + } +} diff --git a/lib/modules/tokens/approvals/permit2/useSignPermit2Approval.tsx b/lib/modules/tokens/approvals/permit2/useSignPermit2Transfer.tsx similarity index 88% rename from lib/modules/tokens/approvals/permit2/useSignPermit2Approval.tsx rename to lib/modules/tokens/approvals/permit2/useSignPermit2Transfer.tsx index bbdd2c005..392b2a32f 100644 --- a/lib/modules/tokens/approvals/permit2/useSignPermit2Approval.tsx +++ b/lib/modules/tokens/approvals/permit2/useSignPermit2Transfer.tsx @@ -2,22 +2,25 @@ import { SdkQueryAddLiquidityOutput } from '@/lib/modules/pool/actions/add-liquidity/add-liquidity.types' import { AddLiquidityHandler } from '@/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler' import { useUserAccount } from '@/lib/modules/web3/UserAccountProvider' -import { useSdkViemClient } from '@/lib/modules/web3/useSdkViemClient' +import { useSdkWalletClient } from '@/lib/modules/web3/useSdkViemClient' import { Toast } from '@/lib/shared/components/toasts/Toast' import { useToast } from '@chakra-ui/react' import { useEffect, useState } from 'react' import { SignPermit2State, usePermit2Signature } from './Permit2SignatureProvider' -import { signPermit2TokenApprovals } from './signPermit2TokenApprovals' +import { signPermit2TokenTransfer } from './signPermit2TokenTransfer' +import { NoncesByTokenAddress } from './usePermit2Nonces' export type AddLiquidityPermit2Params = { handler: AddLiquidityHandler queryOutput?: SdkQueryAddLiquidityOutput slippagePercent: string + nonces?: NoncesByTokenAddress } -export function useSignPermit2Approval({ +export function useSignPermit2Transfer({ queryOutput, slippagePercent, handler, + nonces, }: AddLiquidityPermit2Params) { const toast = useToast() const { userAddress } = useUserAccount() @@ -27,7 +30,7 @@ export function useSignPermit2Approval({ const [error, setError] = useState() - const sdkClient = useSdkViemClient() + const sdkClient = useSdkWalletClient() useEffect(() => { if (sdkClient === undefined) { @@ -48,11 +51,12 @@ export function useSignPermit2Approval({ async function signPermit2() { if (!queryOutput) throw new Error('No input provided for permit2 signature') + if (!nonces) throw new Error('No nonces provided for permit2 signature') setSignPermit2State(SignPermit2State.Confirming) setError(undefined) try { - const signature = await signPermit2TokenApprovals({ + const signature = await signPermit2TokenTransfer({ handler, sdkClient, permit2Input: { @@ -60,6 +64,7 @@ export function useSignPermit2Approval({ slippagePercent, sdkQueryOutput: queryOutput.sdkQueryOutput, }, + nonces, }) if (signature) { diff --git a/lib/modules/transactions/transaction-steps/useSignPermit2Step.tsx b/lib/modules/transactions/transaction-steps/useSignPermit2Step.tsx index bda801196..5d9fda51f 100644 --- a/lib/modules/transactions/transaction-steps/useSignPermit2Step.tsx +++ b/lib/modules/transactions/transaction-steps/useSignPermit2Step.tsx @@ -8,23 +8,43 @@ import { useMemo } from 'react' import { SignPermit2State } from '../../tokens/approvals/permit2/Permit2SignatureProvider' import { AddLiquidityPermit2Params, - useSignPermit2Approval, -} from '../../tokens/approvals/permit2/useSignPermit2Approval' + useSignPermit2Transfer, +} from '../../tokens/approvals/permit2/useSignPermit2Transfer' import { useChainSwitch } from '../../web3/useChainSwitch' import { TransactionStep } from './lib' +import { usePermit2Nonces } from '../../tokens/approvals/permit2/usePermit2Nonces' export const signRelayerStepTitle = 'Sign relayer' export function useSignPermit2Step( params: AddLiquidityPermit2Params, - chainId: number + chainId: number, + enabled = false ): TransactionStep { - const { isConnected } = useUserAccount() - const { signPermit2, signPermit2State, isLoading, isDisabled, buttonLabel, error } = - useSignPermit2Approval(params) + const { isConnected, userAddress } = useUserAccount() + + //TODO: Move this hook into useSignPermit2Transfer? + //TODO: isLoading state depending on amountsIn (simulation loaded)? + const { isLoadingNonces, nonces } = usePermit2Nonces({ + chainId, + tokenAddresses: params.queryOutput?.sdkQueryOutput.amountsIn.map(t => t.token.address), + owner: userAddress, + enabled, + }) + + const { + signPermit2, + signPermit2State, + isLoading: isLoadingTransfer, + isDisabled, + buttonLabel, + error, + } = useSignPermit2Transfer({ ...params, nonces }) const { shouldChangeNetwork, NetworkSwitchButton, networkSwitchButtonProps } = useChainSwitch(chainId) + const isLoading = isLoadingTransfer || isLoadingNonces + const SignPermitButton = () => ( {error && } diff --git a/lib/modules/web3/useSdkViemClient.tsx b/lib/modules/web3/useSdkViemClient.tsx index 63bd0aaf6..0d87204ac 100644 --- a/lib/modules/web3/useSdkViemClient.tsx +++ b/lib/modules/web3/useSdkViemClient.tsx @@ -1,14 +1,9 @@ import { publicActions, walletActions } from 'viem' import { useWalletClient } from 'wagmi' -import { Relayer } from '@balancer/sdk' +import { PublicWalletClient } from '@balancer/sdk' -// TODO: replace this type with the one exposed by the SDK (WIP) -// https://github.com/balancer/b-sdk/pull/417 -type SignRelayerApprovalParams = Parameters -export type SdkClient = SignRelayerApprovalParams[2] - -export function useSdkViemClient(): SdkClient | undefined { +export function useSdkWalletClient(): PublicWalletClient | undefined { const { data: walletClient } = useWalletClient() if (!walletClient) return - return walletClient.extend(publicActions).extend(walletActions) as SdkClient + return walletClient.extend(publicActions).extend(walletActions) as PublicWalletClient }