diff --git a/src/components/icons/Checkmark.tsx b/src/components/icons/Checkmark.tsx
index fe52a64..1fa6fab 100644
--- a/src/components/icons/Checkmark.tsx
+++ b/src/components/icons/Checkmark.tsx
@@ -17,11 +17,11 @@ function _Checkmark({
width={width}
height={height}
className={className}
- viewBox="0 0 16 16"
+ viewBox="0 0 36 27"
>
);
diff --git a/src/components/nav/NavBar.tsx b/src/components/nav/NavBar.tsx
index f0740fd..6cd277b 100644
--- a/src/components/nav/NavBar.tsx
+++ b/src/components/nav/NavBar.tsx
@@ -36,7 +36,7 @@ export function NavBar({ collapsed }: { collapsed?: boolean }) {
{l.to === pathname && (
)}
diff --git a/src/components/notifications/TxSuccessToast.tsx b/src/components/notifications/TxSuccessToast.tsx
index 8050792..e4a142f 100644
--- a/src/components/notifications/TxSuccessToast.tsx
+++ b/src/components/notifications/TxSuccessToast.tsx
@@ -1,47 +1,38 @@
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import { ExternalLink } from 'src/components/buttons/ExternalLink';
-import { ChainId, chainIdToChain } from 'src/config/chains';
+import { ChainId } from 'src/config/chains';
+import { getTxExplorerUrl } from 'src/features/transactions/utils';
import { logger } from 'src/utils/logger';
-export function useToastTxSuccess(
- isConfirmed?: boolean,
- txHash?: string,
- msg?: string,
- chainId: ChainId = ChainId.Celo,
-) {
- useEffect(() => {
- if (!isConfirmed || !txHash) return;
- logger.debug(msg);
- toastTxSuccess(txHash, msg, chainId);
- }, [isConfirmed, txHash, msg, chainId]);
-}
-
-export function toastTxSuccess(
- txHash?: string,
- msg = 'Transaction confirmed!',
- chainId: ChainId = ChainId.Celo,
-) {
- if (!txHash) return;
- const explorerUrl = chainIdToChain[chainId].explorerUrl;
- toast.success(, {
- autoClose: 15000,
- });
-}
-
-export function TxSuccessToast({
- msg,
+export function useToastTxSuccess({
+ isConfirmed,
txHash,
- explorerUrl,
+ message = 'Transaction confirmed!',
+ chainId = ChainId.Celo,
+ enabled = true,
}: {
- msg: string;
- txHash: string;
- explorerUrl: string;
+ isConfirmed?: boolean;
+ txHash?: string;
+ message?: string;
+ chainId?: ChainId;
+ enabled?: boolean;
}) {
+ useEffect(() => {
+ if (!isConfirmed || !txHash || !enabled) return;
+ logger.debug(message);
+ const explorerUrl = getTxExplorerUrl(txHash, chainId);
+ toast.success(, {
+ autoClose: 15000,
+ });
+ }, [isConfirmed, txHash, message, chainId, enabled]);
+}
+
+function TxSuccessToast({ message, explorerUrl }: { message: string; explorerUrl: string }) {
return (
- {msg + ' '}
-
+ {message + ' '}
+
See Details
diff --git a/src/features/account/AccountRegisterForm.tsx b/src/features/account/AccountRegisterForm.tsx
index 9d2c0dc..d665613 100644
--- a/src/features/account/AccountRegisterForm.tsx
+++ b/src/features/account/AccountRegisterForm.tsx
@@ -12,7 +12,7 @@ export function AccountRegisterForm({
}) {
const { writeContract, isLoading } = useWriteContractWithReceipt(
'account registration',
- refetchAccountDetails,
+ () => refetchAccountDetails,
);
const onClickCreate = () => {
diff --git a/src/features/locking/LockFlow.tsx b/src/features/locking/LockFlow.tsx
index 31510bf..7a65957 100644
--- a/src/features/locking/LockFlow.tsx
+++ b/src/features/locking/LockFlow.tsx
@@ -3,26 +3,35 @@ import { AccountRegisterForm } from 'src/features/account/AccountRegisterForm';
import { useAccountDetails } from 'src/features/account/hooks';
import { LockForm } from 'src/features/locking/LockForm';
import { LockActionType } from 'src/features/locking/types';
+import { TransactionConfirmation } from 'src/features/transactions/TransactionConfirmation';
+import { useTransactionFlowConfirmation } from 'src/features/transactions/hooks';
import { isNullish } from 'src/utils/typeof';
import { useAccount } from 'wagmi';
-export function LockFlow({ defaultAction }: { defaultAction?: LockActionType }) {
+export function LockFlow({
+ defaultAction,
+ closeModal,
+}: {
+ defaultAction?: LockActionType;
+ closeModal: () => void;
+}) {
const { address } = useAccount();
- const {
- isRegistered,
- isLoading: isLoadingRegistration,
- refetch: refetchAccountDetails,
- } = useAccountDetails(address);
+ const { isRegistered, refetch: refetchAccountDetails } = useAccountDetails(address);
+
+ const { confirmationDetails, onConfirmed } = useTransactionFlowConfirmation();
let Component;
- if (!address || isLoadingRegistration || isNullish(isRegistered)) {
+ if (!address || isNullish(isRegistered)) {
Component = Loading account data...;
} else if (!isRegistered) {
Component = ;
- // TODO lock complete screen here
+ } else if (!confirmationDetails) {
+ Component = ;
} else {
- Component = ;
+ Component = (
+
+ );
}
return (
diff --git a/src/features/locking/LockForm.tsx b/src/features/locking/LockForm.tsx
index c801c4d..aca708a 100644
--- a/src/features/locking/LockForm.tsx
+++ b/src/features/locking/LockForm.tsx
@@ -1,4 +1,3 @@
-import { lockedGoldABI } from '@celo/abis';
import { Form, Formik, FormikErrors, useFormikContext } from 'formik';
import { useEffect, useMemo } from 'react';
import { toast } from 'react-toastify';
@@ -7,7 +6,6 @@ import { AmountField } from 'src/components/input/AmountField';
import { RadioField } from 'src/components/input/RadioField';
import { TipBox } from 'src/components/layout/TipBox';
import { MIN_REMAINING_BALANCE } from 'src/config/consts';
-import { Addresses } from 'src/config/contracts';
import { useBalance } from 'src/features/account/hooks';
import { useIsGovernanceVoting } from 'src/features/governance/useVotingStatus';
import { getLockTxPlan } from 'src/features/locking/lockPlan';
@@ -26,6 +24,7 @@ import {
import { StakingBalances } from 'src/features/staking/types';
import { emptyStakeBalances, useStakingBalances } from 'src/features/staking/useStakingBalances';
import { useTransactionPlan, useWriteContractWithReceipt } from 'src/features/transactions/hooks';
+import { ConfirmationDetails } from 'src/features/transactions/types';
import { fromWeiRounded, toWei } from 'src/utils/amount';
import { logger } from 'src/utils/logger';
import { toTitleCase } from 'src/utils/strings';
@@ -40,9 +39,11 @@ const initialValues: LockFormValues = {
export function LockForm({
defaultAction,
showTip,
+ onConfirmed,
}: {
defaultAction?: LockActionType;
showTip?: boolean;
+ onConfirmed?: (details: ConfirmationDetails) => void;
}) {
const { address } = useAccount();
const { balance: walletBalance } = useBalance(address);
@@ -54,18 +55,24 @@ export function LockForm({
useTransactionPlan({
createTxPlan: (v) =>
getLockTxPlan(v, pendingWithdrawals || [], stakeBalances || emptyStakeBalances),
- onStepSuccess: refetch,
+ onStepSuccess: () => refetch,
+ onPlanSuccess: onConfirmed
+ ? (v, r) =>
+ onConfirmed({
+ message: `${v.action} successful`,
+ amount: v.amount,
+ receipt: r,
+ properties: [
+ { label: 'Action', value: toTitleCase(v.action) },
+ { label: 'Amount', value: `${v.amount} CELO` },
+ ],
+ })
+ : undefined,
});
const { writeContract, isLoading } = useWriteContractWithReceipt('lock/unlock', onTxSuccess);
const isInputDisabled = isLoading || isPlanStarted;
- const onSubmit = (values: LockFormValues) => {
- writeContract({
- address: Addresses.LockedGold,
- abi: lockedGoldABI,
- ...getNextTx(values),
- });
- };
+ const onSubmit = (values: LockFormValues) => writeContract(getNextTx(values));
const validate = (values: LockFormValues) => {
if (isNullish(walletBalance) || !lockedBalances || !stakeBalances || isNullish(isVoting)) {
diff --git a/src/features/locking/lockPlan.ts b/src/features/locking/lockPlan.ts
index 070acdf..a28b4e5 100644
--- a/src/features/locking/lockPlan.ts
+++ b/src/features/locking/lockPlan.ts
@@ -1,3 +1,5 @@
+import { lockedGoldABI } from '@celo/abis';
+import { Addresses } from 'src/config/contracts';
import { LockActionType, LockFormValues, PendingWithdrawal } from 'src/features/locking/types';
import { StakingBalances } from 'src/features/staking/types';
import { TxPlan } from 'src/features/transactions/types';
@@ -19,7 +21,15 @@ export function getLockTxPlan(
// TODO update this to account for staking, governance, and delegation revocations first
if (action === LockActionType.Unlock) {
- return [{ action, functionName: 'unlock', args: [amountWei] }];
+ return [
+ {
+ action,
+ address: Addresses.LockedGold,
+ abi: lockedGoldABI,
+ functionName: 'unlock',
+ args: [amountWei],
+ },
+ ];
} else if (action === LockActionType.Lock) {
const txs: TxPlan = [];
// Need relock from the pendings in reverse order
@@ -31,6 +41,8 @@ export function getLockTxPlan(
const txAmount = bigIntMin(amountRemaining, p.value);
txs.push({
action,
+ address: Addresses.LockedGold,
+ abi: lockedGoldABI,
functionName: 'relock',
args: [p.index, txAmount],
});
@@ -38,7 +50,13 @@ export function getLockTxPlan(
}
// If pending relocks didn't cover it
if (amountRemaining > 0) {
- txs.push({ action, functionName: 'lock', value: amountRemaining });
+ txs.push({
+ action,
+ address: Addresses.LockedGold,
+ abi: lockedGoldABI,
+ functionName: 'lock',
+ value: amountRemaining,
+ });
}
return txs;
} else if (action === LockActionType.Withdraw) {
@@ -49,6 +67,8 @@ export function getLockTxPlan(
if (p.timestamp <= now)
txs.push({
action,
+ address: Addresses.LockedGold,
+ abi: lockedGoldABI,
functionName: 'withdraw',
args: [p.index],
});
diff --git a/src/features/staking/StakeFlow.tsx b/src/features/staking/StakeFlow.tsx
index 6b75290..8afba4e 100644
--- a/src/features/staking/StakeFlow.tsx
+++ b/src/features/staking/StakeFlow.tsx
@@ -5,6 +5,8 @@ import { LockForm } from 'src/features/locking/LockForm';
import { StakeForm } from 'src/features/staking/StakeForm';
import { StakeActionType } from 'src/features/staking/types';
import { useStakingBalances } from 'src/features/staking/useStakingBalances';
+import { TransactionConfirmation } from 'src/features/transactions/TransactionConfirmation';
+import { useTransactionFlowConfirmation } from 'src/features/transactions/hooks';
import { isNullish } from 'src/utils/typeof';
import { useAccount } from 'wagmi';
@@ -12,38 +14,39 @@ import { useAccount } from 'wagmi';
export function StakeFlow({
defaultGroup,
defaultAction,
+ closeModal,
}: {
defaultGroup?: Address;
defaultAction?: StakeActionType;
+ closeModal: () => void;
}) {
const { address } = useAccount();
- const { lockedBalance, isLoading: isLoadingLocked } = useLockedBalance(address);
- const { stakeBalances, isLoading: isLoadingStaked } = useStakingBalances(address);
- const {
- isRegistered,
- isLoading: isLoadingRegistration,
- refetch: refetchAccountDetails,
- } = useAccountDetails(address);
+ const { lockedBalance } = useLockedBalance(address);
+ const { stakeBalances } = useStakingBalances(address);
+ const { isRegistered, refetch: refetchAccountDetails } = useAccountDetails(address);
+
+ const { confirmationDetails, onConfirmed } = useTransactionFlowConfirmation();
let Component;
- if (
- !address ||
- isLoadingLocked ||
- isLoadingStaked ||
- isLoadingRegistration ||
- isNullish(lockedBalance) ||
- isNullish(stakeBalances) ||
- isNullish(isRegistered)
- ) {
+ if (!address || isNullish(lockedBalance) || isNullish(stakeBalances) || isNullish(isRegistered)) {
Component = Loading staking data...;
} else if (!isRegistered) {
Component = ;
} else if (lockedBalance <= 0n && stakeBalances.total <= 0n) {
Component = ;
+ } else if (!confirmationDetails) {
+ Component = (
+
+ );
} else {
- Component = ;
+ Component = (
+
+ );
}
- // TODO stake complete screen here
return (
<>
diff --git a/src/features/staking/StakeForm.tsx b/src/features/staking/StakeForm.tsx
index 332a680..bce9f73 100644
--- a/src/features/staking/StakeForm.tsx
+++ b/src/features/staking/StakeForm.tsx
@@ -1,4 +1,3 @@
-import { electionABI } from '@celo/abis';
import { Form, Formik, FormikErrors, useField, useFormikContext } from 'formik';
import { SyntheticEvent, useCallback, useEffect, useMemo } from 'react';
import { IconButton } from 'src/components/buttons/IconButton';
@@ -8,7 +7,6 @@ import { AmountField } from 'src/components/input/AmountField';
import { RadioField } from 'src/components/input/RadioField';
import { DropdownMenu } from 'src/components/menus/Dropdown';
import { MIN_GROUP_SCORE_FOR_RANDOM, ZERO_ADDRESS } from 'src/config/consts';
-import { Addresses } from 'src/config/contracts';
import { LockedBalances } from 'src/features/locking/types';
import { useLockedStatus } from 'src/features/locking/useLockedStatus';
import { getStakeTxPlan } from 'src/features/staking/stakePlan';
@@ -21,6 +19,7 @@ import {
} from 'src/features/staking/types';
import { useStakingBalances } from 'src/features/staking/useStakingBalances';
import { useTransactionPlan, useWriteContractWithReceipt } from 'src/features/transactions/hooks';
+import { ConfirmationDetails } from 'src/features/transactions/types';
import { ValidatorGroupLogo } from 'src/features/validators/ValidatorGroupLogo';
import { ValidatorGroup } from 'src/features/validators/types';
import { useValidatorGroups } from 'src/features/validators/useValidatorGroups';
@@ -42,9 +41,11 @@ const initialValues: StakeFormValues = {
export function StakeForm({
defaultGroup,
defaultAction,
+ onConfirmed,
}: {
defaultGroup?: Address;
defaultAction?: StakeActionType;
+ onConfirmed: (details: ConfirmationDetails) => void;
}) {
const { address } = useAccount();
const { groups } = useValidatorGroups();
@@ -54,18 +55,24 @@ export function StakeForm({
const { getNextTx, txPlanIndex, numTxs, isPlanStarted, onTxSuccess } =
useTransactionPlan({
createTxPlan: (v) => getStakeTxPlan(v, groups || [], groupToStake || {}),
- onStepSuccess: refetch,
+ onStepSuccess: () => refetch,
+ onPlanSuccess: (v, r) =>
+ onConfirmed({
+ message: `${v.action} successful`,
+ amount: v.amount,
+ receipt: r,
+ properties: [
+ { label: 'Action', value: toTitleCase(v.action) },
+ { label: 'Group', value: findGroup(groups, v.group)?.name || 'Unknown' },
+ { label: 'Amount', value: `${v.amount} CELO` },
+ ],
+ }),
});
+
const { writeContract, isLoading } = useWriteContractWithReceipt('staking', onTxSuccess);
const isInputDisabled = isLoading || isPlanStarted;
- const onSubmit = (values: StakeFormValues) => {
- writeContract({
- address: Addresses.Election,
- abi: electionABI,
- ...getNextTx(values),
- });
- };
+ const onSubmit = (values: StakeFormValues) => writeContract(getNextTx(values));
const validate = (values: StakeFormValues) => {
if (!lockedBalances || !stakeBalances || !groupToStake || !groups) {
diff --git a/src/features/staking/stakePlan.ts b/src/features/staking/stakePlan.ts
index a82e84a..1bf3891 100644
--- a/src/features/staking/stakePlan.ts
+++ b/src/features/staking/stakePlan.ts
@@ -1,4 +1,6 @@
+import { electionABI } from '@celo/abis';
import { MIN_INCREMENTAL_VOTE_AMOUNT, ZERO_ADDRESS } from 'src/config/consts';
+import { Addresses } from 'src/config/contracts';
import { GroupToStake, StakeActionType, StakeFormValues } from 'src/features/staking/types';
import { TxPlan } from 'src/features/transactions/types';
import { ValidatorGroup } from 'src/features/validators/types';
@@ -36,6 +38,8 @@ function getStakeActionPlan(amountWei: bigint, group: Address, groups: Validator
return [
{
action: StakeActionType.Stake,
+ address: Addresses.Election,
+ abi: electionABI,
functionName: 'vote',
args: [group, amountWei, lesser, greater],
},
@@ -60,6 +64,8 @@ function getUnstakeActionPlan(
const { lesser, greater } = findLesserAndGreaterAfterVote(groups, group, pendingToRevoke * -1n);
txs.push({
action: StakeActionType.Unstake,
+ address: Addresses.Election,
+ abi: electionABI,
functionName: 'revokePending',
args: [group, pendingToRevoke, lesser, greater, groupIndex],
});
@@ -73,6 +79,8 @@ function getUnstakeActionPlan(
const { lesser, greater } = findLesserAndGreaterAfterVote(groups, group, amountRemaining * -1n);
txs.push({
action: StakeActionType.Unstake,
+ address: Addresses.Election,
+ abi: electionABI,
functionName: 'revokeActive',
args: [group, amountRemaining, lesser, greater, groupIndex],
});
diff --git a/src/features/transactions/TransactionConfirmation.tsx b/src/features/transactions/TransactionConfirmation.tsx
new file mode 100644
index 0000000..933dc9e
--- /dev/null
+++ b/src/features/transactions/TransactionConfirmation.tsx
@@ -0,0 +1,47 @@
+import { ExternalLink } from 'src/components/buttons/ExternalLink';
+import { SolidButton } from 'src/components/buttons/SolidButton';
+import { Checkmark } from 'src/components/icons/Checkmark';
+import { Amount } from 'src/components/numbers/Amount';
+import { ConfirmationDetails } from 'src/features/transactions/types';
+import { getTxExplorerUrl } from 'src/features/transactions/utils';
+import { toTitleCase } from 'src/utils/strings';
+
+export function TransactionConfirmation({
+ confirmation,
+ closeModal,
+}: {
+ confirmation: ConfirmationDetails;
+ closeModal: () => void;
+}) {
+ return (
+
+
+
+
+
+
+
{toTitleCase(confirmation.message)}
+
+
+
+ {confirmation.properties.map(({ label, value }) => (
+
+ ))}
+
+
Transaction
+
+ View in explorer
+
+
+
+
+
Close
+
+ );
+}
diff --git a/src/features/transactions/TransactionModal.tsx b/src/features/transactions/TransactionModal.tsx
index 60aa4fb..fb25d9d 100644
--- a/src/features/transactions/TransactionModal.tsx
+++ b/src/features/transactions/TransactionModal.tsx
@@ -50,7 +50,7 @@ export function TransactionModal() {
return (
-
+
);
diff --git a/src/features/transactions/hooks.ts b/src/features/transactions/hooks.ts
index 01cda13..9fccda1 100644
--- a/src/features/transactions/hooks.ts
+++ b/src/features/transactions/hooks.ts
@@ -1,12 +1,17 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useToastTxSuccess } from 'src/components/notifications/TxSuccessToast';
import { useToastError } from 'src/components/notifications/useToastError';
-import { TxPlan } from 'src/features/transactions/types';
+import { ConfirmationDetails, TxPlan } from 'src/features/transactions/types';
import { logger } from 'src/utils/logger';
import { toTitleCase } from 'src/utils/strings';
+import { TransactionReceipt } from 'viem';
import { useWaitForTransactionReceipt, useWriteContract } from 'wagmi';
-export function useWriteContractWithReceipt(description: string, onSuccess?: () => any) {
+export function useWriteContractWithReceipt(
+ description: string,
+ onSuccess?: (receipt: TransactionReceipt) => any,
+ showTxSuccessToast = false,
+) {
const {
data: hash,
error: writeError,
@@ -20,6 +25,7 @@ export function useWriteContractWithReceipt(description: string, onSuccess?: ()
isSuccess: isConfirmed,
error: waitError,
isError: isWaitError,
+ data: receipt,
} = useWaitForTransactionReceipt({
hash,
confirmations: 1,
@@ -34,20 +40,26 @@ export function useWriteContractWithReceipt(description: string, onSuccess?: ()
waitError,
`Error confirming ${description} transaction, please ensure the transaction is valid.`,
);
- useToastTxSuccess(isConfirmed, hash, `${toTitleCase(description)} transaction is confirmed!`);
+ // TODO remove?
+ useToastTxSuccess({
+ isConfirmed,
+ txHash: hash,
+ message: `${toTitleCase(description)} transaction is confirmed!`,
+ enabled: showTxSuccessToast,
+ });
// Run onSuccess when tx is confirmed
// Some extra state is needed to ensure this only runs once per tx
const [hasRunOnSuccess, setHasRunOnSuccess] = useState(false);
useEffect(() => {
- if (hash && isConfirmed && !hasRunOnSuccess && onSuccess) {
+ if (hash && receipt && isConfirmed && !hasRunOnSuccess && onSuccess) {
setHasRunOnSuccess(true);
- onSuccess();
+ onSuccess(receipt);
}
- }, [hash, isConfirmed, hasRunOnSuccess, onSuccess]);
+ }, [hash, receipt, isConfirmed, hasRunOnSuccess, onSuccess]);
useEffect(() => {
- if (!hash || !isConfirmed) setHasRunOnSuccess(false);
- }, [hash, isConfirmed]);
+ if (!hash || !receipt || !isConfirmed) setHasRunOnSuccess(false);
+ }, [hash, receipt, isConfirmed]);
return {
hash,
@@ -66,10 +78,11 @@ export function useTransactionPlan({
onPlanSuccess,
}: {
createTxPlan: (v: FormValues) => TxPlan;
- onStepSuccess?: () => any;
- onPlanSuccess?: () => any;
+ onStepSuccess?: (receipt: TransactionReceipt) => any;
+ onPlanSuccess?: (v: FormValues, receipt: TransactionReceipt) => any;
}) {
const [txPlan, setTxPlan] = useState(undefined);
+ const [formValues, setFormValues] = useState(undefined);
const [txPlanIndex, setTxPlanIndex] = useState(0);
const isPlanStarted = txPlanIndex > 0;
@@ -78,6 +91,7 @@ export function useTransactionPlan({
if (txPlan) return txPlan;
const plan = createTxPlan(v);
setTxPlan(plan);
+ setFormValues(v);
return plan;
},
[txPlan, createTxPlan],
@@ -92,17 +106,29 @@ export function useTransactionPlan({
const numTxs = useMemo(() => (txPlan ? txPlan.length : 0), [txPlan]);
- const onTxSuccess = useCallback(() => {
- logger.debug(`Executing onSuccess for tx ${txPlanIndex + 1} of ${numTxs}`);
- if (onStepSuccess) onStepSuccess();
- if (txPlanIndex >= numTxs - 1) {
- setTxPlan(undefined);
- setTxPlanIndex(0);
- if (onPlanSuccess) onPlanSuccess();
- } else {
- setTxPlanIndex(txPlanIndex + 1);
- }
- }, [numTxs, txPlanIndex, onStepSuccess, onPlanSuccess]);
+ const onTxSuccess = useCallback(
+ (receipt: TransactionReceipt) => {
+ if (!formValues) throw new Error('onTxSuccess:formValues is undefined');
+ logger.debug(`Executing onSuccess for tx ${txPlanIndex + 1} of ${numTxs}`);
+ if (onStepSuccess) onStepSuccess(receipt);
+ if (txPlanIndex >= numTxs - 1) {
+ setTxPlan(undefined);
+ setTxPlanIndex(0);
+ if (onPlanSuccess) onPlanSuccess(formValues, receipt);
+ } else {
+ setTxPlanIndex(txPlanIndex + 1);
+ }
+ },
+ [numTxs, txPlanIndex, formValues, onStepSuccess, onPlanSuccess],
+ );
return { getTxPlan, getNextTx, txPlanIndex, numTxs, isPlanStarted, onTxSuccess };
}
+
+export function useTransactionFlowConfirmation() {
+ const [confirmationDetails, setConfirmationDetails] = useState(
+ undefined,
+ );
+ const onConfirmed = useCallback((d: ConfirmationDetails) => setConfirmationDetails(d), []);
+ return { confirmationDetails, onConfirmed };
+}
diff --git a/src/features/transactions/types.ts b/src/features/transactions/types.ts
index 2a04037..9b9523a 100644
--- a/src/features/transactions/types.ts
+++ b/src/features/transactions/types.ts
@@ -1,3 +1,5 @@
+import type { TransactionReceipt } from 'viem';
+
export enum TxModalType {
Lock = 'lock',
Stake = 'stake',
@@ -7,7 +9,16 @@ export enum TxModalType {
export type TxPlan = Array<{
action: A;
+ address: Address;
+ abi: any;
functionName: F;
args?: Array;
value?: bigint;
}>;
+
+export interface ConfirmationDetails {
+ message: string;
+ amount: bigint | number;
+ receipt: TransactionReceipt;
+ properties: Array<{ label: string; value: string }>;
+}
diff --git a/src/features/transactions/utils.ts b/src/features/transactions/utils.ts
new file mode 100644
index 0000000..625f2f4
--- /dev/null
+++ b/src/features/transactions/utils.ts
@@ -0,0 +1,6 @@
+import { ChainId, chainIdToChain } from 'src/config/chains';
+
+export function getTxExplorerUrl(hash: string, chainId: ChainId = ChainId.Celo) {
+ const chain = chainIdToChain[chainId];
+ return `${chain.explorerUrl}/tx/${hash}`;
+}