Skip to content

Commit

Permalink
refactor for multi tx submission
Browse files Browse the repository at this point in the history
  • Loading branch information
rossbulat committed Nov 30, 2024
1 parent 0ff18a0 commit 0b08347
Show file tree
Hide file tree
Showing 48 changed files with 244 additions and 248 deletions.
15 changes: 13 additions & 2 deletions packages/app/src/api/txSubmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import type { TxSubmissionItem } from './types';
export class TxSubmission {
static uids: TxSubmissionItem[] = [];

static addUid({ from }: { from: MaybeAddress }) {
static addUid({ from, tag }: { from: MaybeAddress; tag?: string }) {
// If tag already exists, delete the entry.
if (tag) {
this.uids = this.uids.filter((item) => item.tag !== tag);
}
const newUid = this.uids.length + 1;
this.uids.push({ uid: newUid, processing: false, from });
this.uids.push({ uid: newUid, processing: false, from, fee: 0n, tag });
this.dispatchEvent();
return newUid;
}

static updateFee(uid: number, fee: bigint) {
this.uids = this.uids.map((item) =>
item.uid === uid ? { ...item, fee } : item
);
this.dispatchEvent();
}

static removeUid(id: number) {
this.uids = this.uids.filter(({ uid }) => uid !== id);
this.dispatchEvent();
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export type ApiChainType = 'relay' | 'system';

export type TxSubmissionItem = {
uid: number;
tag?: string;
fee: bigint;
from: MaybeAddress;
processing: boolean;
};
9 changes: 6 additions & 3 deletions packages/app/src/canvas/CreatePool/Bond/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ import { useTranslation } from 'react-i18next';

export const Bond = ({ section }: SetupStepProps) => {
const { t } = useTranslation('pages');
const { txFees } = useTxMeta();
const { getTxSubmissionByTag } = useTxMeta();
const { activeAccount } = useActiveAccounts();
const { getPoolSetup, setActiveAccountSetup } = useSetup();

const txSubmission = getTxSubmissionByTag('createPool');
const fee = txSubmission?.fee || 0n;

const setup = getPoolSetup(activeAccount);
const { progress } = setup;

Expand Down Expand Up @@ -76,13 +79,13 @@ export const Bond = ({ section }: SetupStepProps) => {
/>
<MotionContainer thisSection={section} activeSection={setup.section}>
<BondFeedback
syncing={txFees === 0n}
syncing={fee === 0n}
bondFor="pool"
inSetup
listenIsValid={(valid) => setBondValid(valid)}
defaultBond={initialBondValue}
setters={[handleSetBond]}
txFees={txFees}
txFees={fee}
maxWidth
/>
<CreatePoolStatusBar value={new BigNumber(bond.bond)} />
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/canvas/CreatePool/Summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const Summary = ({ section }: SetupStepProps) => {
return newBatchCall(tx, activeAccount);
};
const submitExtrinsic = useSubmitExtrinsic({
tag: 'createPool',
tx: getTx(),
from: activeAccount,
shouldSubmit: true,
Expand Down
9 changes: 6 additions & 3 deletions packages/app/src/canvas/NominatorSetup/Bond/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import { useTranslation } from 'react-i18next';

export const Bond = ({ section }: SetupStepProps) => {
const { t } = useTranslation('pages');
const { getTxSubmissionByTag } = useTxMeta();
const { activeAccount } = useActiveAccounts();
const { txFees } = useTxMeta();
const { getNominatorSetup, setActiveAccountSetup } = useSetup();
const setup = getNominatorSetup(activeAccount);
const { progress } = setup;

const txSubmission = getTxSubmissionByTag('nominatorSetup');
const fee = txSubmission?.fee || 0n;

// either free to bond or existing setup value
const initialBondValue = progress.bond || '0';

Expand Down Expand Up @@ -75,13 +78,13 @@ export const Bond = ({ section }: SetupStepProps) => {
/>
<MotionContainer thisSection={section} activeSection={setup.section}>
<BondFeedback
syncing={txFees === 0n}
syncing={fee === 0n}
bondFor="nominator"
inSetup
listenIsValid={(valid) => setBondValid(valid)}
defaultBond={initialBondValue}
setters={[handleSetBond]}
txFees={txFees}
txFees={fee}
maxWidth
/>
<NominateStatusBar value={new BigNumber(bond.bond)} />
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/canvas/NominatorSetup/Summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const Summary = ({ section }: SetupStepProps) => {
};

const submitExtrinsic = useSubmitExtrinsic({
tag: 'nominatorSetup',
tx: getTxs(),
from: activeAccount,
shouldSubmit: true,
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/contexts/Balances/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const defaultBalancesContext: BalancesContextInterface = {
getPayee: (address) => defaultPayee,
getPoolMembership: (address) => null,
getNominations: (address) => [],
getEdReserved: (address, existentialDeposit) => new BigNumber(0),
};
2 changes: 2 additions & 0 deletions packages/app/src/contexts/Balances/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => {
getPayee,
getPoolMembership,
getNominations,
getEdReserved,
} = useActiveBalances({
accounts: [activeAccount, activeProxy, controller],
});
Expand Down Expand Up @@ -121,6 +122,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => {
getPayee,
getPoolMembership,
getNominations,
getEdReserved,
}}
>
{children}
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/contexts/Balances/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface BalancesContextInterface {
getPayee: (address: MaybeAddress) => PayeeConfig;
getPoolMembership: (address: MaybeAddress) => PoolMembership | null;
getNominations: (address: MaybeAddress) => Targets;
getEdReserved: (
address: MaybeAddress,
existentialDeposit: BigNumber
) => BigNumber;
}

export type ActiveBalancesState = Record<string, ActiveBalance>;
Expand Down
7 changes: 1 addition & 6 deletions packages/app/src/contexts/TxMeta/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,5 @@ import type { TxMetaContextInterface } from './types';
export const defaultTxMeta: TxMetaContextInterface = {
uids: [],
getTxSubmission: (uid) => undefined,
setSender: (s) => {},
txFees: 0n,
txFeesValid: false,
setTxFees: (f) => {},
resetTxFees: () => {},
notEnoughFunds: false,
getTxSubmissionByTag: (tag) => undefined,
};
51 changes: 4 additions & 47 deletions packages/app/src/contexts/TxMeta/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { useEffectIgnoreInitial } from '@w3ux/hooks';
import type { TxSubmissionItem } from 'api/types';
import { useApi } from 'contexts/Api';
import { isCustomEvent } from 'controllers/utils';
import { useActiveBalances } from 'hooks/useActiveBalances';
import type { ReactNode } from 'react';
import { createContext, useContext, useRef, useState } from 'react';
import type { MaybeAddress } from 'types';
import { useEventListener } from 'usehooks-ts';
import * as defaults from './defaults';
import type { TxMetaContextInterface } from './types';
Expand All @@ -20,35 +16,9 @@ export const TxMetaContext = createContext<TxMetaContextInterface>(
export const useTxMeta = () => useContext(TxMetaContext);

export const TxMetaProvider = ({ children }: { children: ReactNode }) => {
const {
consts: { existentialDeposit },
} = useApi();

// Store the transaction fees for the transaction.
const [txFees, setTxFees] = useState<bigint>(0n);

// Store the sender of the transaction.
const [sender, setSender] = useState<MaybeAddress>(null);

// Store whether the sender does not have enough funds.
const [notEnoughFunds, setNotEnoughFunds] = useState<boolean>(false);

// Listen to balance updates for the tx sender.
const { getBalance, getEdReserved } = useActiveBalances({
accounts: [sender],
});

// Store uids of transactions, along with their processing status.
const [uids, setUids] = useState<TxSubmissionItem[]>([]);

// Utility to reset transaction fees to zero.
const resetTxFees = () => {
setTxFees(0n);
};

// Check if the transaction fees are valid.
const txFeesValid = txFees === 0n || notEnoughFunds ? false : true;

const handleNewUidStatus = (e: Event) => {
if (isCustomEvent(e)) {
const { uids: eventUids } = e.detail;
Expand All @@ -60,15 +30,9 @@ export const TxMetaProvider = ({ children }: { children: ReactNode }) => {
const getTxSubmission = (uid?: number) =>
uids.find((item) => item.uid === uid);

// Refresh not enough funds status when sender, balance or txFees change.
const senderBalances = getBalance(sender);
useEffectIgnoreInitial(() => {
const edReserved = getEdReserved(sender, existentialDeposit);
const { free, frozen } = senderBalances;
const balanceforTxFees = free.minus(edReserved).minus(frozen);

setNotEnoughFunds(balanceforTxFees.minus(txFees.toString()).isLessThan(0));
}, [txFees, sender, senderBalances]);
// Get a tx submission by tag.
const getTxSubmissionByTag = (tag?: string) =>
uids.find((item) => item.tag === tag);

useEventListener(
'new-tx-uid-status',
Expand All @@ -81,14 +45,7 @@ export const TxMetaProvider = ({ children }: { children: ReactNode }) => {
value={{
uids,
getTxSubmission,
setSender,
// TODO: add to tx `uid` state.
txFees,
txFeesValid,
setTxFees,
resetTxFees,
// TODO: add to tx `uid` state.
notEnoughFunds,
getTxSubmissionByTag,
}}
>
{children}
Expand Down
8 changes: 1 addition & 7 deletions packages/app/src/contexts/TxMeta/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only

import type { TxSubmissionItem } from 'api/types';
import type { MaybeAddress } from 'types';

export interface TxMetaContextInterface {
uids: TxSubmissionItem[];
getTxSubmission: (uid?: number) => TxSubmissionItem | undefined;
setSender: (s: MaybeAddress) => void;
txFees: bigint;
txFeesValid: boolean;
setTxFees: (f: bigint) => void;
resetTxFees: () => void;
notEnoughFunds: boolean;
getTxSubmissionByTag: (tag: string) => TxSubmissionItem | undefined;
}
55 changes: 30 additions & 25 deletions packages/app/src/hooks/useSubmitExtrinsic/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { useLedgerHardware } from 'contexts/LedgerHardware';
import { getLedgerApp } from 'contexts/LedgerHardware/Utils';
import { useNetwork } from 'contexts/Network';
import { usePrompt } from 'contexts/Prompt';
import { useTxMeta } from 'contexts/TxMeta';
import { useWalletConnect } from 'contexts/WalletConnect';
import { Notifications } from 'controllers/Notifications';
import { useProxySupported } from 'hooks/useProxySupported';
Expand All @@ -35,6 +34,7 @@ import { useTranslation } from 'react-i18next';
import type { UseSubmitExtrinsic, UseSubmitExtrinsicProps } from './types';
export const useSubmitExtrinsic = ({
tx,
tag,
from,
shouldSubmit,
callbackSubmit,
Expand All @@ -50,19 +50,18 @@ export const useSubmitExtrinsic = ({
const { extensionsStatus } = useExtensions();
const { isProxySupported } = useProxySupported();
const { openPromptWith, closePrompt } = usePrompt();
const { txFees, setTxFees, setSender } = useTxMeta();
const { handleResetLedgerTask } = useLedgerHardware();
const { getAccount, requiresManualSign } = useImportedAccounts();

// Store the uid for this transaction.
const [uid, setUid] = useState<number>(0);

// Store whether the transaction is in progress.
const [submitting, setSubmitting] = useState<boolean>(false);

// Track for one-shot transaction reset after submission.
const txSubmitted = useRef<boolean>(false);

// Generate a UID for this transaction.
const uid = TxSubmission.addUid({ from });

// If proxy account is active, wrap tx in a proxy call and set the sender to the proxy account. If
// already wrapped, update `from` address and return.
if (
Expand Down Expand Up @@ -91,20 +90,6 @@ export const useSubmitExtrinsic = ({
}
}

// Calculate the estimated tx fee of the transaction.
const calculateEstimatedFee = async () => {
if (tx === null) {
return;
}

// get payment info
// TODO: Send to `uid` info.
const partialFee = (await tx.getPaymentInfo(from)).partial_fee;
if (partialFee !== txFees) {
setTxFees(partialFee);
}
};

// Extrinsic submission handler.
const onSubmit = async () => {
if (from === null) {
Expand Down Expand Up @@ -289,13 +274,33 @@ export const useSubmitExtrinsic = ({
}
};

// Refresh state upon `tx` updates.
// Initialise tx submission.
useEffect(() => {
// ensure sender is up to date.
setSender(from);
// re-calculate estimated tx fee.
calculateEstimatedFee();
}, [tx?.decodedCall?.toString(), from]);
// Add a new uid for this transaction.
if (uid === 0) {
const newUid = TxSubmission.addUid({ from, tag });
setUid(newUid);
}
}, []);

// Re-fetch tx fee if tx changes.
useEffect(() => {
const fetchTxFee = async () => {
if (!tx) {
return;
}
const fee = (await tx.getPaymentInfo(from)).partial_fee;
TxSubmission.updateFee(uid, fee);
};
if (uid > 0) {
fetchTxFee();
}
}, [
uid,
JSON.stringify(tx?.decodedCall, (_, value) =>
typeof value === 'bigint' ? value.toString() : value
),
]);

return {
uid,
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/hooks/useSubmitExtrinsic/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AnyApi, MaybeAddress } from 'types';

export interface UseSubmitExtrinsicProps {
tx: AnyApi;
tag?: string;
from: MaybeAddress;
shouldSubmit: boolean;
callbackSubmit?: () => void;
Expand Down
Loading

0 comments on commit 0b08347

Please sign in to comment.