From e6a3cdccacf761bbe101c2991f8231eaac3b1d4d Mon Sep 17 00:00:00 2001 From: Gianfranco Date: Wed, 21 Aug 2024 11:32:01 -0300 Subject: [PATCH 01/25] modify autodeploy.yaml --- .github/workflows/autodeploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/autodeploy.yaml b/.github/workflows/autodeploy.yaml index 7f551d24..ad8c2b1d 100644 --- a/.github/workflows/autodeploy.yaml +++ b/.github/workflows/autodeploy.yaml @@ -3,7 +3,7 @@ run-name: Gitlab Pipeline Executor on: push: branches: - - polygon-prototype-staging + - polygon-prototype jobs: Execute-Gitlab-Pipeline: runs-on: ubuntu-latest @@ -13,4 +13,4 @@ jobs: - name: Triggers Gitlab Pipeline run: | ls ${{ github.workspace }} - curl -X POST -F token=${{ secrets.GITLAB_TRIGGER }} -F "ref=development" -F "variables[PRODUCTION]=N" -F "variables[STAGING]=N" -F "variables[PRODPOL]=N" -F "variables[STAGPOL]=Y" https://gitlab.com/api/v4/projects/${{ secrets.PROJECT_ID }}/trigger/pipeline + curl -X POST -F token=${{ secrets.GITLAB_TRIGGER }} -F "ref=development" -F "variables[PRODUCTION]=N" -F "variables[STAGING]=N" -F "variables[PRODPOL]=Y" -F "variables[STAGPOL]=N" https://gitlab.com/api/v4/projects/${{ secrets.PROJECT_ID }}/trigger/pipeline From 6cb8f38a95333476749b34b70326893249ecbba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20St=C3=BCber?= <15174476+TorstenStueber@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:12:42 +0100 Subject: [PATCH 02/25] Update the Mykobo home domain --- signer-service/src/constants/tokenConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signer-service/src/constants/tokenConfig.js b/signer-service/src/constants/tokenConfig.js index 9fe1efd3..75948a35 100644 --- a/signer-service/src/constants/tokenConfig.js +++ b/signer-service/src/constants/tokenConfig.js @@ -6,7 +6,7 @@ const TOKEN_CONFIG = { vaultAccountId: '6bsD97dS8ZyomMmp1DLCnCtx25oABtf19dypQKdZe6FBQXSm', minWithdrawalAmount: '10000000000000', maximumSubsidyAmountRaw: '1000000000000', // 1 unit - homeDomain: 'mykobo.co', + homeDomain: 'circle.anchor.mykobo.co', clientDomainEnabled: true, pendulumCurrencyId: { Stellar: { From fb37af3ec2804b9bd318f592147ca6174f38a83c Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 12 Dec 2024 23:35:07 +0100 Subject: [PATCH 03/25] add useOfframpingEvents custom hook --- src/hooks/offramp/useMainProcess.ts | 36 ++++----------------- src/hooks/offramp/useOfframpingEvents.ts | 41 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 src/hooks/offramp/useOfframpingEvents.ts diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index 22260e88..ba1e1500 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -2,16 +2,10 @@ import { useState, useEffect, useCallback, StateUpdater } from 'preact/compat'; import { useConfig } from 'wagmi'; import Big from 'big.js'; -import { EventStatus, GenericEvent } from '../../components/GenericEvent'; - -import { getInputTokenDetailsOrDefault, InputTokenType, OutputTokenType } from '../../constants/tokenConfig'; -import { OFFRAMPING_PHASE_SECONDS } from '../../pages/progress'; - -import { createTransactionEvent, useEventsContext } from '../../contexts/events'; +import { InputTokenType, OutputTokenType } from '../../constants/tokenConfig'; import { useAssetHubNode, usePendulumNode } from '../../contexts/polkadotNode'; import { usePolkadotWalletState } from '../../contexts/polkadotWallet'; import { useNetwork } from '../../contexts/network'; - import { clearOfframpingState, recoverFromFailure, @@ -22,6 +16,7 @@ import { import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; +import { useOfframpingEvents } from './useOfframpingEvents'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -46,9 +41,9 @@ export const useMainProcess = () => { const { apiComponents: assetHubNode } = useAssetHubNode(); const wagmiConfig = useConfig(); - const { trackEvent, resetUniqueEvents } = useEventsContext(); - const [, setEvents] = useState([]); + const { addEvent, trackOfframpingEvent, trackEvent, resetUniqueEvents } = useOfframpingEvents(selectedNetwork); + const { firstSep24IntervalRef, firstSep24Response, @@ -79,23 +74,9 @@ export const useMainProcess = () => { setOfframpingState(state); - if (state?.phase === 'success') { - trackEvent(createTransactionEvent('transaction_success', state, selectedNetwork)); - } else if (state?.failure !== undefined) { - const currentPhase = state?.phase; - const currentPhaseIndex = Object.keys(OFFRAMPING_PHASE_SECONDS).indexOf(currentPhase); - - trackEvent({ - ...createTransactionEvent('transaction_failure', state, selectedNetwork), - event: 'transaction_failure', - phase_name: currentPhase, - phase_index: currentPhaseIndex, - from_asset: getInputTokenDetailsOrDefault(selectedNetwork, state.inputTokenType).assetSymbol, - error_message: state.failure.message, - }); - } + trackOfframpingEvent(state); }, - [trackEvent, selectedNetwork], + [trackOfframpingEvent], ); useEffect(() => { @@ -103,11 +84,6 @@ export const useMainProcess = () => { updateHookStateFromState(state); }, [updateHookStateFromState]); - const addEvent = (message: string, status: EventStatus) => { - console.log('Add event', message, status); - setEvents((prevEvents) => [...prevEvents, { value: message, status }]); - }; - const resetOfframpingState = useCallback(() => { setOfframpingState(undefined); setOfframpingStarted(false); diff --git a/src/hooks/offramp/useOfframpingEvents.ts b/src/hooks/offramp/useOfframpingEvents.ts new file mode 100644 index 00000000..1165a426 --- /dev/null +++ b/src/hooks/offramp/useOfframpingEvents.ts @@ -0,0 +1,41 @@ +import { EventStatus } from '../../components/GenericEvent'; +import { createTransactionEvent } from '../../contexts/events'; + +import { useCallback, useState } from 'preact/compat'; +import { Networks } from '../../contexts/network'; +import { useEventsContext } from '../../contexts/events'; +import { OfframpingState } from '../../services/offrampingFlow'; +import { GenericEvent } from '../../components/GenericEvent'; +import { OFFRAMPING_PHASE_SECONDS } from '../../pages/progress'; +import { getInputTokenDetailsOrDefault } from '../../constants/tokenConfig'; + +export const useOfframpingEvents = (selectedNetwork: Networks) => { + const { trackEvent, resetUniqueEvents } = useEventsContext(); + const [_, setEvents] = useState([]); + + const addEvent = (message: string, status: EventStatus) => { + setEvents((prevEvents) => [...prevEvents, { value: message, status }]); + }; + + const trackOfframpingEvent = useCallback( + (state: OfframpingState | undefined) => { + if (!state) return; + + if (state.phase === 'success') { + trackEvent(createTransactionEvent('transaction_success', state, selectedNetwork)); + } else if (state.failure) { + trackEvent({ + ...createTransactionEvent('transaction_failure', state, selectedNetwork), + event: 'transaction_failure', + phase_name: state.phase, + phase_index: Object.keys(OFFRAMPING_PHASE_SECONDS).indexOf(state.phase), + from_asset: getInputTokenDetailsOrDefault(selectedNetwork, state.inputTokenType).assetSymbol, + error_message: state.failure.message, + }); + } + }, + [trackEvent, selectedNetwork], + ); + + return { addEvent, trackOfframpingEvent, trackEvent, resetUniqueEvents }; +}; From acb0e80ba2c4ac180b02ab85941ba15ad3fee2c7 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 12 Dec 2024 23:46:10 +0100 Subject: [PATCH 04/25] add useOfframpingAdvancement custom hook --- src/hooks/offramp/useMainProcess.ts | 62 ++++--------------- src/hooks/offramp/useOfframpingAdvancement.ts | 55 ++++++++++++++++ src/hooks/offramp/useOfframpingReset.ts | 31 ++++++++++ 3 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 src/hooks/offramp/useOfframpingAdvancement.ts create mode 100644 src/hooks/offramp/useOfframpingReset.ts diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index ba1e1500..233bb5dc 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -1,22 +1,21 @@ import { useState, useEffect, useCallback, StateUpdater } from 'preact/compat'; -import { useConfig } from 'wagmi'; import Big from 'big.js'; import { InputTokenType, OutputTokenType } from '../../constants/tokenConfig'; -import { useAssetHubNode, usePendulumNode } from '../../contexts/polkadotNode'; -import { usePolkadotWalletState } from '../../contexts/polkadotWallet'; +import { usePendulumNode } from '../../contexts/polkadotNode'; import { useNetwork } from '../../contexts/network'; import { clearOfframpingState, recoverFromFailure, readCurrentState, - advanceOfframpingState, OfframpingState, } from '../../services/offrampingFlow'; import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; import { useOfframpingEvents } from './useOfframpingEvents'; +import { useOfframpingReset } from './useOfframpingReset'; +import { useOfframpingAdvancement } from './useOfframpingAdvancement'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -35,14 +34,10 @@ export const useMainProcess = () => { const [signingPhase, setSigningPhase] = useState(undefined); const { selectedNetwork, setOnSelectedNetworkChange } = useNetwork(); - const { walletAccount } = usePolkadotWalletState(); const { apiComponents: pendulumNode } = usePendulumNode(); - const { apiComponents: assetHubNode } = useAssetHubNode(); - const wagmiConfig = useConfig(); - - const { addEvent, trackOfframpingEvent, trackEvent, resetUniqueEvents } = useOfframpingEvents(selectedNetwork); + const { addEvent, trackOfframpingEvent, resetUniqueEvents } = useOfframpingEvents(selectedNetwork); const { firstSep24IntervalRef, @@ -73,7 +68,6 @@ export const useMainProcess = () => { } setOfframpingState(state); - trackOfframpingEvent(state); }, [trackOfframpingEvent], @@ -84,17 +78,7 @@ export const useMainProcess = () => { updateHookStateFromState(state); }, [updateHookStateFromState]); - const resetOfframpingState = useCallback(() => { - setOfframpingState(undefined); - setOfframpingStarted(false); - setIsInitiating(false); - setAnchorSessionParams(undefined); - setFirstSep24Response(undefined); - setExecutionInput(undefined); - cleanSep24FirstVariables(); - clearOfframpingState(); - setSigningPhase(undefined); - }, [ + const resetOfframpingState = useOfframpingReset({ setOfframpingState, setOfframpingStarted, setIsInitiating, @@ -103,7 +87,7 @@ export const useMainProcess = () => { setExecutionInput, cleanSep24FirstVariables, setSigningPhase, - ]); + }); useEffect(() => { setOnSelectedNetworkChange(resetOfframpingState); @@ -132,34 +116,12 @@ export const useMainProcess = () => { updateHookStateFromState(nextState); }, [updateHookStateFromState, offrampingState]); - useEffect(() => { - if (wagmiConfig.state.status !== 'connected') return; - - (async () => { - if (!pendulumNode || !assetHubNode) { - console.error('Polkadot nodes not initialized'); - return; - } - - const nextState = await advanceOfframpingState(offrampingState, { - renderEvent: addEvent, - wagmiConfig, - setSigningPhase, - trackEvent, - pendulumNode, - assetHubNode, - walletAccount, - }); - - if (JSON.stringify(offrampingState) !== JSON.stringify(nextState)) { - updateHookStateFromState(nextState); - } - })(); - // This effect has dependencies that are used inside the async function (assetHubNode, pendulumNode, walletAccount) - // but we intentionally exclude them from the dependency array to prevent unnecessary re-renders. - // These dependencies are stable and won't change during the lifecycle of this hook. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [offrampingState, trackEvent, updateHookStateFromState, wagmiConfig]); + useOfframpingAdvancement({ + offrampingState, + updateHookStateFromState, + addEvent, + setSigningPhase, + }); const maybeCancelSep24First = useCallback(() => { if (firstSep24IntervalRef.current !== undefined) { diff --git a/src/hooks/offramp/useOfframpingAdvancement.ts b/src/hooks/offramp/useOfframpingAdvancement.ts new file mode 100644 index 00000000..6e4c81c0 --- /dev/null +++ b/src/hooks/offramp/useOfframpingAdvancement.ts @@ -0,0 +1,55 @@ +import { StateUpdater } from 'preact/hooks'; + +import { useAssetHubNode } from '../../contexts/polkadotNode'; + +import { EventStatus } from '../../components/GenericEvent'; +import { usePendulumNode } from '../../contexts/polkadotNode'; +import { advanceOfframpingState, OfframpingState } from '../../services/offrampingFlow'; +import { SigningPhase } from './useMainProcess'; +import { usePolkadotWalletState } from '../../contexts/polkadotWallet'; +import { useConfig } from 'wagmi'; +import { useEffect } from 'preact/hooks'; +import { useEventsContext } from '../../contexts/events'; + +export const useOfframpingAdvancement = (deps: { + offrampingState: OfframpingState | undefined; + updateHookStateFromState: (state: OfframpingState | undefined) => void; + addEvent: (message: string, status: EventStatus) => void; + setSigningPhase: StateUpdater; +}) => { + const { offrampingState, updateHookStateFromState, addEvent, setSigningPhase } = deps; + const { trackEvent } = useEventsContext(); + const wagmiConfig = useConfig(); + const { apiComponents: pendulumNode } = usePendulumNode(); + const { apiComponents: assetHubNode } = useAssetHubNode(); + const { walletAccount } = usePolkadotWalletState(); + + useEffect(() => { + if (wagmiConfig.state.status !== 'connected') return; + + (async () => { + if (!pendulumNode || !assetHubNode) { + console.error('Polkadot nodes not initialized'); + return; + } + + const nextState = await advanceOfframpingState(offrampingState, { + renderEvent: addEvent, + wagmiConfig, + setSigningPhase, + trackEvent, + pendulumNode, + assetHubNode, + walletAccount, + }); + + if (JSON.stringify(offrampingState) !== JSON.stringify(nextState)) { + updateHookStateFromState(nextState); + } + })(); + // This effect has dependencies that are used inside the async function (assetHubNode, pendulumNode, walletAccount) + // but we intentionally exclude them from the dependency array to prevent unnecessary re-renders. + // These dependencies are stable and won't change during the lifecycle of this hook. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [offrampingState, trackEvent, updateHookStateFromState, wagmiConfig]); +}; diff --git a/src/hooks/offramp/useOfframpingReset.ts b/src/hooks/offramp/useOfframpingReset.ts new file mode 100644 index 00000000..41117e1c --- /dev/null +++ b/src/hooks/offramp/useOfframpingReset.ts @@ -0,0 +1,31 @@ +import { StateUpdater, useCallback } from 'preact/hooks'; + +import { clearOfframpingState, OfframpingState } from '../../services/offrampingFlow'; +import { IAnchorSessionParams, ISep24Intermediate } from '../../services/anchor'; +import { SigningPhase } from './useMainProcess'; +import { ExtendedExecutionInput } from './useSEP24/useSEP24State'; + +export const useOfframpingReset = (deps: { + setOfframpingState: StateUpdater; + setOfframpingStarted: StateUpdater; + setIsInitiating: StateUpdater; + setAnchorSessionParams: (params: IAnchorSessionParams | undefined) => void; + setFirstSep24Response: (response: ISep24Intermediate | undefined) => void; + setExecutionInput: (input: ExtendedExecutionInput | undefined) => void; + cleanSep24FirstVariables: () => void; + setSigningPhase: StateUpdater; +}) => { + const resetOfframpingState = useCallback(() => { + deps.setAnchorSessionParams(undefined); + deps.setFirstSep24Response(undefined); + deps.setExecutionInput(undefined); + deps.setOfframpingState(undefined); + deps.setSigningPhase(undefined); + deps.setOfframpingStarted(false); + deps.setIsInitiating(false); + deps.cleanSep24FirstVariables(); + clearOfframpingState(); + }, [deps]); + + return resetOfframpingState; +}; From ebaf88609c46d44fcc824b2846c21390d28de7f1 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 13 Dec 2024 00:31:54 +0100 Subject: [PATCH 05/25] reduce cognitive load of useMainProcess hook --- src/hooks/offramp/useMainProcess.ts | 105 +++++++----------- .../useSEP24/useAnchorWindowHandler.ts | 19 +++- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index 233bb5dc..c95a7dca 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -2,7 +2,6 @@ import { useState, useEffect, useCallback, StateUpdater } from 'preact/compat'; import Big from 'big.js'; import { InputTokenType, OutputTokenType } from '../../constants/tokenConfig'; -import { usePendulumNode } from '../../contexts/polkadotNode'; import { useNetwork } from '../../contexts/network'; import { clearOfframpingState, @@ -28,118 +27,96 @@ export interface ExecutionInput { } export const useMainProcess = () => { + // State management const [offrampingStarted, setOfframpingStarted] = useState(false); const [isInitiating, setIsInitiating] = useState(false); const [offrampingState, setOfframpingState] = useState(undefined); const [signingPhase, setSigningPhase] = useState(undefined); + // Context const { selectedNetwork, setOnSelectedNetworkChange } = useNetwork(); - const { apiComponents: pendulumNode } = usePendulumNode(); - - const { addEvent, trackOfframpingEvent, resetUniqueEvents } = useOfframpingEvents(selectedNetwork); - - const { - firstSep24IntervalRef, - firstSep24Response, - setFirstSep24Response, - setExecutionInput, - setAnchorSessionParams, - cleanSep24FirstVariables, - handleOnAnchorWindowOpen: sep24HandleOnAnchorWindowOpen, - } = useSEP24(); - - const handleOnSubmit = useSubmitOfframp({ - firstSep24IntervalRef, - setFirstSep24Response, - setExecutionInput, - setAnchorSessionParams, - cleanSep24FirstVariables, - offrampingStarted, - offrampingState, - setOfframpingStarted, - setIsInitiating, - }); + const events = useOfframpingEvents(selectedNetwork); + const sep24 = useSEP24(); const updateHookStateFromState = useCallback( (state: OfframpingState | undefined) => { - if (state === undefined || state.phase === 'success' || state.failure !== undefined) { + if (!state || state.phase === 'success' || state.failure !== undefined) { setSigningPhase(undefined); } - setOfframpingState(state); - trackOfframpingEvent(state); + events.trackOfframpingEvent(state); }, - [trackOfframpingEvent], + [events], ); + // Initialize state from storage useEffect(() => { - const state = readCurrentState(); - updateHookStateFromState(state); + updateHookStateFromState(readCurrentState()); }, [updateHookStateFromState]); + // Reset handlers const resetOfframpingState = useOfframpingReset({ setOfframpingState, setOfframpingStarted, setIsInitiating, - setAnchorSessionParams, - setFirstSep24Response, - setExecutionInput, - cleanSep24FirstVariables, + setAnchorSessionParams: sep24.setAnchorSessionParams, + setFirstSep24Response: sep24.setFirstSep24Response, + setExecutionInput: sep24.setExecutionInput, + cleanSep24FirstVariables: sep24.cleanSep24FirstVariables, setSigningPhase, }); + // Reset offramping state when the network is changed useEffect(() => { setOnSelectedNetworkChange(resetOfframpingState); }, [setOnSelectedNetworkChange, resetOfframpingState]); - const handleOnAnchorWindowOpen = useCallback(async () => { - if (!pendulumNode) { - console.error('Pendulum node not initialized'); - return; - } - - await sep24HandleOnAnchorWindowOpen(selectedNetwork, setOfframpingStarted, updateHookStateFromState, pendulumNode); - }, [selectedNetwork, setOfframpingStarted, updateHookStateFromState, pendulumNode, sep24HandleOnAnchorWindowOpen]); + const handleOnSubmit = useSubmitOfframp({ + ...sep24, + offrampingStarted, + offrampingState, + setOfframpingStarted, + setIsInitiating, + }); - const finishOfframping = useCallback(() => { - (async () => { - await clearOfframpingState(); - resetUniqueEvents(); - setOfframpingStarted(false); - updateHookStateFromState(undefined); - })(); - }, [updateHookStateFromState, resetUniqueEvents]); + // Flow control handlers + const finishOfframping = useCallback(async () => { + await clearOfframpingState(); + events.resetUniqueEvents(); + setOfframpingStarted(false); + updateHookStateFromState(undefined); + }, [events, updateHookStateFromState]); const continueFailedFlow = useCallback(() => { - const nextState = recoverFromFailure(offrampingState); - updateHookStateFromState(nextState); + updateHookStateFromState(recoverFromFailure(offrampingState)); }, [updateHookStateFromState, offrampingState]); + const maybeCancelSep24First = useCallback(() => { + if (sep24.firstSep24IntervalRef.current !== undefined) { + setOfframpingStarted(false); + sep24.cleanSep24FirstVariables(); + } + }, [sep24]); + + // Determines the current offramping phase useOfframpingAdvancement({ offrampingState, updateHookStateFromState, - addEvent, + addEvent: events.addEvent, setSigningPhase, }); - const maybeCancelSep24First = useCallback(() => { - if (firstSep24IntervalRef.current !== undefined) { - setOfframpingStarted(false); - cleanSep24FirstVariables(); - } - }, [firstSep24IntervalRef, cleanSep24FirstVariables]); - return { handleOnSubmit, - firstSep24ResponseState: firstSep24Response, + firstSep24ResponseState: sep24.firstSep24Response, offrampingState, offrampingStarted, isInitiating, setIsInitiating, finishOfframping, continueFailedFlow, - handleOnAnchorWindowOpen, + handleOnAnchorWindowOpen: sep24.handleOnAnchorWindowOpen, signingPhase, maybeCancelSep24First, }; diff --git a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts index f8c28bf4..a50d6ba3 100644 --- a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts +++ b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts @@ -1,5 +1,4 @@ import { useCallback } from 'preact/compat'; -import { ApiPromise } from '@polkadot/api'; import Big from 'big.js'; import { Networks } from '../../../contexts/network'; @@ -11,6 +10,7 @@ import { showToast, ToastMessage } from '../../../helpers/notifications'; import { UseSEP24StateReturn } from './useSEP24State'; import { useTrackSEP24Events } from './useTrackSEP24Events'; +import { usePendulumNode } from '../../../contexts/polkadotNode'; const handleAmountMismatch = (setOfframpingStarted: (started: boolean) => void): void => { setOfframpingStarted(false); @@ -24,6 +24,7 @@ const handleError = (error: unknown, setOfframpingStarted: (started: boolean) => export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupFn: () => void) => { const { trackKYCStarted, trackKYCCompleted } = useTrackSEP24Events(); + const { apiComponents: pendulumNode } = usePendulumNode(); const { firstSep24Response, anchorSessionParams, executionInput } = sep24State; return useCallback( @@ -31,12 +32,16 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF selectedNetwork: Networks, setOfframpingStarted: (started: boolean) => void, updateHookStateFromState: (state: OfframpingState | undefined) => void, - pendulumNode: { ss58Format: number; api: ApiPromise; decimals: number }, ) => { if (!firstSep24Response || !anchorSessionParams || !executionInput) { return; } + if (!pendulumNode) { + console.error('Pendulum node not initialized'); + return; + } + trackKYCStarted(executionInput, selectedNetwork); cleanupFn(); @@ -66,6 +71,14 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF handleError(error, setOfframpingStarted); } }, - [firstSep24Response, anchorSessionParams, executionInput, trackKYCStarted, cleanupFn, trackKYCCompleted], + [ + firstSep24Response, + anchorSessionParams, + executionInput, + pendulumNode, + trackKYCStarted, + cleanupFn, + trackKYCCompleted, + ], ); }; From bd108237426a76b583f5950976a5be5ad3bc0880 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 13 Dec 2024 01:11:08 +0100 Subject: [PATCH 06/25] implement zustand for useMainProcess --- package.json | 3 +- src/hooks/offramp/useMainProcess.ts | 116 ++++++------------ src/hooks/offramp/useOfframpingAdvancement.ts | 31 ++--- src/hooks/offramp/useOfframpingEvents.ts | 16 ++- src/hooks/offramp/useOfframpingReset.ts | 31 ----- .../useSEP24/useAnchorWindowHandler.ts | 100 +++++++-------- src/stores/offrampStore.ts | 62 ++++++++++ src/types/offramp.ts | 40 ++++++ yarn.lock | 22 ++++ 9 files changed, 241 insertions(+), 180 deletions(-) delete mode 100644 src/hooks/offramp/useOfframpingReset.ts create mode 100644 src/stores/offrampStore.ts create mode 100644 src/types/offramp.ts diff --git a/package.json b/package.json index 801c7888..dcc54662 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "viem": "^2.21.43", "wagmi": "^2.12.29", "web3": "^4.10.0", - "yup": "^1.4.0" + "yup": "^1.4.0", + "zustand": "^5.0.2" }, "devDependencies": { "@babel/core": "^7.20.12", diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index c95a7dca..f843577d 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -1,20 +1,15 @@ -import { useState, useEffect, useCallback, StateUpdater } from 'preact/compat'; +import { useEffect, StateUpdater } from 'preact/compat'; import Big from 'big.js'; import { InputTokenType, OutputTokenType } from '../../constants/tokenConfig'; import { useNetwork } from '../../contexts/network'; -import { - clearOfframpingState, - recoverFromFailure, - readCurrentState, - OfframpingState, -} from '../../services/offrampingFlow'; +import { recoverFromFailure, readCurrentState } from '../../services/offrampingFlow'; import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; import { useOfframpingEvents } from './useOfframpingEvents'; -import { useOfframpingReset } from './useOfframpingReset'; import { useOfframpingAdvancement } from './useOfframpingAdvancement'; +import { useOfframpStatus, useOfframpStore } from '../../stores/offrampStore'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -27,97 +22,64 @@ export interface ExecutionInput { } export const useMainProcess = () => { - // State management - const [offrampingStarted, setOfframpingStarted] = useState(false); - const [isInitiating, setIsInitiating] = useState(false); - const [offrampingState, setOfframpingState] = useState(undefined); - const [signingPhase, setSigningPhase] = useState(undefined); + // State + const { offrampingStarted, isInitiating, offrampingState, signingPhase } = useOfframpStatus(); - // Context - const { selectedNetwork, setOnSelectedNetworkChange } = useNetwork(); + // State updater + const { setOfframpingStarted, setIsInitiating, resetState, updateHookStateFromState } = useOfframpStore(); - const events = useOfframpingEvents(selectedNetwork); - const sep24 = useSEP24(); + // Contexts + const { setOnSelectedNetworkChange } = useNetwork(); - const updateHookStateFromState = useCallback( - (state: OfframpingState | undefined) => { - if (!state || state.phase === 'success' || state.failure !== undefined) { - setSigningPhase(undefined); - } - setOfframpingState(state); - events.trackOfframpingEvent(state); - }, - [events], - ); + // Custom hooks + const events = useOfframpingEvents(); + const sep24 = useSEP24(); // Initialize state from storage useEffect(() => { - updateHookStateFromState(readCurrentState()); - }, [updateHookStateFromState]); - - // Reset handlers - const resetOfframpingState = useOfframpingReset({ - setOfframpingState, - setOfframpingStarted, - setIsInitiating, - setAnchorSessionParams: sep24.setAnchorSessionParams, - setFirstSep24Response: sep24.setFirstSep24Response, - setExecutionInput: sep24.setExecutionInput, - cleanSep24FirstVariables: sep24.cleanSep24FirstVariables, - setSigningPhase, - }); + const recoveryState = readCurrentState(); + updateHookStateFromState(recoveryState); + events.trackOfframpingEvent(recoveryState); + }, [updateHookStateFromState, events]); // Reset offramping state when the network is changed useEffect(() => { - setOnSelectedNetworkChange(resetOfframpingState); - }, [setOnSelectedNetworkChange, resetOfframpingState]); - - const handleOnSubmit = useSubmitOfframp({ - ...sep24, - offrampingStarted, - offrampingState, - setOfframpingStarted, - setIsInitiating, - }); - - // Flow control handlers - const finishOfframping = useCallback(async () => { - await clearOfframpingState(); - events.resetUniqueEvents(); - setOfframpingStarted(false); - updateHookStateFromState(undefined); - }, [events, updateHookStateFromState]); - - const continueFailedFlow = useCallback(() => { - updateHookStateFromState(recoverFromFailure(offrampingState)); - }, [updateHookStateFromState, offrampingState]); - - const maybeCancelSep24First = useCallback(() => { - if (sep24.firstSep24IntervalRef.current !== undefined) { - setOfframpingStarted(false); - sep24.cleanSep24FirstVariables(); - } - }, [sep24]); + setOnSelectedNetworkChange(resetState); + }, [setOnSelectedNetworkChange, resetState]); // Determines the current offramping phase useOfframpingAdvancement({ - offrampingState, - updateHookStateFromState, addEvent: events.addEvent, - setSigningPhase, }); return { - handleOnSubmit, + handleOnSubmit: useSubmitOfframp({ + ...sep24, + offrampingStarted, + offrampingState, + setOfframpingStarted, + setIsInitiating, + }), firstSep24ResponseState: sep24.firstSep24Response, offrampingState, offrampingStarted, isInitiating, setIsInitiating, - finishOfframping, - continueFailedFlow, + finishOfframping: () => { + events.resetUniqueEvents(); + resetState(); + }, + continueFailedFlow: () => { + updateHookStateFromState(recoverFromFailure(offrampingState)); + }, handleOnAnchorWindowOpen: sep24.handleOnAnchorWindowOpen, signingPhase, - maybeCancelSep24First, + // @todo: why do we need this? + maybeCancelSep24First: () => { + if (sep24.firstSep24IntervalRef.current !== undefined) { + setOfframpingStarted(false); + sep24.cleanSep24FirstVariables(); + } + }, }; }; diff --git a/src/hooks/offramp/useOfframpingAdvancement.ts b/src/hooks/offramp/useOfframpingAdvancement.ts index 6e4c81c0..21f45e3c 100644 --- a/src/hooks/offramp/useOfframpingAdvancement.ts +++ b/src/hooks/offramp/useOfframpingAdvancement.ts @@ -1,28 +1,29 @@ -import { StateUpdater } from 'preact/hooks'; - -import { useAssetHubNode } from '../../contexts/polkadotNode'; +import { useEffect } from 'preact/hooks'; +import { useConfig } from 'wagmi'; +import { advanceOfframpingState } from '../../services/offrampingFlow'; import { EventStatus } from '../../components/GenericEvent'; -import { usePendulumNode } from '../../contexts/polkadotNode'; -import { advanceOfframpingState, OfframpingState } from '../../services/offrampingFlow'; -import { SigningPhase } from './useMainProcess'; + import { usePolkadotWalletState } from '../../contexts/polkadotWallet'; -import { useConfig } from 'wagmi'; -import { useEffect } from 'preact/hooks'; +import { useAssetHubNode } from '../../contexts/polkadotNode'; +import { usePendulumNode } from '../../contexts/polkadotNode'; import { useEventsContext } from '../../contexts/events'; -export const useOfframpingAdvancement = (deps: { - offrampingState: OfframpingState | undefined; - updateHookStateFromState: (state: OfframpingState | undefined) => void; +import { useOfframpStore } from '../../stores/offrampStore'; + +interface AdvancementDeps { addEvent: (message: string, status: EventStatus) => void; - setSigningPhase: StateUpdater; -}) => { - const { offrampingState, updateHookStateFromState, addEvent, setSigningPhase } = deps; +} + +export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { + const { walletAccount } = usePolkadotWalletState(); const { trackEvent } = useEventsContext(); const wagmiConfig = useConfig(); + const { apiComponents: pendulumNode } = usePendulumNode(); const { apiComponents: assetHubNode } = useAssetHubNode(); - const { walletAccount } = usePolkadotWalletState(); + + const { offrampingState, updateHookStateFromState, setSigningPhase } = useOfframpStore(); useEffect(() => { if (wagmiConfig.state.status !== 'connected') return; diff --git a/src/hooks/offramp/useOfframpingEvents.ts b/src/hooks/offramp/useOfframpingEvents.ts index 1165a426..06f70971 100644 --- a/src/hooks/offramp/useOfframpingEvents.ts +++ b/src/hooks/offramp/useOfframpingEvents.ts @@ -1,18 +1,22 @@ +import { useCallback, useState } from 'preact/compat'; + import { EventStatus } from '../../components/GenericEvent'; -import { createTransactionEvent } from '../../contexts/events'; +import { GenericEvent } from '../../components/GenericEvent'; -import { useCallback, useState } from 'preact/compat'; -import { Networks } from '../../contexts/network'; +import { createTransactionEvent } from '../../contexts/events'; import { useEventsContext } from '../../contexts/events'; +import { useNetwork } from '../../contexts/network'; + +import { getInputTokenDetailsOrDefault } from '../../constants/tokenConfig'; import { OfframpingState } from '../../services/offrampingFlow'; -import { GenericEvent } from '../../components/GenericEvent'; import { OFFRAMPING_PHASE_SECONDS } from '../../pages/progress'; -import { getInputTokenDetailsOrDefault } from '../../constants/tokenConfig'; -export const useOfframpingEvents = (selectedNetwork: Networks) => { +export const useOfframpingEvents = () => { const { trackEvent, resetUniqueEvents } = useEventsContext(); + const { selectedNetwork } = useNetwork(); const [_, setEvents] = useState([]); + // @todo: why do we need this? const addEvent = (message: string, status: EventStatus) => { setEvents((prevEvents) => [...prevEvents, { value: message, status }]); }; diff --git a/src/hooks/offramp/useOfframpingReset.ts b/src/hooks/offramp/useOfframpingReset.ts deleted file mode 100644 index 41117e1c..00000000 --- a/src/hooks/offramp/useOfframpingReset.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { StateUpdater, useCallback } from 'preact/hooks'; - -import { clearOfframpingState, OfframpingState } from '../../services/offrampingFlow'; -import { IAnchorSessionParams, ISep24Intermediate } from '../../services/anchor'; -import { SigningPhase } from './useMainProcess'; -import { ExtendedExecutionInput } from './useSEP24/useSEP24State'; - -export const useOfframpingReset = (deps: { - setOfframpingState: StateUpdater; - setOfframpingStarted: StateUpdater; - setIsInitiating: StateUpdater; - setAnchorSessionParams: (params: IAnchorSessionParams | undefined) => void; - setFirstSep24Response: (response: ISep24Intermediate | undefined) => void; - setExecutionInput: (input: ExtendedExecutionInput | undefined) => void; - cleanSep24FirstVariables: () => void; - setSigningPhase: StateUpdater; -}) => { - const resetOfframpingState = useCallback(() => { - deps.setAnchorSessionParams(undefined); - deps.setFirstSep24Response(undefined); - deps.setExecutionInput(undefined); - deps.setOfframpingState(undefined); - deps.setSigningPhase(undefined); - deps.setOfframpingStarted(false); - deps.setIsInitiating(false); - deps.cleanSep24FirstVariables(); - clearOfframpingState(); - }, [deps]); - - return resetOfframpingState; -}; diff --git a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts index a50d6ba3..6548b131 100644 --- a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts +++ b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts @@ -1,9 +1,9 @@ import { useCallback } from 'preact/compat'; import Big from 'big.js'; -import { Networks } from '../../../contexts/network'; +import { useNetwork } from '../../../contexts/network'; -import { constructInitialState, OfframpingState } from '../../../services/offrampingFlow'; +import { constructInitialState } from '../../../services/offrampingFlow'; import { sep24Second } from '../../../services/anchor'; import { showToast, ToastMessage } from '../../../helpers/notifications'; @@ -11,6 +11,7 @@ import { showToast, ToastMessage } from '../../../helpers/notifications'; import { UseSEP24StateReturn } from './useSEP24State'; import { useTrackSEP24Events } from './useTrackSEP24Events'; import { usePendulumNode } from '../../../contexts/polkadotNode'; +import { useOfframpStore } from '../../../stores/offrampStore'; const handleAmountMismatch = (setOfframpingStarted: (started: boolean) => void): void => { setOfframpingStarted(false); @@ -24,61 +25,60 @@ const handleError = (error: unknown, setOfframpingStarted: (started: boolean) => export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupFn: () => void) => { const { trackKYCStarted, trackKYCCompleted } = useTrackSEP24Events(); + const { selectedNetwork } = useNetwork(); const { apiComponents: pendulumNode } = usePendulumNode(); + const { setOfframpingStarted, updateHookStateFromState } = useOfframpStore(); + const { firstSep24Response, anchorSessionParams, executionInput } = sep24State; - return useCallback( - async ( - selectedNetwork: Networks, - setOfframpingStarted: (started: boolean) => void, - updateHookStateFromState: (state: OfframpingState | undefined) => void, - ) => { - if (!firstSep24Response || !anchorSessionParams || !executionInput) { - return; - } + return useCallback(async () => { + if (!firstSep24Response || !anchorSessionParams || !executionInput) { + return; + } - if (!pendulumNode) { - console.error('Pendulum node not initialized'); - return; - } + if (!pendulumNode) { + console.error('Pendulum node not initialized'); + return; + } - trackKYCStarted(executionInput, selectedNetwork); - cleanupFn(); + trackKYCStarted(executionInput, selectedNetwork); + cleanupFn(); - try { - const secondSep24Response = await sep24Second(firstSep24Response, anchorSessionParams); + try { + const secondSep24Response = await sep24Second(firstSep24Response, anchorSessionParams); - if (!Big(secondSep24Response.amount).eq(executionInput.offrampAmount)) { - handleAmountMismatch(setOfframpingStarted); - return; - } + if (!Big(secondSep24Response.amount).eq(executionInput.offrampAmount)) { + handleAmountMismatch(setOfframpingStarted); + return; + } - const initialState = await constructInitialState({ - sep24Id: firstSep24Response.id, - stellarEphemeralSecret: executionInput.stellarEphemeralSecret, - inputTokenType: executionInput.inputTokenType, - outputTokenType: executionInput.outputTokenType, - amountIn: executionInput.amountInUnits, - amountOut: executionInput.offrampAmount, - sepResult: secondSep24Response, - network: selectedNetwork, - pendulumNode, - }); + const initialState = await constructInitialState({ + sep24Id: firstSep24Response.id, + stellarEphemeralSecret: executionInput.stellarEphemeralSecret, + inputTokenType: executionInput.inputTokenType, + outputTokenType: executionInput.outputTokenType, + amountIn: executionInput.amountInUnits, + amountOut: executionInput.offrampAmount, + sepResult: secondSep24Response, + network: selectedNetwork, + pendulumNode, + }); - trackKYCCompleted(initialState, selectedNetwork); - updateHookStateFromState(initialState); - } catch (error) { - handleError(error, setOfframpingStarted); - } - }, - [ - firstSep24Response, - anchorSessionParams, - executionInput, - pendulumNode, - trackKYCStarted, - cleanupFn, - trackKYCCompleted, - ], - ); + trackKYCCompleted(initialState, selectedNetwork); + updateHookStateFromState(initialState); + } catch (error) { + handleError(error, setOfframpingStarted); + } + }, [ + firstSep24Response, + anchorSessionParams, + executionInput, + pendulumNode, + trackKYCStarted, + cleanupFn, + trackKYCCompleted, + selectedNetwork, + setOfframpingStarted, + updateHookStateFromState, + ]); }; diff --git a/src/stores/offrampStore.ts b/src/stores/offrampStore.ts new file mode 100644 index 00000000..7ff72fbe --- /dev/null +++ b/src/stores/offrampStore.ts @@ -0,0 +1,62 @@ +import { create } from 'zustand'; +import { OfframpState, OfframpActions } from '../types/offramp'; +import { clearOfframpingState, recoverFromFailure, readCurrentState } from '../services/offrampingFlow'; + +interface OfframpStore extends OfframpState, OfframpActions {} + +export const useOfframpStore = create()((set, get) => ({ + // Initial state + offrampingStarted: false, + isInitiating: false, + offrampingState: undefined, + signingPhase: undefined, + anchorSessionParams: undefined, + firstSep24Response: undefined, + executionInput: undefined, + + // Simple setters + setOfframpingStarted: (started) => set({ offrampingStarted: started }), + setIsInitiating: (initiating) => set({ isInitiating: initiating }), + setOfframpingState: (state) => set({ offrampingState: state }), + setSigningPhase: (phase) => set({ signingPhase: phase }), + + // Complex setters + setSep24Params: (params) => set((state) => ({ ...state, ...params })), + + // Business logic + updateHookStateFromState: (state) => { + if (!state || state.phase === 'success' || state.failure !== undefined) { + set({ signingPhase: undefined }); + } + set({ offrampingState: state }); + }, + + resetState: async () => { + await clearOfframpingState(); + set({ + offrampingStarted: false, + isInitiating: false, + offrampingState: undefined, + signingPhase: undefined, + anchorSessionParams: undefined, + firstSep24Response: undefined, + executionInput: undefined, + }); + }, +})); + +// Selector hooks for better performance +export const useOfframpStatus = () => + useOfframpStore((state) => ({ + offrampingStarted: state.offrampingStarted, + isInitiating: state.isInitiating, + offrampingState: state.offrampingState, + signingPhase: state.signingPhase, + })); + +export const useSep24State = () => + useOfframpStore((state) => ({ + anchorSessionParams: state.anchorSessionParams, + firstSep24Response: state.firstSep24Response, + executionInput: state.executionInput, + })); diff --git a/src/types/offramp.ts b/src/types/offramp.ts new file mode 100644 index 00000000..2150499c --- /dev/null +++ b/src/types/offramp.ts @@ -0,0 +1,40 @@ +import { StateUpdater } from 'preact/hooks'; +import Big from 'big.js'; +import { OfframpingState } from '../services/offrampingFlow'; +import { InputTokenType, OutputTokenType } from '../constants/tokenConfig'; +import { ISep24Intermediate, IAnchorSessionParams } from '../services/anchor'; + +export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; + +export interface ExecutionInput { + inputTokenType: InputTokenType; + outputTokenType: OutputTokenType; + amountInUnits: string; + offrampAmount: Big; + setInitializeFailed: StateUpdater; +} + +export interface OfframpState { + // Core state + offrampingStarted: boolean; + isInitiating: boolean; + offrampingState: OfframpingState | undefined; + signingPhase: SigningPhase | undefined; + + // SEP24 related state + anchorSessionParams: IAnchorSessionParams | undefined; + firstSep24Response: ISep24Intermediate | undefined; + executionInput: ExecutionInput | undefined; +} + +export interface OfframpActions { + setOfframpingStarted: (started: boolean) => void; + setIsInitiating: (initiating: boolean) => void; + setOfframpingState: (state: OfframpingState | undefined) => void; + setSigningPhase: (phase: SigningPhase | undefined) => void; + setSep24Params: ( + params: Partial>, + ) => void; + updateHookStateFromState: (state: OfframpingState | undefined) => void; + resetState: () => void; +} diff --git a/yarn.lock b/yarn.lock index e4afabb4..6ec51468 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15185,6 +15185,7 @@ __metadata: wagmi: "npm:^2.12.29" web3: "npm:^4.10.0" yup: "npm:^1.4.0" + zustand: "npm:^5.0.2" languageName: unknown linkType: soft @@ -15893,3 +15894,24 @@ __metadata: checksum: 10/be75ef4d1b218b143314467bb9e23641231043cad2d5c3a4b2219c46d1609ee799cd8dc9acec9b23d55ec3a2a619a06616e593aea4049f3b7323938af9a33bfe languageName: node linkType: hard + +"zustand@npm:^5.0.2": + version: 5.0.2 + resolution: "zustand@npm:5.0.2" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 10/9fb60796b9770dcc3f78dd794e7f424ff735e5676784cbc9726761037613942b62470b24a9ca9e98534ee4369a3b5429be570ff34281cb3c9d6d4e8df559ec3f + languageName: node + linkType: hard From b8ece4f213c004cea2814e5bfd3149b088286a5b Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 13 Dec 2024 01:35:18 +0100 Subject: [PATCH 07/25] improve zustand config --- src/hooks/offramp/useMainProcess.ts | 20 ++++++++++--------- src/hooks/offramp/useOfframpingAdvancement.ts | 2 ++ src/hooks/offramp/useSubmitOfframp.ts | 11 ++-------- src/stores/offrampStore.ts | 13 ++---------- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index f843577d..81388dd4 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -9,7 +9,7 @@ import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; import { useOfframpingEvents } from './useOfframpingEvents'; import { useOfframpingAdvancement } from './useOfframpingAdvancement'; -import { useOfframpStatus, useOfframpStore } from '../../stores/offrampStore'; +import { useOfframpStore } from '../../stores/offrampStore'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -22,11 +22,17 @@ export interface ExecutionInput { } export const useMainProcess = () => { - // State - const { offrampingStarted, isInitiating, offrampingState, signingPhase } = useOfframpStatus(); - // State updater - const { setOfframpingStarted, setIsInitiating, resetState, updateHookStateFromState } = useOfframpStore(); + const { + setOfframpingStarted, + setIsInitiating, + resetState, + updateHookStateFromState, + offrampingStarted, + isInitiating, + offrampingState, + signingPhase, + } = useOfframpStore(); // Contexts const { setOnSelectedNetworkChange } = useNetwork(); @@ -55,10 +61,6 @@ export const useMainProcess = () => { return { handleOnSubmit: useSubmitOfframp({ ...sep24, - offrampingStarted, - offrampingState, - setOfframpingStarted, - setIsInitiating, }), firstSep24ResponseState: sep24.firstSep24Response, offrampingState, diff --git a/src/hooks/offramp/useOfframpingAdvancement.ts b/src/hooks/offramp/useOfframpingAdvancement.ts index 21f45e3c..44285baa 100644 --- a/src/hooks/offramp/useOfframpingAdvancement.ts +++ b/src/hooks/offramp/useOfframpingAdvancement.ts @@ -48,6 +48,8 @@ export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { updateHookStateFromState(nextState); } })(); + + // @todo: investigate and remove this // This effect has dependencies that are used inside the async function (assetHubNode, pendulumNode, walletAccount) // but we intentionally exclude them from the dependency array to prevent unnecessary re-renders. // These dependencies are stable and won't change during the lifecycle of this hook. diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index 748bf934..dcd960bb 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -16,7 +16,7 @@ import { sep24First, } from '../../services/anchor'; -import { OfframpingState } from '../../services/offrampingFlow'; +import { useOfframpStore } from '../../stores/offrampStore'; import { ExtendedExecutionInput } from './useSEP24/useSEP24State'; import { ExecutionInput } from './useSEP24'; @@ -26,10 +26,6 @@ interface UseSubmitOfframpProps { setExecutionInput: (input: ExtendedExecutionInput | undefined) => void; setAnchorSessionParams: (params: IAnchorSessionParams | undefined) => void; cleanSep24FirstVariables: () => void; - offrampingStarted: boolean; - offrampingState: OfframpingState | undefined; - setOfframpingStarted: (started: boolean) => void; - setIsInitiating: (isInitiating: boolean) => void; } export const useSubmitOfframp = ({ @@ -38,16 +34,13 @@ export const useSubmitOfframp = ({ setExecutionInput, setAnchorSessionParams, cleanSep24FirstVariables, - offrampingStarted, - offrampingState, - setOfframpingStarted, - setIsInitiating, }: UseSubmitOfframpProps) => { const { selectedNetwork } = useNetwork(); const { switchChain } = useSwitchChain(); const { trackEvent } = useEventsContext(); const { address } = useAccount(); const { checkAndWaitForSignature, forceRefreshAndWaitForSignature } = useSiweContext(); + const { offrampingStarted, offrampingState, setOfframpingStarted, setIsInitiating } = useOfframpStore(); const addEvent = (message: string, status: string) => { console.log('Add event', message, status); diff --git a/src/stores/offrampStore.ts b/src/stores/offrampStore.ts index 7ff72fbe..8d18c4d5 100644 --- a/src/stores/offrampStore.ts +++ b/src/stores/offrampStore.ts @@ -1,10 +1,10 @@ import { create } from 'zustand'; import { OfframpState, OfframpActions } from '../types/offramp'; -import { clearOfframpingState, recoverFromFailure, readCurrentState } from '../services/offrampingFlow'; +import { clearOfframpingState } from '../services/offrampingFlow'; interface OfframpStore extends OfframpState, OfframpActions {} -export const useOfframpStore = create()((set, get) => ({ +export const useOfframpStore = create()((set) => ({ // Initial state offrampingStarted: false, isInitiating: false, @@ -45,15 +45,6 @@ export const useOfframpStore = create()((set, get) => ({ }, })); -// Selector hooks for better performance -export const useOfframpStatus = () => - useOfframpStore((state) => ({ - offrampingStarted: state.offrampingStarted, - isInitiating: state.isInitiating, - offrampingState: state.offrampingState, - signingPhase: state.signingPhase, - })); - export const useSep24State = () => useOfframpStore((state) => ({ anchorSessionParams: state.anchorSessionParams, From 06075593b7871889f4026a309dc6a45f3714ccad Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Sun, 15 Dec 2024 12:18:38 +0100 Subject: [PATCH 08/25] refactor offrampStore --- src/stores/offrampStore.ts | 93 ++++++++++++++++++++------------------ src/types/offramp.ts | 38 ++++++++-------- 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/stores/offrampStore.ts b/src/stores/offrampStore.ts index 8d18c4d5..4e33c480 100644 --- a/src/stores/offrampStore.ts +++ b/src/stores/offrampStore.ts @@ -2,52 +2,59 @@ import { create } from 'zustand'; import { OfframpState, OfframpActions } from '../types/offramp'; import { clearOfframpingState } from '../services/offrampingFlow'; -interface OfframpStore extends OfframpState, OfframpActions {} +interface OfframpStore extends OfframpState { + actions: OfframpActions; +} -export const useOfframpStore = create()((set) => ({ +const useOfframpStore = create()((set) => ({ // Initial state - offrampingStarted: false, - isInitiating: false, - offrampingState: undefined, - signingPhase: undefined, - anchorSessionParams: undefined, - firstSep24Response: undefined, - executionInput: undefined, - - // Simple setters - setOfframpingStarted: (started) => set({ offrampingStarted: started }), - setIsInitiating: (initiating) => set({ isInitiating: initiating }), - setOfframpingState: (state) => set({ offrampingState: state }), - setSigningPhase: (phase) => set({ signingPhase: phase }), - - // Complex setters - setSep24Params: (params) => set((state) => ({ ...state, ...params })), - - // Business logic - updateHookStateFromState: (state) => { - if (!state || state.phase === 'success' || state.failure !== undefined) { - set({ signingPhase: undefined }); - } - set({ offrampingState: state }); - }, + offrampStarted: false, + offrampInitiating: false, + offrampState: undefined, + offrampSigningPhase: undefined, + offrampAnchorSessionParams: undefined, + offrampFirstSep24Response: undefined, + offrampExecutionInput: undefined, + + actions: { + // Simple setters + setOfframpStarted: (started) => set({ offrampStarted: started }), + setOfframpInitiating: (initiating) => set({ offrampInitiating: initiating }), + setOfframpState: (state) => set({ offrampState: state }), + setOfframpSigningPhase: (phase) => set({ offrampSigningPhase: phase }), + + // Complex setters + setOfframpSep24Params: (params) => set((state) => ({ ...state, ...params })), - resetState: async () => { - await clearOfframpingState(); - set({ - offrampingStarted: false, - isInitiating: false, - offrampingState: undefined, - signingPhase: undefined, - anchorSessionParams: undefined, - firstSep24Response: undefined, - executionInput: undefined, - }); + // Business logic + updateOfframpHookStateFromState: (state) => { + if (!state || state.phase === 'success' || state.failure !== undefined) { + set({ offrampSigningPhase: undefined }); + } + set({ offrampState: state }); + }, + + resetOfframpState: async () => { + await clearOfframpingState(); + set({ + offrampStarted: false, + offrampInitiating: false, + offrampState: undefined, + offrampSigningPhase: undefined, + offrampAnchorSessionParams: undefined, + offrampFirstSep24Response: undefined, + offrampExecutionInput: undefined, + }); + }, }, })); -export const useSep24State = () => - useOfframpStore((state) => ({ - anchorSessionParams: state.anchorSessionParams, - firstSep24Response: state.firstSep24Response, - executionInput: state.executionInput, - })); +export const useOfframpSigningPhase = () => useOfframpStore((state) => state.offrampSigningPhase); +export const useOfframpState = () => useOfframpStore((state) => state.offrampState); +export const useOfframpStarted = () => useOfframpStore((state) => state.offrampStarted); +export const useOfframpInitiating = () => useOfframpStore((state) => state.offrampInitiating); +export const useOfframpFirstSep24Response = () => useOfframpStore((state) => state.offrampFirstSep24Response); +export const useOfframpExecutionInput = () => useOfframpStore((state) => state.offrampExecutionInput); +export const useOfframpAnchorSessionParams = () => useOfframpStore((state) => state.offrampAnchorSessionParams); + +export const useOfframpActions = () => useOfframpStore((state) => state.actions); diff --git a/src/types/offramp.ts b/src/types/offramp.ts index 2150499c..22587612 100644 --- a/src/types/offramp.ts +++ b/src/types/offramp.ts @@ -4,9 +4,9 @@ import { OfframpingState } from '../services/offrampingFlow'; import { InputTokenType, OutputTokenType } from '../constants/tokenConfig'; import { ISep24Intermediate, IAnchorSessionParams } from '../services/anchor'; -export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; +export type OfframpSigningPhase = 'started' | 'approved' | 'signed' | 'finished'; -export interface ExecutionInput { +export interface OfframpExecutionInput { inputTokenType: InputTokenType; outputTokenType: OutputTokenType; amountInUnits: string; @@ -16,25 +16,27 @@ export interface ExecutionInput { export interface OfframpState { // Core state - offrampingStarted: boolean; - isInitiating: boolean; - offrampingState: OfframpingState | undefined; - signingPhase: SigningPhase | undefined; + offrampStarted: boolean; + offrampInitiating: boolean; + offrampState: OfframpingState | undefined; + offrampSigningPhase: OfframpSigningPhase | undefined; - // SEP24 related state - anchorSessionParams: IAnchorSessionParams | undefined; - firstSep24Response: ISep24Intermediate | undefined; - executionInput: ExecutionInput | undefined; + // SEP24 related state @todo move to separate store + offrampAnchorSessionParams: IAnchorSessionParams | undefined; + offrampFirstSep24Response: ISep24Intermediate | undefined; + offrampExecutionInput: OfframpExecutionInput | undefined; } export interface OfframpActions { - setOfframpingStarted: (started: boolean) => void; - setIsInitiating: (initiating: boolean) => void; - setOfframpingState: (state: OfframpingState | undefined) => void; - setSigningPhase: (phase: SigningPhase | undefined) => void; - setSep24Params: ( - params: Partial>, + setOfframpStarted: (started: boolean) => void; + setOfframpInitiating: (initiating: boolean) => void; + setOfframpState: (state: OfframpingState | undefined) => void; + setOfframpSigningPhase: (phase: OfframpSigningPhase | undefined) => void; + setOfframpSep24Params: ( + params: Partial< + Pick + >, ) => void; - updateHookStateFromState: (state: OfframpingState | undefined) => void; - resetState: () => void; + updateOfframpHookStateFromState: (state: OfframpingState | undefined) => void; + resetOfframpState: () => void; } From 3e3358c3dbcd29f73b47d1388e454946661f4151 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Sun, 15 Dec 2024 13:03:50 +0100 Subject: [PATCH 09/25] update offrampStore property names to reduce cognitive load --- src/hooks/offramp/useMainProcess.ts | 35 ++++--------- src/hooks/offramp/useOfframpingAdvancement.ts | 15 +++--- .../useSEP24/useAnchorWindowHandler.ts | 14 +++--- src/hooks/offramp/useSubmitOfframp.ts | 28 ++++++----- src/pages/swap/index.tsx | 50 +++++++++++-------- src/services/offrampingFlow.ts | 2 +- src/services/phases/polkadot/assethub.ts | 8 +-- src/services/phases/squidrouter/process.ts | 10 ++-- 8 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index 81388dd4..6ba66ac3 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -9,7 +9,7 @@ import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; import { useOfframpingEvents } from './useOfframpingEvents'; import { useOfframpingAdvancement } from './useOfframpingAdvancement'; -import { useOfframpStore } from '../../stores/offrampStore'; +import { useOfframpActions, useOfframpState } from '../../stores/offrampStore'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -22,17 +22,9 @@ export interface ExecutionInput { } export const useMainProcess = () => { - // State updater - const { - setOfframpingStarted, - setIsInitiating, - resetState, - updateHookStateFromState, - offrampingStarted, - isInitiating, - offrampingState, - signingPhase, - } = useOfframpStore(); + const { updateOfframpHookStateFromState, resetOfframpState, setOfframpStarted } = useOfframpActions(); + + const offrampState = useOfframpState(); // Contexts const { setOnSelectedNetworkChange } = useNetwork(); @@ -44,14 +36,14 @@ export const useMainProcess = () => { // Initialize state from storage useEffect(() => { const recoveryState = readCurrentState(); - updateHookStateFromState(recoveryState); + updateOfframpHookStateFromState(recoveryState); events.trackOfframpingEvent(recoveryState); - }, [updateHookStateFromState, events]); + }, [updateOfframpHookStateFromState, events]); // Reset offramping state when the network is changed useEffect(() => { - setOnSelectedNetworkChange(resetState); - }, [setOnSelectedNetworkChange, resetState]); + setOnSelectedNetworkChange(resetOfframpState); + }, [setOnSelectedNetworkChange, resetOfframpState]); // Determines the current offramping phase useOfframpingAdvancement({ @@ -63,23 +55,18 @@ export const useMainProcess = () => { ...sep24, }), firstSep24ResponseState: sep24.firstSep24Response, - offrampingState, - offrampingStarted, - isInitiating, - setIsInitiating, finishOfframping: () => { events.resetUniqueEvents(); - resetState(); + resetOfframpState(); }, continueFailedFlow: () => { - updateHookStateFromState(recoverFromFailure(offrampingState)); + updateOfframpHookStateFromState(recoverFromFailure(offrampState)); }, handleOnAnchorWindowOpen: sep24.handleOnAnchorWindowOpen, - signingPhase, // @todo: why do we need this? maybeCancelSep24First: () => { if (sep24.firstSep24IntervalRef.current !== undefined) { - setOfframpingStarted(false); + setOfframpStarted(false); sep24.cleanSep24FirstVariables(); } }, diff --git a/src/hooks/offramp/useOfframpingAdvancement.ts b/src/hooks/offramp/useOfframpingAdvancement.ts index 44285baa..f3f9a2a0 100644 --- a/src/hooks/offramp/useOfframpingAdvancement.ts +++ b/src/hooks/offramp/useOfframpingAdvancement.ts @@ -9,7 +9,7 @@ import { useAssetHubNode } from '../../contexts/polkadotNode'; import { usePendulumNode } from '../../contexts/polkadotNode'; import { useEventsContext } from '../../contexts/events'; -import { useOfframpStore } from '../../stores/offrampStore'; +import { useOfframpActions, useOfframpState } from '../../stores/offrampStore'; interface AdvancementDeps { addEvent: (message: string, status: EventStatus) => void; @@ -23,7 +23,8 @@ export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { const { apiComponents: pendulumNode } = usePendulumNode(); const { apiComponents: assetHubNode } = useAssetHubNode(); - const { offrampingState, updateHookStateFromState, setSigningPhase } = useOfframpStore(); + const offrampState = useOfframpState(); + const { updateOfframpHookStateFromState, setOfframpSigningPhase } = useOfframpActions(); useEffect(() => { if (wagmiConfig.state.status !== 'connected') return; @@ -34,18 +35,18 @@ export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { return; } - const nextState = await advanceOfframpingState(offrampingState, { + const nextState = await advanceOfframpingState(offrampState, { renderEvent: addEvent, wagmiConfig, - setSigningPhase, + setOfframpSigningPhase, trackEvent, pendulumNode, assetHubNode, walletAccount, }); - if (JSON.stringify(offrampingState) !== JSON.stringify(nextState)) { - updateHookStateFromState(nextState); + if (JSON.stringify(offrampState) !== JSON.stringify(nextState)) { + updateOfframpHookStateFromState(nextState); } })(); @@ -54,5 +55,5 @@ export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { // but we intentionally exclude them from the dependency array to prevent unnecessary re-renders. // These dependencies are stable and won't change during the lifecycle of this hook. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [offrampingState, trackEvent, updateHookStateFromState, wagmiConfig]); + }, [offrampState, trackEvent, updateOfframpHookStateFromState, wagmiConfig]); }; diff --git a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts index 6548b131..c3d889e2 100644 --- a/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts +++ b/src/hooks/offramp/useSEP24/useAnchorWindowHandler.ts @@ -11,7 +11,7 @@ import { showToast, ToastMessage } from '../../../helpers/notifications'; import { UseSEP24StateReturn } from './useSEP24State'; import { useTrackSEP24Events } from './useTrackSEP24Events'; import { usePendulumNode } from '../../../contexts/polkadotNode'; -import { useOfframpStore } from '../../../stores/offrampStore'; +import { useOfframpActions } from '../../../stores/offrampStore'; const handleAmountMismatch = (setOfframpingStarted: (started: boolean) => void): void => { setOfframpingStarted(false); @@ -27,7 +27,7 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF const { trackKYCStarted, trackKYCCompleted } = useTrackSEP24Events(); const { selectedNetwork } = useNetwork(); const { apiComponents: pendulumNode } = usePendulumNode(); - const { setOfframpingStarted, updateHookStateFromState } = useOfframpStore(); + const { setOfframpStarted, updateOfframpHookStateFromState } = useOfframpActions(); const { firstSep24Response, anchorSessionParams, executionInput } = sep24State; @@ -48,7 +48,7 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF const secondSep24Response = await sep24Second(firstSep24Response, anchorSessionParams); if (!Big(secondSep24Response.amount).eq(executionInput.offrampAmount)) { - handleAmountMismatch(setOfframpingStarted); + handleAmountMismatch(setOfframpStarted); return; } @@ -65,9 +65,9 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF }); trackKYCCompleted(initialState, selectedNetwork); - updateHookStateFromState(initialState); + updateOfframpHookStateFromState(initialState); } catch (error) { - handleError(error, setOfframpingStarted); + handleError(error, setOfframpStarted); } }, [ firstSep24Response, @@ -78,7 +78,7 @@ export const useAnchorWindowHandler = (sep24State: UseSEP24StateReturn, cleanupF cleanupFn, trackKYCCompleted, selectedNetwork, - setOfframpingStarted, - updateHookStateFromState, + setOfframpStarted, + updateOfframpHookStateFromState, ]); }; diff --git a/src/hooks/offramp/useSubmitOfframp.ts b/src/hooks/offramp/useSubmitOfframp.ts index dcd960bb..2e066c71 100644 --- a/src/hooks/offramp/useSubmitOfframp.ts +++ b/src/hooks/offramp/useSubmitOfframp.ts @@ -16,7 +16,7 @@ import { sep24First, } from '../../services/anchor'; -import { useOfframpStore } from '../../stores/offrampStore'; +import { useOfframpActions, useOfframpStarted, useOfframpState } from '../../stores/offrampStore'; import { ExtendedExecutionInput } from './useSEP24/useSEP24State'; import { ExecutionInput } from './useSEP24'; @@ -40,7 +40,9 @@ export const useSubmitOfframp = ({ const { trackEvent } = useEventsContext(); const { address } = useAccount(); const { checkAndWaitForSignature, forceRefreshAndWaitForSignature } = useSiweContext(); - const { offrampingStarted, offrampingState, setOfframpingStarted, setIsInitiating } = useOfframpStore(); + const offrampStarted = useOfframpStarted(); + const offrampState = useOfframpState(); + const { setOfframpStarted, setOfframpInitiating } = useOfframpActions(); const addEvent = (message: string, status: string) => { console.log('Add event', message, status); @@ -50,14 +52,14 @@ export const useSubmitOfframp = ({ (executionInput: ExecutionInput) => { const { inputTokenType, amountInUnits, outputTokenType, offrampAmount, setInitializeFailed } = executionInput; - if (offrampingStarted || offrampingState !== undefined) { - setIsInitiating(false); + if (offrampStarted || offrampState !== undefined) { + setOfframpInitiating(false); return; } (async () => { switchChain({ chainId: polygon.id }); - setOfframpingStarted(true); + setOfframpStarted(true); trackEvent({ event: 'transaction_confirmation', @@ -110,25 +112,25 @@ export const useSubmitOfframp = ({ } catch (error) { console.error('Error finalizing the initial state of the offramping process', error); setInitializeFailed(true); - setOfframpingStarted(false); + setOfframpStarted(false); cleanSep24FirstVariables(); } finally { - setIsInitiating(false); + setOfframpInitiating(false); } } catch (error) { console.error('Error initializing the offramping process', error); setInitializeFailed(true); - setOfframpingStarted(false); - setIsInitiating(false); + setOfframpStarted(false); + setOfframpInitiating(false); } })(); }, [ - offrampingStarted, - offrampingState, - setIsInitiating, + offrampStarted, + offrampState, + setOfframpInitiating, switchChain, - setOfframpingStarted, + setOfframpStarted, trackEvent, selectedNetwork, address, diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 3bdc3756..7c49d7d5 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -50,6 +50,13 @@ import { BaseLayout } from '../../layouts'; import { ProgressPage } from '../progress'; import { FailurePage } from '../failure'; import { SuccessPage } from '../success'; +import { + useOfframpActions, + useOfframpSigningPhase, + useOfframpState, + useOfframpStarted, + useOfframpInitiating, +} from '../../stores/offrampStore'; const Arrow = () => (
@@ -96,16 +103,17 @@ export const SwapPage = () => { handleOnSubmit, finishOfframping, continueFailedFlow, - offrampingStarted, firstSep24ResponseState, handleOnAnchorWindowOpen, - offrampingState, - isInitiating, - signingPhase, - setIsInitiating, maybeCancelSep24First, } = useMainProcess(); + const offrampStarted = useOfframpStarted(); + const offrampState = useOfframpState(); + const offrampSigningPhase = useOfframpSigningPhase(); + const offrampInitiating = useOfframpInitiating(); + const { setOfframpInitiating } = useOfframpActions(); + // Store the id as it is cleared after the user opens the anchor window useEffect(() => { if (firstSep24ResponseState?.id != undefined) { @@ -170,9 +178,11 @@ export const SwapPage = () => { const preciseQuotedAmountOut = tokenOutAmountData.preciseQuotedAmountOut; + // @todo: why do we need offrampInitiating? offrampStarted is not enough? + // test the route for starting token, then proceed // will disable the confirm button - setIsInitiating(true); + setOfframpInitiating(true); const outputToken = OUTPUT_TOKEN_CONFIG[to]; const inputToken = getInputTokenDetailsOrDefault(selectedNetwork, from); @@ -207,7 +217,7 @@ export const SwapPage = () => { }); }) .catch((_error) => { - setIsInitiating(false); + setOfframpInitiating(false); setInitializeFailed(true); }); } @@ -254,10 +264,10 @@ export const SwapPage = () => { }, []); useEffect(() => { - if (offrampingState?.phase !== undefined) { + if (offrampState?.phase !== undefined) { setNetworkSelectorDisabled(true); } - }, [offrampingState, setNetworkSelectorDisabled]); + }, [offrampState, setNetworkSelectorDisabled]); const ReceiveNumericInput = useMemo( () => ( @@ -359,38 +369,38 @@ export const SwapPage = () => { ); - if (offrampingState?.phase === 'success') { + if (offrampState?.phase === 'success') { return ; } - if (offrampingState?.failure !== undefined) { + if (offrampState?.failure !== undefined) { return ( ); } - if (offrampingState !== undefined || offrampingStarted) { + if (offrampState !== undefined || offrampStarted) { const isAssetHubFlow = selectedNetwork === Networks.AssetHub && - (offrampingState?.phase === 'pendulumFundEphemeral' || offrampingState?.phase === 'executeAssetHubXCM'); + (offrampState?.phase === 'pendulumFundEphemeral' || offrampState?.phase === 'executeAssetHubXCM'); const showMainScreenAnyway = - offrampingState === undefined || - ['prepareTransactions', 'squidRouter'].includes(offrampingState.phase) || + offrampState === undefined || + ['prepareTransactions', 'squidRouter'].includes(offrampState.phase) || isAssetHubFlow; if (!showMainScreenAnyway) { - return ; + return ; } } const main = (
- +
{ ) : ( )}
diff --git a/src/services/offrampingFlow.ts b/src/services/offrampingFlow.ts index 54f281f6..95891993 100644 --- a/src/services/offrampingFlow.ts +++ b/src/services/offrampingFlow.ts @@ -66,7 +66,7 @@ export type FinalOfframpingPhase = 'success'; export interface ExecutionContext { wagmiConfig: Config; renderEvent: RenderEventHandler; - setSigningPhase: (n: SigningPhase) => void; + setOfframpSigningPhase: (n: SigningPhase) => void; trackEvent: (event: TrackableEvent) => void; pendulumNode: { ss58Format: number; api: ApiPromise; decimals: number }; assetHubNode: { api: ApiPromise }; diff --git a/src/services/phases/polkadot/assethub.ts b/src/services/phases/polkadot/assethub.ts index 6949c49e..8232160c 100644 --- a/src/services/phases/polkadot/assethub.ts +++ b/src/services/phases/polkadot/assethub.ts @@ -30,7 +30,7 @@ export function createAssethubAssetTransfer(assethubApi: ApiPromise, receiverAdd } export async function executeAssetHubXCM(state: OfframpingState, context: ExecutionContext): Promise { - const { assetHubNode, walletAccount, setSigningPhase } = context; + const { assetHubNode, walletAccount, setOfframpSigningPhase } = context; const { pendulumEphemeralAddress } = state; if (!walletAccount) { @@ -40,7 +40,7 @@ export async function executeAssetHubXCM(state: OfframpingState, context: Execut throw new Error('AssetHub node not available'); } - setSigningPhase?.('started'); + setOfframpSigningPhase?.('started'); const didInputTokenArrivedOnPendulum = async () => { const inputBalanceRaw = await getRawInputBalance(state, context); @@ -52,9 +52,9 @@ export async function executeAssetHubXCM(state: OfframpingState, context: Execut if (assetHubXcmTransactionHash === undefined) { const tx = createAssethubAssetTransfer(assetHubNode.api, pendulumEphemeralAddress, inputAmount.raw); - context.setSigningPhase('started'); + context.setOfframpSigningPhase('started'); const { hash } = await tx.signAndSend(walletAccount.address, { signer: walletAccount.signer as Signer }); - setSigningPhase?.('finished'); + setOfframpSigningPhase?.('finished'); return { ...state, assetHubXcmTransactionHash: hash.toString() }; } diff --git a/src/services/phases/squidrouter/process.ts b/src/services/phases/squidrouter/process.ts index 96c0fb24..c25ab5d2 100644 --- a/src/services/phases/squidrouter/process.ts +++ b/src/services/phases/squidrouter/process.ts @@ -11,7 +11,7 @@ import { getRouteTransactionRequest } from './route'; export async function squidRouter( state: OfframpingState, - { wagmiConfig, setSigningPhase, trackEvent }: ExecutionContext, + { wagmiConfig, setOfframpSigningPhase, trackEvent }: ExecutionContext, ): Promise { const inputToken = getInputTokenDetails(state.network, state.inputTokenType); if (!inputToken || !isEvmInputTokenDetails(inputToken)) { @@ -37,7 +37,7 @@ export async function squidRouter( state.inputAmount.units, ); - setSigningPhase?.('started'); + setOfframpSigningPhase?.('started'); let approvalHash; try { @@ -68,7 +68,7 @@ export async function squidRouter( return { ...state, failure: { type: 'unrecoverable', message: e?.toString() } }; } - setSigningPhase?.('approved'); + setOfframpSigningPhase?.('approved'); await waitForTransactionReceipt(wagmiConfig, { hash: approvalHash }); @@ -100,12 +100,12 @@ export async function squidRouter( return { ...state, failure: { type: 'unrecoverable', message: e?.toString() } }; } - setSigningPhase?.('signed'); + setOfframpSigningPhase?.('signed'); const axelarScanLink = 'https://axelarscan.io/gmp/' + swapHash; console.log(`Squidrouter Swap Initiated! Check Axelarscan for details: ${axelarScanLink}`); - setSigningPhase?.('finished'); + setOfframpSigningPhase?.('finished'); return { ...state, From 677421bd27b5d5056e3fe25bdf459c8daf4361be Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Mon, 16 Dec 2024 14:48:28 +0100 Subject: [PATCH 10/25] Don't show terms and conditions --- src/pages/swap/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index a5250cc9..e69c98c9 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -24,7 +24,6 @@ import { useMainProcess } from '../../hooks/useMainProcess'; import { ProgressPage } from '../progress'; import { SuccessPage } from '../success'; import { FailurePage } from '../failure'; -import { TermsAndConditions } from '../../components/TermsAndConditions'; import { useInputTokenBalance } from '../../hooks/useInputTokenBalance'; import { UserBalance } from '../../components/UserBalance'; import { useEventsContext } from '../../contexts/events'; @@ -320,7 +319,6 @@ export const SwapPage = () => { const modals = ( <> - { From bd3a8a9ea968e34fc9024eb57c6b71f5a2367cbd Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 16 Dec 2024 15:20:43 +0100 Subject: [PATCH 11/25] export default postcss config --- postcss.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postcss.config.cjs b/postcss.config.cjs index e569373f..feab3515 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,4 +1,4 @@ -module.exports = { +export default { plugins: { 'postcss-import': {}, 'tailwindcss/nesting': {}, From 5715082f2c4e5577ef4bf7c35ff4b353b9819797 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 16 Dec 2024 15:24:30 +0100 Subject: [PATCH 12/25] change postcss config to .js from .mjs --- postcss.config.cjs => postcss.config.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename postcss.config.cjs => postcss.config.js (100%) diff --git a/postcss.config.cjs b/postcss.config.js similarity index 100% rename from postcss.config.cjs rename to postcss.config.js From 1cb889683f47f6ad6c6282569e7875fe0aa7cb84 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 16 Dec 2024 15:27:04 +0100 Subject: [PATCH 13/25] export default tailwindcss config --- tailwind.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index b88c0dc6..99132d41 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -25,7 +25,7 @@ const colors = { }, }; // eslint-disable-next-line no-undef -module.exports = { +export default { darkMode: 'class', content: [ './src/**/*.{js,ts,jsx,tsx}', From fc0d8705af4c478425ad4409f08078734b434973 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 16 Dec 2024 15:30:39 +0100 Subject: [PATCH 14/25] change daisyui import --- tailwind.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index 99132d41..f8930c5d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,5 @@ +import daisyui from 'daisyui'; + const colors = { whiteAlpha: { 50: 'rgba(255, 255, 255, 0.04)', @@ -33,7 +35,7 @@ export default { 'node_modules/react-daisyui/dist/**/*.js', ], // eslint-disable-next-line no-undef - plugins: [require('daisyui')], + plugins: [daisyui], themes: ['pendulum', 'amplitude'], theme: { extend: { From 3bf312c6c3e4a9f6d672defe9b70dccd2c3bb3d0 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 16 Dec 2024 16:37:53 +0100 Subject: [PATCH 15/25] rename Offramping to Offramp --- src/hooks/offramp/useMainProcess.ts | 8 ++++---- ...eOfframpingAdvancement.ts => useOfframpAdvancement.ts} | 2 +- .../{useOfframpingEvents.ts => useOfframpEvents.ts} | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/hooks/offramp/{useOfframpingAdvancement.ts => useOfframpAdvancement.ts} (96%) rename src/hooks/offramp/{useOfframpingEvents.ts => useOfframpEvents.ts} (97%) diff --git a/src/hooks/offramp/useMainProcess.ts b/src/hooks/offramp/useMainProcess.ts index 6ba66ac3..2d2c539d 100644 --- a/src/hooks/offramp/useMainProcess.ts +++ b/src/hooks/offramp/useMainProcess.ts @@ -7,8 +7,8 @@ import { recoverFromFailure, readCurrentState } from '../../services/offrampingF import { useSEP24 } from './useSEP24'; import { useSubmitOfframp } from './useSubmitOfframp'; -import { useOfframpingEvents } from './useOfframpingEvents'; -import { useOfframpingAdvancement } from './useOfframpingAdvancement'; +import { useOfframpEvents } from './useOfframpEvents'; +import { useOfframpAdvancement } from './useOfframpAdvancement'; import { useOfframpActions, useOfframpState } from '../../stores/offrampStore'; export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished'; @@ -30,7 +30,7 @@ export const useMainProcess = () => { const { setOnSelectedNetworkChange } = useNetwork(); // Custom hooks - const events = useOfframpingEvents(); + const events = useOfframpEvents(); const sep24 = useSEP24(); // Initialize state from storage @@ -46,7 +46,7 @@ export const useMainProcess = () => { }, [setOnSelectedNetworkChange, resetOfframpState]); // Determines the current offramping phase - useOfframpingAdvancement({ + useOfframpAdvancement({ addEvent: events.addEvent, }); diff --git a/src/hooks/offramp/useOfframpingAdvancement.ts b/src/hooks/offramp/useOfframpAdvancement.ts similarity index 96% rename from src/hooks/offramp/useOfframpingAdvancement.ts rename to src/hooks/offramp/useOfframpAdvancement.ts index f3f9a2a0..e4565d7c 100644 --- a/src/hooks/offramp/useOfframpingAdvancement.ts +++ b/src/hooks/offramp/useOfframpAdvancement.ts @@ -15,7 +15,7 @@ interface AdvancementDeps { addEvent: (message: string, status: EventStatus) => void; } -export const useOfframpingAdvancement = ({ addEvent }: AdvancementDeps) => { +export const useOfframpAdvancement = ({ addEvent }: AdvancementDeps) => { const { walletAccount } = usePolkadotWalletState(); const { trackEvent } = useEventsContext(); const wagmiConfig = useConfig(); diff --git a/src/hooks/offramp/useOfframpingEvents.ts b/src/hooks/offramp/useOfframpEvents.ts similarity index 97% rename from src/hooks/offramp/useOfframpingEvents.ts rename to src/hooks/offramp/useOfframpEvents.ts index 06f70971..7f27ff34 100644 --- a/src/hooks/offramp/useOfframpingEvents.ts +++ b/src/hooks/offramp/useOfframpEvents.ts @@ -11,7 +11,7 @@ import { getInputTokenDetailsOrDefault } from '../../constants/tokenConfig'; import { OfframpingState } from '../../services/offrampingFlow'; import { OFFRAMPING_PHASE_SECONDS } from '../../pages/progress'; -export const useOfframpingEvents = () => { +export const useOfframpEvents = () => { const { trackEvent, resetUniqueEvents } = useEventsContext(); const { selectedNetwork } = useNetwork(); const [_, setEvents] = useState([]); From b4136ff9d341aebde9a9d1b1813f3e44ff4e225c Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 19 Dec 2024 15:10:25 +0100 Subject: [PATCH 16/25] add fromAmount and showFees url params --- src/components/FeeCollapse/index.tsx | 5 +++-- src/pages/swap/index.tsx | 8 +++++++- src/pages/swap/useSwapUrlParams.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 src/pages/swap/useSwapUrlParams.ts diff --git a/src/components/FeeCollapse/index.tsx b/src/components/FeeCollapse/index.tsx index adcc7254..f219281a 100644 --- a/src/components/FeeCollapse/index.tsx +++ b/src/components/FeeCollapse/index.tsx @@ -22,9 +22,10 @@ interface CollapseProps { toAmount?: Big; toToken: OutputTokenDetails; exchangeRate?: JSX.Element; + expanded?: boolean; } -export const FeeCollapse: FC = ({ toAmount = Big(0), toToken, exchangeRate }) => { +export const FeeCollapse: FC = ({ toAmount = Big(0), toToken, exchangeRate, expanded = false }) => { const { trackEvent } = useEventsContext(); const toTokenSymbol = toToken.fiat.symbol; @@ -39,7 +40,7 @@ export const FeeCollapse: FC = ({ toAmount = Big(0), toToken, exc return (
- +

Details

diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index e69c98c9..609003ff 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -37,6 +37,7 @@ import { FeeComparison } from '../../components/FeeComparison'; import { SignInModal } from '../../components/SignIn'; import { useSiweContext } from '../../contexts/siwe'; +import { useSwapUrlParams } from './useSwapUrlParams'; const Arrow = () => (
@@ -110,6 +111,10 @@ export const SwapPage = () => { to, } = useSwapForm(); + const [showFeeCollapse, setShowFeeCollapse] = useState(false); + + useSwapUrlParams({ form, setShowFeeCollapse }); + const fromToken = INPUT_TOKEN_CONFIG[from]; const toToken = OUTPUT_TOKEN_CONFIG[to]; const formToAmount = form.watch('toAmount'); @@ -370,6 +375,7 @@ export const SwapPage = () => {

{getCurrentErrorMessage()}

{

)} -
+