From 76377251ecae7ca9cb166b70298ebd0baca170ae Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 3 Jan 2025 10:44:31 -0300 Subject: [PATCH 01/19] improve UI on log in --- src/components/SignIn/index.tsx | 37 +++++++++++++++++++++++++++++---- src/contexts/network.tsx | 2 +- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/components/SignIn/index.tsx b/src/components/SignIn/index.tsx index de567715..e35d0b06 100644 --- a/src/components/SignIn/index.tsx +++ b/src/components/SignIn/index.tsx @@ -1,4 +1,5 @@ -import { FC } from 'preact/compat'; +import { FC, useState } from 'preact/compat'; +import { useEffect } from 'react'; import { Modal } from 'react-daisyui'; interface SignInModalProps { @@ -8,10 +9,22 @@ interface SignInModalProps { } export const SignInModal: FC = ({ signingPending, closeModal, handleSignIn }) => { + const [waitingForWallet, setWaitingForWallet] = useState(false); + + const onSignMessage = () => { + setWaitingForWallet(true); + handleSignIn(); + }; + if (!signingPending) { return null; } + // We reset the waiting state when the signing process is finished, or failed. + useEffect(() => { + setWaitingForWallet(false); + }, [signingPending]); + return ( @@ -21,11 +34,27 @@ export const SignInModal: FC = ({ signingPending, closeModal, -

Please sign the message to log-in

+ {waitingForWallet ? ( +
+ Proceed to your wallet to complete the signing process. +
+ ) : ( +

Please sign the message to log in.

+ )}
- + */} From 0bc5aacb80215b368a8ed42c38f78974471bea5d Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Mon, 13 Jan 2025 10:14:18 -0300 Subject: [PATCH 04/19] reuse signing box for login --- src/components/SignIn/index.tsx | 69 ----------------------------- src/components/SigningBox/index.tsx | 41 ++++++++++------- src/hooks/offramp/useMainProcess.ts | 1 - src/hooks/useSignChallenge.ts | 27 +++++------ src/pages/swap/index.tsx | 8 +--- src/services/offrampingFlow.ts | 4 +- src/types/offramp.ts | 2 +- 7 files changed, 41 insertions(+), 111 deletions(-) delete mode 100644 src/components/SignIn/index.tsx diff --git a/src/components/SignIn/index.tsx b/src/components/SignIn/index.tsx deleted file mode 100644 index cc5eaf19..00000000 --- a/src/components/SignIn/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { FC } from 'preact/compat'; -import { useEffect, useState } from 'preact/hooks'; -import { Modal } from 'react-daisyui'; - -interface SignInModalProps { - signingPending: boolean; - closeModal: () => void; - handleSignIn: () => void; -} - -export const SignInModal: FC = ({ signingPending, closeModal, handleSignIn }) => { - const [waitingForWallet, setWaitingForWallet] = useState(true); - - // const onSignMessage = () => { - // setWaitingForWallet(true); - // handleSignIn(); - // }; - - if (!signingPending) { - return null; - } - - // Legacy setWaitingForWallet to true on signingPending change. - // Confirm button then triggers immediate handleSignIn. - useEffect(() => { - setWaitingForWallet(true); - if (signingPending) { - handleSignIn(); - } - }, [signingPending]); - - return ( - - - Sign In - - - - {waitingForWallet ? ( -
- Proceed to your wallet to complete the signing process. -
- ) : ( -

Please sign the message to log in.

- )} -
- - {/* */} - - -
- ); -}; diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index f7f1e27a..79383900 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -2,7 +2,7 @@ import { Progress } from 'react-daisyui'; import { FC } from 'preact/compat'; import accountBalanceWalletIcon from '../../assets/account-balance-wallet.svg'; -import { SigningPhase } from '../../hooks/offramp/useMainProcess'; +import { OfframpSigningPhase } from '../../types/offramp'; import { isNetworkEVM, Networks } from '../../helpers/networks'; import { useNetwork } from '../../contexts/network'; import { Spinner } from '../Spinner'; @@ -12,11 +12,12 @@ type ProgressStep = { signed: string; finished: string; approved: string; + login: string; }; type SignatureConfig = { maxSignatures: number; - getSignatureNumber: (step: SigningPhase) => string; + getSignatureNumber: (step: OfframpSigningPhase) => string; }; const EVM_PROGRESS_CONFIG: ProgressStep = { @@ -24,6 +25,7 @@ const EVM_PROGRESS_CONFIG: ProgressStep = { approved: '50', signed: '75', finished: '100', + login: '0', }; const NON_EVM_PROGRESS_CONFIG: ProgressStep = { @@ -31,11 +33,12 @@ const NON_EVM_PROGRESS_CONFIG: ProgressStep = { finished: '100', signed: '0', approved: '0', + login: '0', }; const EVM_SIGNATURE_CONFIG: SignatureConfig = { maxSignatures: 2, - getSignatureNumber: (step: SigningPhase) => (step === 'started' ? '1' : '2'), + getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' ? '1' : '2'), }; const NON_EVM_SIGNATURE_CONFIG: SignatureConfig = { @@ -52,12 +55,12 @@ const getSignatureConfig = (network: Networks): SignatureConfig => { }; interface SigningBoxProps { - step?: SigningPhase; + step?: OfframpSigningPhase; } -const isValidStep = (step: SigningPhase | undefined, network: Networks): step is SigningPhase => { +const isValidStep = (step: OfframpSigningPhase | undefined, network: Networks): step is OfframpSigningPhase => { if (!step) return false; - if (!['started', 'approved', 'signed'].includes(step)) return false; + if (step === 'finished') return false; if (!isNetworkEVM(network) && (step === 'approved' || step === 'signed')) return false; return true; }; @@ -88,17 +91,25 @@ export const SigningBox: FC = ({ step }) => { -
- -
+ {step !== 'login' && ( +
+ +
+ )} -
- -

- Waiting for signature {getSignatureNumber(step)}/{maxSignatures} -

-
+ {step !== 'login' && ( +
+ +

+ Waiting for signature {getSignatureNumber(step)}/{maxSignatures} +

+
+ )} ); diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index 400bdfa9..12f0b440 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -12,7 +12,6 @@ import { useOfframpActions, useOfframpState } from '../../stores/offrampStore'; import { useSep24UrlInterval, useSep24InitialResponse } from '../../stores/sep24Store'; import { useSep24Actions } from '../../stores/sep24Store'; import { useAnchorWindowHandler } from './useSEP24/useAnchorWindowHandler'; -export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; export interface ExecutionInput { inputTokenType: InputTokenType; diff --git a/src/hooks/useSignChallenge.ts b/src/hooks/useSignChallenge.ts index 6b56a68e..2e8ddf5b 100644 --- a/src/hooks/useSignChallenge.ts +++ b/src/hooks/useSignChallenge.ts @@ -5,6 +5,8 @@ import { DEFAULT_LOGIN_EXPIRATION_TIME_HOURS } from '../constants/constants'; import { SIGNING_SERVICE_URL } from '../constants/constants'; import { storageKeys } from '../constants/localStorage'; import { useVortexAccount } from './useVortexAccount'; +import { useOfframpActions } from '../stores/offrampStore'; +import { useEffect } from 'react'; export interface SiweSignatureData { signatureSet: boolean; @@ -24,9 +26,8 @@ function createSiweMessage(address: string, nonce: string) { } export function useSiweSignature() { - const [signingPending, setSigningPending] = useState(false); const { address, getMessageSignature } = useVortexAccount(); - + const { setOfframpSigningPhase } = useOfframpActions(); // Used to wait for the modal interaction and/or return of the // signing promise. const [signPromise, setSignPromise] = useState<{ @@ -55,9 +56,9 @@ export function useSiweSignature() { if (signPromise) return; return new Promise((resolve, reject) => { setSignPromise({ resolve, reject }); - setSigningPending(true); + setOfframpSigningPhase?.('login'); }); - }, [setSigningPending, setSignPromise, signPromise]); + }, [setSignPromise, signPromise]); const handleSign = useCallback(async () => { if (!address || !signPromise) return; @@ -99,19 +100,14 @@ export function useSiweSignature() { const errorMessage = error instanceof Error ? error.message : String(error); signPromise.reject(new Error('Signing failed: ' + errorMessage)); } finally { - setSigningPending(false); setSignPromise(null); + setOfframpSigningPhase?.(undefined); } - }, [address, storageKey, signPromise, setSigningPending, setSignPromise, getMessageSignature]); + }, [address, storageKey, signPromise, setSignPromise, getMessageSignature]); - // Handler for modal cancellation - const handleCancel = useCallback(() => { - if (signPromise) { - signPromise.reject(new Error('User cancelled')); - setSignPromise(null); - } - setSigningPending(false); - }, [signPromise, setSigningPending, setSignPromise]); + useEffect(() => { + if (signPromise) handleSign(); + }, [signPromise, handleSign]); const checkAndWaitForSignature = useCallback(async (): Promise => { const stored = checkStoredSignature(); @@ -125,9 +121,6 @@ export function useSiweSignature() { }, [storageKey, signMessage]); return { - signingPending, - handleSign, - handleCancel, checkAndWaitForSignature, forceRefreshAndWaitForSignature, }; diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 962e9f39..e10be812 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -15,7 +15,6 @@ import { ExchangeRate } from '../../components/ExchangeRate'; import { LabeledInput } from '../../components/LabeledInput'; import { UserBalance } from '../../components/UserBalance'; import { SigningBox } from '../../components/SigningBox'; -import { SignInModal } from '../../components/SignIn'; import { PoweredBy } from '../../components/PoweredBy'; import { @@ -30,11 +29,10 @@ import { config } from '../../config'; import { useEventsContext } from '../../contexts/events'; import { useNetwork } from '../../contexts/network'; import { usePendulumNode } from '../../contexts/polkadotNode'; -import { useSiweContext } from '../../contexts/siwe'; import { multiplyByPowerOfTen, stringifyBigWithSignificantDecimals } from '../../helpers/contracts'; import { showToast, ToastMessage } from '../../helpers/notifications'; -import { isNetworkEVM, Networks } from '../../helpers/networks'; +import { isNetworkEVM } from '../../helpers/networks'; import { useInputTokenBalance } from '../../hooks/useInputTokenBalance'; import { useTokenOutAmount } from '../../hooks/nabla/useTokenAmountOut'; @@ -78,7 +76,6 @@ export const SwapPage = () => { const [cachedId, setCachedId] = useState(undefined); const { trackEvent } = useEventsContext(); const { selectedNetwork, setNetworkSelectorDisabled } = useNetwork(); - const { signingPending, handleSign, handleCancel } = useSiweContext(); const [termsAnimationKey, setTermsAnimationKey] = useState(0); @@ -369,7 +366,7 @@ export const SwapPage = () => { from, selectedNetwork, fromAmountString, - requiresSquidRouter: selectedNetwork === Networks.Polygon, + requiresSquidRouter: isNetworkEVM(selectedNetwork), setOfframpInitiating, setInitializeFailed, handleOnSubmit, @@ -379,7 +376,6 @@ export const SwapPage = () => { const main = (
-
void; + setOfframpSigningPhase: (n: OfframpSigningPhase) => void; trackEvent: (event: TrackableEvent) => void; pendulumNode: { ss58Format: number; api: ApiPromise; decimals: number }; assetHubNode: { api: ApiPromise }; diff --git a/src/types/offramp.ts b/src/types/offramp.ts index 305299ff..4dcbeecc 100644 --- a/src/types/offramp.ts +++ b/src/types/offramp.ts @@ -5,7 +5,7 @@ import { OfframpingState } from '../services/offrampingFlow'; import { InputTokenType, OutputTokenType } from '../constants/tokenConfig'; import { ISep24Intermediate, IAnchorSessionParams } from './sep'; -export type OfframpSigningPhase = 'started' | 'approved' | 'signed' | 'finished'; +export type OfframpSigningPhase = 'login' | 'started' | 'approved' | 'signed' | 'finished'; export interface OfframpExecutionInput { inputTokenType: InputTokenType; From f7c14d9347e473f735d1741ae42a2734e7e0a2d4 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Mon, 13 Jan 2025 10:38:55 -0300 Subject: [PATCH 05/19] better error handling on wallet rejections --- src/hooks/offramp/useSubmitOfframp.ts | 4 +++- src/pages/swap/index.tsx | 16 +++++++++++++++- src/services/anchor/sep10/index.ts | 10 +++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index a4ffc881..327a8ef3 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -127,7 +127,9 @@ export const useSubmitOfframp = () => { console.error('Error initializing the offramping process', error); // Display error message, differentiating between user rejection and other errors if ((error as Error).message.includes('User rejected the request')) { - setInitializeFailed('Please switch to the correct network and try again.'); + setInitializeFailed('Please switch to the correct network and try again.'); // Case: User rejected switching the network, relevant in Metamask. + } else if ((error as Error).message.includes('User rejected sign request')) { + setInitializeFailed('Please sign the login message to continue.'); // Case: User rejected signing the login challenge. } else { setInitializeFailed(); } diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index e10be812..e582d863 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -96,6 +96,15 @@ export const SwapPage = () => { ); }, []); + const clearInitializeErrors = useCallback(() => { + if (!initializeFailedMessage) return; + if (initializeFailedMessage.includes('Please reload')) { + return window.location.reload(); + } else { + setInitializeFailedMessage(null); + } + }, [initializeFailedMessage, setInitializeFailedMessage]); + useEffect(() => { const initialize = async () => { try { @@ -405,7 +414,12 @@ export const SwapPage = () => {
{(initializeFailedMessage || apiInitializeFailed) && ( -

{initializeFailedMessage}

+
+

{initializeFailedMessage}

+ +
)}
diff --git a/src/services/anchor/sep10/index.ts b/src/services/anchor/sep10/index.ts index 7a1740e2..bd0e41ca 100644 --- a/src/services/anchor/sep10/index.ts +++ b/src/services/anchor/sep10/index.ts @@ -56,7 +56,15 @@ export async function sep10( const transactionSigned = await fetchAndValidateChallenge(webAuthEndpoint, urlParams, signingKey); if (usesMemo) { - await checkAndWaitForSignature(); + try { + await checkAndWaitForSignature(); + } catch (error) { + // We must differentiate between user driven rejection and other errors + if ((error as Error).message.includes('User rejected the request')) { + throw new Error('User rejected sign request'); + } + throw new Error('Failed to sign login challenge'); + } } const { masterClientSignature, clientSignature, clientPublic } = await sep10SignaturesWithLoginRefresh( From 932d4a7d98d3b4f8e2986a19ab6529cefa06e06a Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Mon, 13 Jan 2025 10:55:24 -0300 Subject: [PATCH 06/19] log signature login raw error --- src/services/anchor/sep10/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/anchor/sep10/index.ts b/src/services/anchor/sep10/index.ts index bd0e41ca..6412cbd0 100644 --- a/src/services/anchor/sep10/index.ts +++ b/src/services/anchor/sep10/index.ts @@ -60,6 +60,7 @@ export async function sep10( await checkAndWaitForSignature(); } catch (error) { // We must differentiate between user driven rejection and other errors + console.log((error as Error).message); if ((error as Error).message.includes('User rejected the request')) { throw new Error('User rejected sign request'); } From 9b96dfa1d18450f4a7891145ee811d874386769b Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Mon, 13 Jan 2025 11:03:00 -0300 Subject: [PATCH 07/19] support different error message after wallet rejection --- src/pages/swap/index.tsx | 2 +- src/services/anchor/sep10/index.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 889482d6..dd68888d 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -100,7 +100,7 @@ export const SwapPage = () => { const clearInitializeErrors = useCallback(() => { if (!initializeFailedMessage) return; - if (initializeFailedMessage.includes('Please reload')) { + if (initializeFailedMessage.includes('Hang tight')) { return window.location.reload(); } else { setInitializeFailedMessage(null); diff --git a/src/services/anchor/sep10/index.ts b/src/services/anchor/sep10/index.ts index 6412cbd0..77d98bc8 100644 --- a/src/services/anchor/sep10/index.ts +++ b/src/services/anchor/sep10/index.ts @@ -61,7 +61,11 @@ export async function sep10( } catch (error) { // We must differentiate between user driven rejection and other errors console.log((error as Error).message); - if ((error as Error).message.includes('User rejected the request')) { + if ( + (error as Error).message.includes('User rejected the request') || + (error as Error).message.includes('Signing failed: Cancelled') + ) { + // First case Assethub, second case EVM throw new Error('User rejected sign request'); } throw new Error('Failed to sign login challenge'); From f41851f98f518ee2c05e41d54d34d2c90b2e27d1 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Thu, 16 Jan 2025 13:07:59 +0100 Subject: [PATCH 08/19] Enable progress bar and footer also for `login` phase --- src/components/SigningBox/index.tsx | 30 +++++++++++------------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index 79383900..443fac9c 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -25,7 +25,7 @@ const EVM_PROGRESS_CONFIG: ProgressStep = { approved: '50', signed: '75', finished: '100', - login: '0', + login: '15', }; const NON_EVM_PROGRESS_CONFIG: ProgressStep = { @@ -33,7 +33,7 @@ const NON_EVM_PROGRESS_CONFIG: ProgressStep = { finished: '100', signed: '0', approved: '0', - login: '0', + login: '15', }; const EVM_SIGNATURE_CONFIG: SignatureConfig = { @@ -91,25 +91,17 @@ export const SigningBox: FC = ({ step }) => { - {step !== 'login' && ( -
- -
- )} +
+ +
- {step !== 'login' && ( -
- -

- Waiting for signature {getSignatureNumber(step)}/{maxSignatures} -

-
- )} +
+ +

+ Waiting for signature {getSignatureNumber(step)}/{maxSignatures} +

+
); From 591190b485b8bb1cd092362e576b0dbc60aebc9a Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Thu, 16 Jan 2025 17:19:40 -0300 Subject: [PATCH 09/19] reset sep variables also when changing network --- src/contexts/network.tsx | 3 +++ src/pages/swap/index.tsx | 1 + src/stores/sep24Store.ts | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/contexts/network.tsx b/src/contexts/network.tsx index bab19759..5458dac9 100644 --- a/src/contexts/network.tsx +++ b/src/contexts/network.tsx @@ -5,6 +5,7 @@ import { useLocalStorage, LocalStorageKeys } from '../hooks/useLocalStorage'; import { WALLETCONNECT_ASSETHUB_ID } from '../constants/constants'; import { useOfframpActions } from '../stores/offrampStore'; import { getNetworkId, isNetworkEVM, Networks } from '../helpers/networks'; +import { useSep24Actions } from '../stores/sep24Store'; interface NetworkContextType { walletConnectPolkadotSelectedNetworkId: string; @@ -36,11 +37,13 @@ export const NetworkProvider = ({ children }: NetworkProviderProps) => { const [networkSelectorDisabled, setNetworkSelectorDisabled] = useState(false); const { resetOfframpState } = useOfframpActions(); + const { cleanup: cleanupSep24Variables } = useSep24Actions(); const { switchChain } = useSwitchChain(); const setSelectedNetwork = useCallback( (network: Networks) => { resetOfframpState(); + cleanupSep24Variables(); setSelectedNetworkState(network); setSelectedNetworkLocalStorage(network); diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 5eb56f44..c7bc24fc 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -1,6 +1,7 @@ import Big from 'big.js'; import { useEffect, useMemo, useRef, useState, useCallback } from 'preact/hooks'; import { ApiPromise } from '@polkadot/api'; +import { Fragment } from 'preact'; import { calculateTotalReceive, FeeCollapse } from '../../components/FeeCollapse'; import { PoolSelectorModal } from '../../components/InputKeys/SelectionModal'; diff --git a/src/stores/sep24Store.ts b/src/stores/sep24Store.ts index 9520227f..3072a918 100644 --- a/src/stores/sep24Store.ts +++ b/src/stores/sep24Store.ts @@ -36,13 +36,14 @@ const useSep24Store = create()((set, get) => ({ setExecutionInput: (input) => set({ executionInput: input }), setUrlInterval: (interval) => set({ urlInterval: interval }), - reset: () => + reset: () => { set({ anchorSessionParams: undefined, initialResponse: undefined, executionInput: undefined, urlInterval: undefined, - }), + }); + }, cleanup: () => { const { urlInterval } = get(); From 3f6551fcb7a47cceea6facd42688325cb32b602b Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Thu, 16 Jan 2025 19:12:14 -0300 Subject: [PATCH 10/19] sign finished animation --- src/components/SigningBox/index.tsx | 44 +++++++++++++++++++++++++---- src/hooks/useSignChallenge.ts | 2 +- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index 443fac9c..60546855 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -1,5 +1,5 @@ import { Progress } from 'react-daisyui'; -import { FC } from 'preact/compat'; +import { FC, useRef, useState } from 'preact/compat'; import accountBalanceWalletIcon from '../../assets/account-balance-wallet.svg'; import { OfframpSigningPhase } from '../../types/offramp'; @@ -60,19 +60,49 @@ interface SigningBoxProps { const isValidStep = (step: OfframpSigningPhase | undefined, network: Networks): step is OfframpSigningPhase => { if (!step) return false; - if (step === 'finished') return false; + if (step === 'finished') return true; if (!isNetworkEVM(network) && (step === 'approved' || step === 'signed')) return false; return true; }; +const increaseProgressValueTo = (from: number, to: number, setProgressValueDisplay: (value: number) => void) => { + setProgressValueDisplay(from); + if (from === to) return; + + setTimeout(() => { + increaseProgressValueTo(from + 1, to, setProgressValueDisplay); + }, 10); +}; + export const SigningBox: FC = ({ step }) => { const { selectedNetwork } = useNetwork(); + const [progressValueDisplay, setProgressValueDisplay] = useState(0); + const initialMaxSignaturesRef = useRef(0); + const initialSignatureNumberRef = useRef(0); if (!isValidStep(step, selectedNetwork)) return null; - const progressValue = getProgressConfig(selectedNetwork)[step]; + if (step === 'finished') { + increaseProgressValueTo(progressValueDisplay, 100, setProgressValueDisplay); + } else { + setProgressValueDisplay(Number(getProgressConfig(selectedNetwork)[step])); + } + + if (progressValueDisplay == 100) { + return null; + } + const { maxSignatures, getSignatureNumber } = getSignatureConfig(selectedNetwork); + // If it is login step, signatureNumber is 0 and maxSignatures is 1, for any network + // Finished will display the last signature number and maxSignatures + const signatureNumber = + step === 'login' ? 1 : step === 'finished' ? initialMaxSignaturesRef.current : Number(getSignatureNumber(step)); + initialSignatureNumberRef.current = signatureNumber; + const maxSignaturesDisplay = + step === 'login' ? 1 : step === 'finished' ? initialMaxSignaturesRef.current : maxSignatures; + initialMaxSignaturesRef.current = maxSignaturesDisplay; + return (
@@ -92,14 +122,18 @@ export const SigningBox: FC = ({ step }) => {
- +

- Waiting for signature {getSignatureNumber(step)}/{maxSignatures} + Waiting for signature {initialSignatureNumberRef.current}/{initialMaxSignaturesRef.current}

diff --git a/src/hooks/useSignChallenge.ts b/src/hooks/useSignChallenge.ts index 2e8ddf5b..06f5094c 100644 --- a/src/hooks/useSignChallenge.ts +++ b/src/hooks/useSignChallenge.ts @@ -101,7 +101,7 @@ export function useSiweSignature() { signPromise.reject(new Error('Signing failed: ' + errorMessage)); } finally { setSignPromise(null); - setOfframpSigningPhase?.(undefined); + setOfframpSigningPhase?.('finished'); } }, [address, storageKey, signPromise, setSignPromise, getMessageSignature]); From b1520d5dacabc6973dd3dc9675929522aa3fb149 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 10:10:55 -0300 Subject: [PATCH 11/19] improve animation code --- src/components/SigningBox/index.tsx | 76 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index 60546855..c9208974 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -1,11 +1,12 @@ import { Progress } from 'react-daisyui'; -import { FC, useRef, useState } from 'preact/compat'; +import { FC, StateUpdater, useRef, useState } from 'preact/compat'; import accountBalanceWalletIcon from '../../assets/account-balance-wallet.svg'; import { OfframpSigningPhase } from '../../types/offramp'; import { isNetworkEVM, Networks } from '../../helpers/networks'; import { useNetwork } from '../../contexts/network'; import { Spinner } from '../Spinner'; +import { useEffect } from 'react'; type ProgressStep = { started: string; @@ -16,8 +17,8 @@ type ProgressStep = { }; type SignatureConfig = { - maxSignatures: number; - getSignatureNumber: (step: OfframpSigningPhase) => string; + maxSignatures: (step: OfframpSigningPhase) => number; + getSignatureNumber: (step: OfframpSigningPhase) => number; }; const EVM_PROGRESS_CONFIG: ProgressStep = { @@ -37,13 +38,13 @@ const NON_EVM_PROGRESS_CONFIG: ProgressStep = { }; const EVM_SIGNATURE_CONFIG: SignatureConfig = { - maxSignatures: 2, - getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' ? '1' : '2'), + maxSignatures: (step: OfframpSigningPhase) => (step === 'login' ? 1 : 2), + getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' || 'login' ? 1 : 2), }; const NON_EVM_SIGNATURE_CONFIG: SignatureConfig = { - maxSignatures: 1, - getSignatureNumber: () => '1', + maxSignatures: () => 1, + getSignatureNumber: () => 1, }; const getProgressConfig = (network: Networks): ProgressStep => { @@ -65,43 +66,44 @@ const isValidStep = (step: OfframpSigningPhase | undefined, network: Networks): return true; }; -const increaseProgressValueTo = (from: number, to: number, setProgressValueDisplay: (value: number) => void) => { - setProgressValueDisplay(from); - if (from === to) return; - - setTimeout(() => { - increaseProgressValueTo(from + 1, to, setProgressValueDisplay); - }, 10); -}; - export const SigningBox: FC = ({ step }) => { const { selectedNetwork } = useNetwork(); - const [progressValueDisplay, setProgressValueDisplay] = useState(0); + const [progressValue, setProgressValue] = useState(0); const initialMaxSignaturesRef = useRef(0); const initialSignatureNumberRef = useRef(0); if (!isValidStep(step, selectedNetwork)) return null; - if (step === 'finished') { - increaseProgressValueTo(progressValueDisplay, 100, setProgressValueDisplay); - } else { - setProgressValueDisplay(Number(getProgressConfig(selectedNetwork)[step])); - } + useEffect(() => { + if (step === 'finished') { + const animateProgress = () => { + setProgressValue((prev) => { + if (prev >= 100) return 100; + return prev + 1; + }); + }; - if (progressValueDisplay == 100) { - return null; - } + const intervalId = setInterval(animateProgress, 5); + return () => clearInterval(intervalId); + } else { + setProgressValue(Number(getProgressConfig(selectedNetwork)[step])); + } + }, [step, selectedNetwork]); + + if (progressValue === 100) return null; const { maxSignatures, getSignatureNumber } = getSignatureConfig(selectedNetwork); - // If it is login step, signatureNumber is 0 and maxSignatures is 1, for any network - // Finished will display the last signature number and maxSignatures - const signatureNumber = - step === 'login' ? 1 : step === 'finished' ? initialMaxSignaturesRef.current : Number(getSignatureNumber(step)); - initialSignatureNumberRef.current = signatureNumber; - const maxSignaturesDisplay = - step === 'login' ? 1 : step === 'finished' ? initialMaxSignaturesRef.current : maxSignatures; - initialMaxSignaturesRef.current = maxSignaturesDisplay; + useEffect(() => { + if (step !== 'finished') { + initialMaxSignaturesRef.current = maxSignatures(step); + initialSignatureNumberRef.current = getSignatureNumber(step); + } + }, [step, maxSignatures, getSignatureNumber]); + + const signatureNumber = step === 'finished' ? initialSignatureNumberRef.current : Number(getSignatureNumber(step)); + + const maxSignaturesDisplay = step === 'finished' ? initialMaxSignaturesRef.current : maxSignatures(step); return (
@@ -122,18 +124,14 @@ export const SigningBox: FC = ({ step }) => {
- +

- Waiting for signature {initialSignatureNumberRef.current}/{initialMaxSignaturesRef.current} + Waiting for signature {signatureNumber}/{maxSignaturesDisplay}

From 9ae32d7a10f7bd4d07d941973d4e06ff365db8a6 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 10:41:23 -0300 Subject: [PATCH 12/19] show toast error on sign rejection --- src/hooks/offramp/useSubmitOfframp.ts | 10 +++++----- src/hooks/useSignChallenge.ts | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index 327a8ef3..00cddac0 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -19,6 +19,8 @@ import { useOfframpActions, useOfframpStarted, useOfframpState } from '../../sto import { ExecutionInput } from './useMainProcess'; import { useSep24Actions } from '../../stores/sep24Store'; +import { showToast, ToastMessage } from '../../helpers/notifications'; + export const useSubmitOfframp = () => { const { selectedNetwork } = useNetwork(); const { switchChainAsync, switchChain } = useSwitchChain(); @@ -124,12 +126,10 @@ export const useSubmitOfframp = () => { setOfframpInitiating(false); } } catch (error) { - console.error('Error initializing the offramping process', error); + console.error('Error initializing the offramping process', (error as Error).message); // Display error message, differentiating between user rejection and other errors - if ((error as Error).message.includes('User rejected the request')) { - setInitializeFailed('Please switch to the correct network and try again.'); // Case: User rejected switching the network, relevant in Metamask. - } else if ((error as Error).message.includes('User rejected sign request')) { - setInitializeFailed('Please sign the login message to continue.'); // Case: User rejected signing the login challenge. + if ((error as Error).message.includes('User rejected')) { + showToast(ToastMessage.ERROR, 'You must sign the login request to offramp Agentinian Peso'); } else { setInitializeFailed(); } diff --git a/src/hooks/useSignChallenge.ts b/src/hooks/useSignChallenge.ts index 06f5094c..48f59c66 100644 --- a/src/hooks/useSignChallenge.ts +++ b/src/hooks/useSignChallenge.ts @@ -96,12 +96,13 @@ export function useSiweSignature() { localStorage.setItem(storageKey, JSON.stringify(signatureData)); signPromise.resolve(); + setOfframpSigningPhase?.('finished'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); signPromise.reject(new Error('Signing failed: ' + errorMessage)); + setOfframpSigningPhase?.(undefined); } finally { setSignPromise(null); - setOfframpSigningPhase?.('finished'); } }, [address, storageKey, signPromise, setSignPromise, getMessageSignature]); From 17ebe3d2c91cab223ceef8996fa7dfa09daf5b55 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 11:02:57 -0300 Subject: [PATCH 13/19] typo, small sign box improvement for assethub --- src/hooks/offramp/useSubmitOfframp.ts | 2 +- src/services/phases/polkadot/assethub.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index 00cddac0..8336066e 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -129,7 +129,7 @@ export const useSubmitOfframp = () => { console.error('Error initializing the offramping process', (error as Error).message); // Display error message, differentiating between user rejection and other errors if ((error as Error).message.includes('User rejected')) { - showToast(ToastMessage.ERROR, 'You must sign the login request to offramp Agentinian Peso'); + showToast(ToastMessage.ERROR, 'You must sign the login request to offramp Argentine Peso'); } else { setInitializeFailed(); } diff --git a/src/services/phases/polkadot/assethub.ts b/src/services/phases/polkadot/assethub.ts index 8232160c..83cac673 100644 --- a/src/services/phases/polkadot/assethub.ts +++ b/src/services/phases/polkadot/assethub.ts @@ -40,8 +40,6 @@ export async function executeAssetHubXCM(state: OfframpingState, context: Execut throw new Error('AssetHub node not available'); } - setOfframpSigningPhase?.('started'); - const didInputTokenArrivedOnPendulum = async () => { const inputBalanceRaw = await getRawInputBalance(state, context); return inputBalanceRaw.gt(Big(0)); From 67d4903f0fe292c0a4a4303d43d1e52fd3c21ed4 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 11:08:11 -0300 Subject: [PATCH 14/19] rollback on useless change --- src/stores/sep24Store.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/stores/sep24Store.ts b/src/stores/sep24Store.ts index 3072a918..9520227f 100644 --- a/src/stores/sep24Store.ts +++ b/src/stores/sep24Store.ts @@ -36,14 +36,13 @@ const useSep24Store = create()((set, get) => ({ setExecutionInput: (input) => set({ executionInput: input }), setUrlInterval: (interval) => set({ urlInterval: interval }), - reset: () => { + reset: () => set({ anchorSessionParams: undefined, initialResponse: undefined, executionInput: undefined, urlInterval: undefined, - }); - }, + }), cleanup: () => { const { urlInterval } = get(); From 8d55f7324b071f8b3cdfbed1cd1804226477313b Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 11:23:13 -0300 Subject: [PATCH 15/19] fix lint errors --- src/components/SigningBox/index.tsx | 24 ++++++++++++------------ src/contexts/network.tsx | 2 +- src/hooks/useSignChallenge.ts | 4 ++-- src/pages/swap/index.tsx | 12 ------------ 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index c9208974..d28de732 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -1,5 +1,5 @@ import { Progress } from 'react-daisyui'; -import { FC, StateUpdater, useRef, useState } from 'preact/compat'; +import { FC, useRef, useState } from 'preact/compat'; import accountBalanceWalletIcon from '../../assets/account-balance-wallet.svg'; import { OfframpSigningPhase } from '../../types/offramp'; @@ -39,7 +39,7 @@ const NON_EVM_PROGRESS_CONFIG: ProgressStep = { const EVM_SIGNATURE_CONFIG: SignatureConfig = { maxSignatures: (step: OfframpSigningPhase) => (step === 'login' ? 1 : 2), - getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' || 'login' ? 1 : 2), + getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' || step === 'login' ? 1 : 2), }; const NON_EVM_SIGNATURE_CONFIG: SignatureConfig = { @@ -72,7 +72,14 @@ export const SigningBox: FC = ({ step }) => { const initialMaxSignaturesRef = useRef(0); const initialSignatureNumberRef = useRef(0); - if (!isValidStep(step, selectedNetwork)) return null; + const { maxSignatures, getSignatureNumber } = getSignatureConfig(selectedNetwork); + + useEffect(() => { + if (step !== 'finished' && isValidStep(step, selectedNetwork)) { + initialMaxSignaturesRef.current = maxSignatures(step); + initialSignatureNumberRef.current = getSignatureNumber(step); + } + }, [step, selectedNetwork, maxSignatures, getSignatureNumber]); useEffect(() => { if (step === 'finished') { @@ -86,21 +93,14 @@ export const SigningBox: FC = ({ step }) => { const intervalId = setInterval(animateProgress, 5); return () => clearInterval(intervalId); } else { + if (!isValidStep(step, selectedNetwork)) return; setProgressValue(Number(getProgressConfig(selectedNetwork)[step])); } }, [step, selectedNetwork]); + if (!isValidStep(step, selectedNetwork)) return null; if (progressValue === 100) return null; - const { maxSignatures, getSignatureNumber } = getSignatureConfig(selectedNetwork); - - useEffect(() => { - if (step !== 'finished') { - initialMaxSignaturesRef.current = maxSignatures(step); - initialSignatureNumberRef.current = getSignatureNumber(step); - } - }, [step, maxSignatures, getSignatureNumber]); - const signatureNumber = step === 'finished' ? initialSignatureNumberRef.current : Number(getSignatureNumber(step)); const maxSignaturesDisplay = step === 'finished' ? initialMaxSignaturesRef.current : maxSignatures(step); diff --git a/src/contexts/network.tsx b/src/contexts/network.tsx index 5458dac9..9a4cea0d 100644 --- a/src/contexts/network.tsx +++ b/src/contexts/network.tsx @@ -52,7 +52,7 @@ export const NetworkProvider = ({ children }: NetworkProviderProps) => { switchChain({ chainId: getNetworkId(network) }); } }, - [switchChain, setSelectedNetworkLocalStorage, resetOfframpState], + [switchChain, setSelectedNetworkLocalStorage, resetOfframpState, cleanupSep24Variables], ); // Only run on first render diff --git a/src/hooks/useSignChallenge.ts b/src/hooks/useSignChallenge.ts index 48f59c66..f243df82 100644 --- a/src/hooks/useSignChallenge.ts +++ b/src/hooks/useSignChallenge.ts @@ -58,7 +58,7 @@ export function useSiweSignature() { setSignPromise({ resolve, reject }); setOfframpSigningPhase?.('login'); }); - }, [setSignPromise, signPromise]); + }, [setOfframpSigningPhase, setSignPromise, signPromise]); const handleSign = useCallback(async () => { if (!address || !signPromise) return; @@ -104,7 +104,7 @@ export function useSiweSignature() { } finally { setSignPromise(null); } - }, [address, storageKey, signPromise, setSignPromise, getMessageSignature]); + }, [address, storageKey, signPromise, setSignPromise, getMessageSignature, setOfframpSigningPhase]); useEffect(() => { if (signPromise) handleSign(); diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index c7bc24fc..4b728680 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -94,15 +94,6 @@ export const SwapPage = () => { ); }, []); - const clearInitializeErrors = useCallback(() => { - if (!initializeFailedMessage) return; - if (initializeFailedMessage.includes('Hang tight')) { - return window.location.reload(); - } else { - setInitializeFailedMessage(null); - } - }, [initializeFailedMessage, setInitializeFailedMessage]); - useEffect(() => { const initialize = async () => { try { @@ -408,9 +399,6 @@ export const SwapPage = () => { {(initializeFailedMessage || apiInitializeFailed) && (

{initializeFailedMessage}

-
)}
From 680218be44ae8b519514aa4508a29d3503ce2fd8 Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Fri, 17 Jan 2025 11:33:37 -0300 Subject: [PATCH 16/19] move user reject detection to signChallenge component --- src/hooks/useSignChallenge.ts | 10 +++++++++- src/services/anchor/sep10/index.ts | 15 +-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/hooks/useSignChallenge.ts b/src/hooks/useSignChallenge.ts index f243df82..c950c188 100644 --- a/src/hooks/useSignChallenge.ts +++ b/src/hooks/useSignChallenge.ts @@ -99,8 +99,16 @@ export function useSiweSignature() { setOfframpSigningPhase?.('finished'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - signPromise.reject(new Error('Signing failed: ' + errorMessage)); setOfframpSigningPhase?.(undefined); + + if ( + (error as Error).message.includes('User rejected the request') || + (error as Error).message.includes('Signing failed: Cancelled') + ) { + // First case Assethub, second case EVM + return signPromise.reject(new Error('Signing failed: User rejected sign request')); + } + return signPromise.reject(new Error('Signing failed: Failed to sign login challenge. ' + errorMessage)); } finally { setSignPromise(null); } diff --git a/src/services/anchor/sep10/index.ts b/src/services/anchor/sep10/index.ts index 77d98bc8..7a1740e2 100644 --- a/src/services/anchor/sep10/index.ts +++ b/src/services/anchor/sep10/index.ts @@ -56,20 +56,7 @@ export async function sep10( const transactionSigned = await fetchAndValidateChallenge(webAuthEndpoint, urlParams, signingKey); if (usesMemo) { - try { - await checkAndWaitForSignature(); - } catch (error) { - // We must differentiate between user driven rejection and other errors - console.log((error as Error).message); - if ( - (error as Error).message.includes('User rejected the request') || - (error as Error).message.includes('Signing failed: Cancelled') - ) { - // First case Assethub, second case EVM - throw new Error('User rejected sign request'); - } - throw new Error('Failed to sign login challenge'); - } + await checkAndWaitForSignature(); } const { masterClientSignature, clientSignature, clientPublic } = await sep10SignaturesWithLoginRefresh( From c80dbdfe62680916c28071117c842c6fc67d75b7 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 20 Jan 2025 11:25:19 +0100 Subject: [PATCH 17/19] Improve SigningBox animation --- src/assets/account-balance-wallet-blue.svg | 3 + src/components/SigningBox/index.tsx | 199 ++++++++++----------- 2 files changed, 97 insertions(+), 105 deletions(-) create mode 100644 src/assets/account-balance-wallet-blue.svg diff --git a/src/assets/account-balance-wallet-blue.svg b/src/assets/account-balance-wallet-blue.svg new file mode 100644 index 00000000..2f55a2d1 --- /dev/null +++ b/src/assets/account-balance-wallet-blue.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index d28de732..7c350d22 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -1,140 +1,129 @@ -import { Progress } from 'react-daisyui'; -import { FC, useRef, useState } from 'preact/compat'; -import accountBalanceWalletIcon from '../../assets/account-balance-wallet.svg'; +import { FC, useState, useEffect } from 'preact/compat'; +import { motion, AnimatePresence, useSpring, useTransform } from 'framer-motion'; +import accountBalanceWalletIcon from '../../assets/account-balance-wallet-blue.svg'; import { OfframpSigningPhase } from '../../types/offramp'; -import { isNetworkEVM, Networks } from '../../helpers/networks'; +import { isNetworkEVM } from '../../helpers/networks'; import { useNetwork } from '../../contexts/network'; import { Spinner } from '../Spinner'; -import { useEffect } from 'react'; - -type ProgressStep = { - started: string; - signed: string; - finished: string; - approved: string; - login: string; -}; - -type SignatureConfig = { - maxSignatures: (step: OfframpSigningPhase) => number; - getSignatureNumber: (step: OfframpSigningPhase) => number; -}; - -const EVM_PROGRESS_CONFIG: ProgressStep = { - started: '25', - approved: '50', - signed: '75', - finished: '100', - login: '15', -}; - -const NON_EVM_PROGRESS_CONFIG: ProgressStep = { - started: '33', - finished: '100', - signed: '0', - approved: '0', - login: '15', -}; -const EVM_SIGNATURE_CONFIG: SignatureConfig = { - maxSignatures: (step: OfframpSigningPhase) => (step === 'login' ? 1 : 2), - getSignatureNumber: (step: OfframpSigningPhase) => (step === 'started' || step === 'login' ? 1 : 2), +type ProgressConfig = { + [key in OfframpSigningPhase]: number; }; -const NON_EVM_SIGNATURE_CONFIG: SignatureConfig = { - maxSignatures: () => 1, - getSignatureNumber: () => 1, +const PROGRESS_CONFIGS: Record<'EVM' | 'NON_EVM', ProgressConfig> = { + EVM: { + started: 25, + approved: 50, + signed: 75, + finished: 100, + login: 15, + }, + NON_EVM: { + started: 33, + finished: 100, + signed: 0, + approved: 0, + login: 15, + }, }; -const getProgressConfig = (network: Networks): ProgressStep => { - return isNetworkEVM(network) ? EVM_PROGRESS_CONFIG : NON_EVM_PROGRESS_CONFIG; -}; - -const getSignatureConfig = (network: Networks): SignatureConfig => { - return isNetworkEVM(network) ? EVM_SIGNATURE_CONFIG : NON_EVM_SIGNATURE_CONFIG; +const getSignatureDetails = (step: OfframpSigningPhase, isEVM: boolean) => { + if (!isEVM) return { max: 1, current: 1 }; + if (step === 'login') return { max: 1, current: 1 }; + if (step === 'started') return { max: 2, current: 1 }; + return { max: 2, current: 2 }; }; interface SigningBoxProps { step?: OfframpSigningPhase; } -const isValidStep = (step: OfframpSigningPhase | undefined, network: Networks): step is OfframpSigningPhase => { +const isValidStep = (step: OfframpSigningPhase | undefined, isEVM: boolean): step is OfframpSigningPhase => { if (!step) return false; if (step === 'finished') return true; - if (!isNetworkEVM(network) && (step === 'approved' || step === 'signed')) return false; + if (!isEVM && (step === 'approved' || step === 'signed')) return false; return true; }; export const SigningBox: FC = ({ step }) => { const { selectedNetwork } = useNetwork(); - const [progressValue, setProgressValue] = useState(0); - const initialMaxSignaturesRef = useRef(0); - const initialSignatureNumberRef = useRef(0); + const isEVM = isNetworkEVM(selectedNetwork); + const progressConfig = isEVM ? PROGRESS_CONFIGS.EVM : PROGRESS_CONFIGS.NON_EVM; - const { maxSignatures, getSignatureNumber } = getSignatureConfig(selectedNetwork); + const progressValue = useSpring(0, { + stiffness: 60, + damping: 15, + restDelta: 0.001, + }); + const displayProgress = useTransform(progressValue, Math.round); - useEffect(() => { - if (step !== 'finished' && isValidStep(step, selectedNetwork)) { - initialMaxSignaturesRef.current = maxSignatures(step); - initialSignatureNumberRef.current = getSignatureNumber(step); - } - }, [step, selectedNetwork, maxSignatures, getSignatureNumber]); + const [signatureState, setSignatureState] = useState({ max: 0, current: 0 }); + const [shouldExit, setShouldExit] = useState(false); useEffect(() => { + if (!isValidStep(step, isEVM)) return; + if (step === 'finished') { - const animateProgress = () => { - setProgressValue((prev) => { - if (prev >= 100) return 100; - return prev + 1; - }); - }; - - const intervalId = setInterval(animateProgress, 5); - return () => clearInterval(intervalId); - } else { - if (!isValidStep(step, selectedNetwork)) return; - setProgressValue(Number(getProgressConfig(selectedNetwork)[step])); + progressValue.set(100); + setTimeout(() => setShouldExit(true), 3500); + return; } - }, [step, selectedNetwork]); - if (!isValidStep(step, selectedNetwork)) return null; - if (progressValue === 100) return null; + progressValue.set(progressConfig[step]); + setSignatureState(getSignatureDetails(step, isEVM)); + }, [step, isEVM, progressConfig, progressValue]); - const signatureNumber = step === 'finished' ? initialSignatureNumberRef.current : Number(getSignatureNumber(step)); - - const maxSignaturesDisplay = step === 'finished' ? initialMaxSignaturesRef.current : maxSignatures(step); + if (!isValidStep(step, isEVM) || shouldExit) return null; return ( -
-
-
-

Action Required

-
- -
-
-
- wallet account button -
-
-

Please sign the transaction in

-

your connected wallet to proceed

-
-
- -
- + + {!isValidStep(step, isEVM) || shouldExit ? null : ( + +
+ +

Action Required

+
+ +
+ +
+ wallet account button +
+
+

Please sign the transaction in

+

your connected wallet to proceed

+
+
+ + +
+ +
+
+
+ + + +

+ Waiting for signature {signatureState.current}/{signatureState.max} +

+
-
- -
- -

- Waiting for signature {signatureNumber}/{maxSignaturesDisplay} -

-
-
-
+ + )} + ); }; From 5d5d74a457405c248dbae6fdeed689e8ea31feae Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Tue, 21 Jan 2025 11:39:33 -0300 Subject: [PATCH 18/19] tweak signing box animation --- src/components/SigningBox/index.tsx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/components/SigningBox/index.tsx b/src/components/SigningBox/index.tsx index 7c350d22..e5e9e6ff 100644 --- a/src/components/SigningBox/index.tsx +++ b/src/components/SigningBox/index.tsx @@ -1,5 +1,5 @@ import { FC, useState, useEffect } from 'preact/compat'; -import { motion, AnimatePresence, useSpring, useTransform } from 'framer-motion'; +import { motion, AnimatePresence } from 'framer-motion'; import accountBalanceWalletIcon from '../../assets/account-balance-wallet-blue.svg'; import { OfframpSigningPhase } from '../../types/offramp'; @@ -41,7 +41,7 @@ interface SigningBoxProps { const isValidStep = (step: OfframpSigningPhase | undefined, isEVM: boolean): step is OfframpSigningPhase => { if (!step) return false; - if (step === 'finished') return true; + if (step === 'finished' || step === 'login') return true; if (!isEVM && (step === 'approved' || step === 'signed')) return false; return true; }; @@ -51,30 +51,26 @@ export const SigningBox: FC = ({ step }) => { const isEVM = isNetworkEVM(selectedNetwork); const progressConfig = isEVM ? PROGRESS_CONFIGS.EVM : PROGRESS_CONFIGS.NON_EVM; - const progressValue = useSpring(0, { - stiffness: 60, - damping: 15, - restDelta: 0.001, - }); - const displayProgress = useTransform(progressValue, Math.round); - + const [progress, setProgress] = useState(0); const [signatureState, setSignatureState] = useState({ max: 0, current: 0 }); const [shouldExit, setShouldExit] = useState(false); useEffect(() => { if (!isValidStep(step, isEVM)) return; + if (step !== 'finished' && shouldExit) { + setShouldExit(false); + } + if (step === 'finished') { - progressValue.set(100); - setTimeout(() => setShouldExit(true), 3500); + setProgress(100); + setTimeout(() => setShouldExit(true), 2500); return; } - progressValue.set(progressConfig[step]); + setProgress(progressConfig[step]); setSignatureState(getSignatureDetails(step, isEVM)); - }, [step, isEVM, progressConfig, progressValue]); - - if (!isValidStep(step, isEVM) || shouldExit) return null; + }, [step, isEVM, progressConfig, shouldExit]); return ( @@ -108,7 +104,7 @@ export const SigningBox: FC = ({ step }) => { From f5af08a2292d34ec32c89805e631fe453658880f Mon Sep 17 00:00:00 2001 From: gianfra-t <96739519+gianfra-t@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:54:14 -0300 Subject: [PATCH 19/19] Update src/hooks/offramp/useSubmitOfframp.ts Co-authored-by: Marcel Ebert --- src/hooks/offramp/useSubmitOfframp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index 8336066e..057b40c4 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -129,7 +129,7 @@ export const useSubmitOfframp = () => { console.error('Error initializing the offramping process', (error as Error).message); // Display error message, differentiating between user rejection and other errors if ((error as Error).message.includes('User rejected')) { - showToast(ToastMessage.ERROR, 'You must sign the login request to offramp Argentine Peso'); + showToast(ToastMessage.ERROR, 'You must sign the login request to be able to sell Argentine Peso'); } else { setInitializeFailed(); }