diff --git a/packages/kit/src/connectors/wagmiConnectors/sequenceWaasConnector.ts b/packages/kit/src/connectors/wagmiConnectors/sequenceWaasConnector.ts index 92456207..a78608dc 100644 --- a/packages/kit/src/connectors/wagmiConnectors/sequenceWaasConnector.ts +++ b/packages/kit/src/connectors/wagmiConnectors/sequenceWaasConnector.ts @@ -14,7 +14,8 @@ import { ProviderDisconnectedError, TransactionRejectedRpcError, UserRejectedRequestError, - getAddress + getAddress, + zeroAddress } from 'viem' import { createConnector } from 'wagmi' @@ -322,7 +323,13 @@ export class SequenceWaasProvider extends ethers.AbstractProvider implements EIP throw new UserRejectedRequestError(new Error('User confirmation ids do not match')) } - selectedFeeOption = feeOptions.find(feeOption => feeOption.token.contractAddress === confirmation.feeTokenAddress) + selectedFeeOption = feeOptions.find(feeOption => { + // Handle the case where feeTokenAddress is ZeroAddress and contractAddress is null + if (confirmation.feeTokenAddress === zeroAddress && feeOption.token.contractAddress === null) { + return true + } + return feeOption.token.contractAddress === confirmation.feeTokenAddress + }) } if (this.requestConfirmationHandler && this.showConfirmation) { diff --git a/packages/kit/src/hooks/useCheckWaasFeeOptions.ts b/packages/kit/src/hooks/useCheckWaasFeeOptions.ts new file mode 100644 index 00000000..dd2531dd --- /dev/null +++ b/packages/kit/src/hooks/useCheckWaasFeeOptions.ts @@ -0,0 +1,26 @@ +'use client' + +import { FeeOption, Transaction } from '@0xsequence/waas' +import { useConnections } from 'wagmi' + +export function useCheckWaasFeeOptions(): (params: { transactions: Transaction[]; chainId: number }) => Promise<{ + feeQuote: string | undefined + feeOptions: FeeOption[] | undefined + isSponsored: boolean +}> { + const connections = useConnections() + const waasConnector = connections.find(c => c.connector.id.includes('waas'))?.connector + + return async ({ transactions, chainId }) => { + if (!waasConnector) { + throw new Error('WaaS connector not found') + } + + const waasProvider = (waasConnector as any).sequenceWaasProvider + if (!waasProvider) { + throw new Error('WaaS provider not found') + } + + return waasProvider.checkTransactionFeeOptions({ transactions, chainId }) + } +} diff --git a/packages/kit/src/hooks/useWaasFeeOptions.ts b/packages/kit/src/hooks/useWaasFeeOptions.ts index 6085b963..886c03ed 100644 --- a/packages/kit/src/hooks/useWaasFeeOptions.ts +++ b/packages/kit/src/hooks/useWaasFeeOptions.ts @@ -2,14 +2,11 @@ import { FeeOption } from '@0xsequence/waas' import { ethers } from 'ethers' -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { Connector, useConnections } from 'wagmi' import { Deferred } from '../utils/deferred' -// null means it's native token -let _pendingFeeConfirmation: Deferred<{ id: string; feeTokenAddress?: string | null; confirmed: boolean }> | undefined - export type WaasFeeOptionConfirmation = { id: string options: FeeOption[] @@ -23,49 +20,55 @@ export function useWaasFeeOptions(): [ ] { const connections = useConnections() const waasConnector: Connector | undefined = connections.find(c => c.connector.id.includes('waas'))?.connector - const [pendingFeeOptionConfirmation, setPendingFeeOptionConfirmation] = useState() + const pendingConfirmationRef = useRef>() function confirmPendingFeeOption(id: string, feeTokenAddress: string | null) { - _pendingFeeConfirmation?.resolve({ id, feeTokenAddress, confirmed: true }) - setPendingFeeOptionConfirmation(undefined) - _pendingFeeConfirmation = undefined + if (pendingConfirmationRef.current) { + pendingConfirmationRef.current.resolve({ id, feeTokenAddress, confirmed: true }) + setPendingFeeOptionConfirmation(undefined) + pendingConfirmationRef.current = undefined + } } function rejectPendingFeeOption(id: string) { - _pendingFeeConfirmation?.resolve({ id, feeTokenAddress: undefined, confirmed: false }) - setPendingFeeOptionConfirmation(undefined) - _pendingFeeConfirmation = undefined + if (pendingConfirmationRef.current) { + pendingConfirmationRef.current.resolve({ id, feeTokenAddress: undefined, confirmed: false }) + setPendingFeeOptionConfirmation(undefined) + pendingConfirmationRef.current = undefined + } } useEffect(() => { - async function setup() { - if (!waasConnector) { - return - } + if (!waasConnector) { + return + } - const waasProvider = (waasConnector as any).sequenceWaasProvider + const waasProvider = (waasConnector as any).sequenceWaasProvider + if (!waasProvider) { + return + } - if (!waasProvider) { - return - } + const originalHandler = waasProvider.feeConfirmationHandler - waasProvider.feeConfirmationHandler = { - confirmFeeOption( - id: string, - options: FeeOption[], - txs: ethers.Transaction[], - chainId: number - ): Promise<{ id: string; feeTokenAddress?: string | null; confirmed: boolean }> { - const pending = new Deferred<{ id: string; confirmed: boolean }>() - setPendingFeeOptionConfirmation({ id, options, chainId }) - _pendingFeeConfirmation = pending - return pending.promise - } + waasProvider.feeConfirmationHandler = { + confirmFeeOption( + id: string, + options: FeeOption[], + txs: ethers.Transaction[], + chainId: number + ): Promise<{ id: string; feeTokenAddress?: string | null; confirmed: boolean }> { + const pending = new Deferred<{ id: string; feeTokenAddress?: string | null; confirmed: boolean }>() + pendingConfirmationRef.current = pending + setPendingFeeOptionConfirmation({ id, options, chainId }) + return pending.promise } } - setup() - }) + + return () => { + waasProvider.feeConfirmationHandler = originalHandler + } + }, [waasConnector]) return [pendingFeeOptionConfirmation, confirmPendingFeeOption, rejectPendingFeeOption] } diff --git a/packages/kit/src/index.ts b/packages/kit/src/index.ts index 57342645..fcfb845a 100644 --- a/packages/kit/src/index.ts +++ b/packages/kit/src/index.ts @@ -84,6 +84,7 @@ export { useOpenConnectModal } from './hooks/useOpenConnectModal' export { useTheme } from './hooks/useTheme' export { useWalletSettings } from './hooks/useWalletSettings' export { useWaasFeeOptions } from './hooks/useWaasFeeOptions' +export { useCheckWaasFeeOptions } from './hooks/useCheckWaasFeeOptions' export { useWaasSignInEmail } from './hooks/useWaasSignInEmail' export { useSignInEmail } from './hooks/useSignInEmail' export { useProjectAccessKey } from './hooks/useProjectAccessKey' diff --git a/packages/wallet/src/contexts/Navigation.ts b/packages/wallet/src/contexts/Navigation.ts index 18a375f4..68543700 100644 --- a/packages/wallet/src/contexts/Navigation.ts +++ b/packages/wallet/src/contexts/Navigation.ts @@ -102,6 +102,8 @@ export type History = Navigation[] type NavigationContext = { setHistory: (history: History) => void history: History + isBackButtonEnabled: boolean + setIsBackButtonEnabled: (enabled: boolean) => void } export const [useNavigationContext, NavigationContextProvider] = createGenericContext() diff --git a/packages/wallet/src/shared/Alert.tsx b/packages/wallet/src/shared/Alert.tsx new file mode 100644 index 00000000..8ba4fc88 --- /dev/null +++ b/packages/wallet/src/shared/Alert.tsx @@ -0,0 +1,53 @@ +import { Box, Button, Text } from '@0xsequence/design-system' +import React, { ComponentProps } from 'react' + +export type AlertProps = { + title: string + description: string + secondaryDescription?: string + variant: 'negative' | 'warning' | 'positive' + buttonProps?: ComponentProps + children?: React.ReactNode +} + +export const Alert = ({ title, description, secondaryDescription, variant, buttonProps, children }: AlertProps) => { + return ( + + + + + + {title} + + + + {description} + + + {secondaryDescription && ( + + {secondaryDescription} + + )} + + + {buttonProps ? ( + +