Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor useMainProcess #324

Merged
merged 45 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e6a3cdc
modify autodeploy.yaml
gianfra-t Aug 21, 2024
1665a3c
Merge branch 'polygon-prototype-staging' into upgrade-polyon-prototype
TorstenStueber Sep 11, 2024
0148805
Merge pull request #151 from pendulum-chain/upgrade-polyon-prototype
TorstenStueber Sep 11, 2024
3fe70bb
Merge branch 'polygon-prototype-staging' into release-polygon-prototy…
TorstenStueber Sep 19, 2024
0c0a818
Merge pull request #161 from pendulum-chain/release-polygon-prototype…
TorstenStueber Sep 19, 2024
1379abf
Merge pull request #176 from pendulum-chain/polygon-prototype-staging
gianfra-t Sep 25, 2024
91fc9ac
Merge pull request #194 from pendulum-chain/polygon-prototype-staging
TorstenStueber Oct 7, 2024
8e28135
Merge pull request #200 from pendulum-chain/polygon-prototype-staging
ebma Oct 9, 2024
238a73a
Merge pull request #202 from pendulum-chain/polygon-prototype-staging
ebma Oct 9, 2024
e732ab7
Merge pull request #207 from pendulum-chain/polygon-prototype-staging
ebma Oct 10, 2024
8ee1efd
Merge pull request #232 from pendulum-chain/polygon-prototype-staging
TorstenStueber Oct 26, 2024
9d5d5f4
Merge pull request #253 from pendulum-chain/polygon-prototype-staging
TorstenStueber Nov 4, 2024
8332dca
Merge pull request #287 from pendulum-chain/polygon-prototype-staging
ebma Nov 19, 2024
6cb8f38
Update the Mykobo home domain
TorstenStueber Nov 27, 2024
1dd389c
Merge pull request #304 from pendulum-chain/TorstenStueber-patch-1
TorstenStueber Nov 27, 2024
50872e2
Merge pull request #312 from pendulum-chain/polygon-prototype-staging
ebma Dec 3, 2024
fb37af3
add useOfframpingEvents custom hook
Sharqiewicz Dec 12, 2024
acb0e80
add useOfframpingAdvancement custom hook
Sharqiewicz Dec 12, 2024
ebaf886
reduce cognitive load of useMainProcess hook
Sharqiewicz Dec 12, 2024
bd10823
implement zustand for useMainProcess
Sharqiewicz Dec 13, 2024
b8ece4f
improve zustand config
Sharqiewicz Dec 13, 2024
0607559
refactor offrampStore
Sharqiewicz Dec 15, 2024
3e3358c
update offrampStore property names to reduce cognitive load
Sharqiewicz Dec 15, 2024
677421b
Don't show terms and conditions
ebma Dec 16, 2024
bd3a8a9
export default postcss config
Sharqiewicz Dec 16, 2024
5715082
change postcss config to .js from .mjs
Sharqiewicz Dec 16, 2024
1cb8896
export default tailwindcss config
Sharqiewicz Dec 16, 2024
fc0d870
change daisyui import
Sharqiewicz Dec 16, 2024
3bf312c
rename Offramping to Offramp
Sharqiewicz Dec 16, 2024
a4c7b7a
Merge pull request #330 from pendulum-chain/remove-t-and-c-dialog
ebma Dec 16, 2024
b4136ff
add fromAmount and showFees url params
Sharqiewicz Dec 19, 2024
84628fb
max 2 decimal places fromAmount url param
Sharqiewicz Dec 19, 2024
ac71195
fromAmount url param accept only bigger than 0
Sharqiewicz Dec 19, 2024
0e7fdbc
add showCompareFees param
Sharqiewicz Dec 19, 2024
77750b7
remove compareFees param
Sharqiewicz Dec 19, 2024
0fd6726
remove unused setEvents code
Sharqiewicz Dec 19, 2024
90a8245
add renderEvent
Sharqiewicz Dec 19, 2024
1ea84b3
remove unnecessary comment
Sharqiewicz Dec 19, 2024
aa37e1e
Merge branch 'polygon-prototype-staging' into refactor/useMainProcess
Sharqiewicz Dec 19, 2024
5e0f00b
Merge branch 'polygon-prototype-staging' into refactor/useMainProcess
Sharqiewicz Dec 19, 2024
c740ec0
fix conflict
Sharqiewicz Dec 19, 2024
40c5a32
Merge pull request #333 from pendulum-chain/321-add-prefill-form-url
ebma Dec 23, 2024
3e166e8
Merge branch 'polygon-prototype' into polygon-prototype-staging
Sharqiewicz Dec 23, 2024
9b73bcc
fix autodeploy config
Sharqiewicz Dec 23, 2024
81dca9f
Merge branch 'polygon-prototype-staging' into refactor/useMainProcess
Sharqiewicz Dec 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,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",
Expand Down
234 changes: 43 additions & 191 deletions src/hooks/offramp/useMainProcess.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
import { useState, useEffect, useCallback, StateUpdater, useRef } from 'preact/compat';
import { useConfig } from 'wagmi';
import { useEffect, StateUpdater } from 'preact/compat';
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 { useAssetHubNode, usePendulumNode } from '../../contexts/polkadotNode';
import { usePolkadotWalletState } from '../../contexts/polkadotWallet';
import { Networks, useNetwork } from '../../contexts/network';

import {
clearOfframpingState,
recoverFromFailure,
readCurrentState,
advanceOfframpingState,
OfframpingState,
} from '../../services/offrampingFlow';
import { InputTokenType, OutputTokenType } from '../../constants/tokenConfig';
import { useNetwork } from '../../contexts/network';
import { recoverFromFailure, readCurrentState } from '../../services/offrampingFlow';

import { useSEP24 } from './useSEP24';
import { useSubmitOfframp } from './useSubmitOfframp';
import { useOfframpEvents } from './useOfframpEvents';
import { useOfframpAdvancement } from './useOfframpAdvancement';
import { useOfframpActions, useOfframpState } from '../../stores/offrampStore';

export type SigningPhase = 'started' | 'approved' | 'signed' | 'finished';

Expand All @@ -34,187 +22,51 @@ export interface ExecutionInput {
}

export const useMainProcess = () => {
const [offrampingStarted, setOfframpingStarted] = useState<boolean>(false);
const [isInitiating, setIsInitiating] = useState<boolean>(false);
const [offrampingState, setOfframpingState] = useState<OfframpingState | undefined>(undefined);
const [signingPhase, setSigningPhase] = useState<SigningPhase | undefined>(undefined);
const isProcessingAdvance = useRef(false);

const { selectedNetwork, setOnSelectedNetworkChange } = useNetwork();
const { walletAccount } = usePolkadotWalletState();

const { apiComponents: pendulumNode } = usePendulumNode();
const { apiComponents: assetHubNode } = useAssetHubNode();

const wagmiConfig = useConfig();
const { trackEvent, resetUniqueEvents } = useEventsContext();

const [, setEvents] = useState<GenericEvent[]>([]);
const {
firstSep24IntervalRef,
firstSep24Response,
setFirstSep24Response,
setExecutionInput,
setAnchorSessionParams,
cleanSep24FirstVariables,
handleOnAnchorWindowOpen: sep24HandleOnAnchorWindowOpen,
} = useSEP24();
const { updateOfframpHookStateFromState, resetOfframpState, setOfframpStarted } = useOfframpActions();

const handleOnSubmit = useSubmitOfframp({
firstSep24IntervalRef,
setFirstSep24Response,
setExecutionInput,
setAnchorSessionParams,
cleanSep24FirstVariables,
offrampingStarted,
offrampingState,
setOfframpingStarted,
setIsInitiating,
});
const offrampState = useOfframpState();

const updateHookStateFromState = useCallback(
(state: OfframpingState | undefined) => {
if (state === undefined || state.phase === 'success' || state.failure !== undefined) {
setSigningPhase(undefined);
}

setOfframpingState(state);
// Contexts
const { setOnSelectedNetworkChange } = useNetwork();

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,
});
}
},
[trackEvent, selectedNetwork],
);
// Custom hooks
const events = useOfframpEvents();
const sep24 = useSEP24();

// Initialize state from storage
useEffect(() => {
const state = readCurrentState();
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);
setIsInitiating(false);
setAnchorSessionParams(undefined);
setFirstSep24Response(undefined);
setExecutionInput(undefined);
cleanSep24FirstVariables();
clearOfframpingState();
setSigningPhase(undefined);
}, [
setOfframpingState,
setOfframpingStarted,
setIsInitiating,
setAnchorSessionParams,
setFirstSep24Response,
setExecutionInput,
cleanSep24FirstVariables,
setSigningPhase,
]);

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 finishOfframping = useCallback(() => {
(async () => {
clearOfframpingState();
resetUniqueEvents();
setOfframpingStarted(false);
updateHookStateFromState(undefined);
})();
}, [updateHookStateFromState, resetUniqueEvents]);

const continueFailedFlow = useCallback(() => {
const nextState = recoverFromFailure(offrampingState);
updateHookStateFromState(nextState);
}, [updateHookStateFromState, offrampingState]);
const recoveryState = readCurrentState();
updateOfframpHookStateFromState(recoveryState);
events.trackOfframpingEvent(recoveryState);
}, [updateOfframpHookStateFromState, events]);

// Reset offramping state when the network is changed
useEffect(() => {
if (selectedNetwork == Networks.Polygon && wagmiConfig.state.status !== 'connected') return;
if (selectedNetwork == Networks.AssetHub && !walletAccount?.address) return;

(async () => {
try {
if (isProcessingAdvance.current) return;
isProcessingAdvance.current = true;

if (!pendulumNode || !assetHubNode) {
console.error('Polkadot nodes not initialized');
return;
}
setOnSelectedNetworkChange(resetOfframpState);
}, [setOnSelectedNetworkChange, resetOfframpState]);

const nextState = await advanceOfframpingState(offrampingState, {
renderEvent: addEvent,
wagmiConfig,
setSigningPhase,
trackEvent,
pendulumNode,
assetHubNode,
walletAccount,
});

if (JSON.stringify(offrampingState) !== JSON.stringify(nextState)) {
updateHookStateFromState(nextState);
}
} catch (error) {
console.error('Error advancing offramping state:', error);
} finally {
isProcessingAdvance.current = false;
}
})();
}, [
offrampingState,
trackEvent,
updateHookStateFromState,
wagmiConfig,
pendulumNode,
assetHubNode,
wagmiConfig.state.status,
walletAccount?.address,
]);

const maybeCancelSep24First = useCallback(() => {
if (firstSep24IntervalRef.current !== undefined) {
setOfframpingStarted(false);
cleanSep24FirstVariables();
}
}, [firstSep24IntervalRef, cleanSep24FirstVariables]);
// Determines the current offramping phase
useOfframpAdvancement();

return {
handleOnSubmit,
firstSep24ResponseState: firstSep24Response,
offrampingState,
offrampingStarted,
isInitiating,
setIsInitiating,
finishOfframping,
continueFailedFlow,
handleOnAnchorWindowOpen,
signingPhase,
maybeCancelSep24First,
handleOnSubmit: useSubmitOfframp({
...sep24,
}),
firstSep24ResponseState: sep24.firstSep24Response,
finishOfframping: () => {
events.resetUniqueEvents();
resetOfframpState();
},
continueFailedFlow: () => {
updateOfframpHookStateFromState(recoverFromFailure(offrampState));
},
handleOnAnchorWindowOpen: sep24.handleOnAnchorWindowOpen,
// @todo: why do we need this?
maybeCancelSep24First: () => {
if (sep24.firstSep24IntervalRef.current !== undefined) {
setOfframpStarted(false);
sep24.cleanSep24FirstVariables();
}
},
};
};
57 changes: 57 additions & 0 deletions src/hooks/offramp/useOfframpAdvancement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useEffect } from 'preact/hooks';
import { useConfig } from 'wagmi';

import { advanceOfframpingState } from '../../services/offrampingFlow';

import { usePolkadotWalletState } from '../../contexts/polkadotWallet';
import { useAssetHubNode } from '../../contexts/polkadotNode';
import { usePendulumNode } from '../../contexts/polkadotNode';
import { useEventsContext } from '../../contexts/events';

import { useOfframpActions, useOfframpState } from '../../stores/offrampStore';
import { EventStatus } from '../../components/GenericEvent';

export const useOfframpAdvancement = () => {
const { walletAccount } = usePolkadotWalletState();
const { trackEvent } = useEventsContext();
const wagmiConfig = useConfig();

const { apiComponents: pendulumNode } = usePendulumNode();
const { apiComponents: assetHubNode } = useAssetHubNode();

const offrampState = useOfframpState();
const { updateOfframpHookStateFromState, setOfframpSigningPhase } = useOfframpActions();

useEffect(() => {
if (wagmiConfig.state.status !== 'connected') return;

(async () => {
if (!pendulumNode || !assetHubNode) {
console.error('Polkadot nodes not initialized');
return;
}

const nextState = await advanceOfframpingState(offrampState, {
wagmiConfig,
setOfframpSigningPhase,
trackEvent,
pendulumNode,
assetHubNode,
walletAccount,
renderEvent: (message: string, status: EventStatus) => {
console.log('renderEvent: ', message, status);
},
});

if (JSON.stringify(offrampState) !== JSON.stringify(nextState)) {
updateOfframpHookStateFromState(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.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [offrampState, trackEvent, updateOfframpHookStateFromState, wagmiConfig]);
};
35 changes: 35 additions & 0 deletions src/hooks/offramp/useOfframpEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useCallback } from 'preact/compat';
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 { OFFRAMPING_PHASE_SECONDS } from '../../pages/progress';

export const useOfframpEvents = () => {
const { trackEvent, resetUniqueEvents } = useEventsContext();
const { selectedNetwork } = useNetwork();

const trackOfframpingEvent = useCallback(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could move this to useEventsContext? Especially since we remove the addEvent thingy. This useOfframpEvents is a bit intermediary.
If it's too much trouble then no need.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gianfra-t I think we should keep the logic in a custom hook and in the Events Context keep only state-related code. The Event Context file is actually quite big right now

(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 { trackOfframpingEvent, trackEvent, resetUniqueEvents };
};
Loading
Loading