diff --git a/app/components/home/stacking-card.tsx b/app/components/home/stacking-card.tsx index a0b76c4e8..00ae702fc 100644 --- a/app/components/home/stacking-card.tsx +++ b/app/components/home/stacking-card.tsx @@ -12,16 +12,13 @@ import { type StackingCardProps = any; -export const StackingCard: FC = props => { - const {} = props; - +export const StackingCard: FC = () => { const { stackingDetails, stackerInfo, nextCycleInfo } = useSelector((state: RootState) => ({ stackingDetails: selectPoxInfo(state), stackerInfo: selectStackerInfo(state), nextCycleInfo: selectNextCycleInfo(state), })); - // if (stackingDetails && stackerInfo && !stackingError && !nextCycleInfo?.isStackingCallPending) return ( = ({ onClose, numCycles, poxA const broadcastTx = async () => { if (balance === null) return; - const broadcastActions = { + const broadcastActions: Omit = { amount: new BigNumber(balance), isStackingCall: true, - onBroadcastSuccess: () => history.push(routes.HOME), + onBroadcastSuccess: txId => { + dispatch(activeStackingTx({ txId })); + history.push(routes.HOME); + }, onBroadcastFail: () => setStep(StackingModalStep.FailedContractCall), }; diff --git a/app/pages/app.tsx b/app/pages/app.tsx index 5d159ae47..a1c6f52b1 100644 --- a/app/pages/app.tsx +++ b/app/pages/app.tsx @@ -24,15 +24,18 @@ import { fetchCoreDetails, fetchStackerInfo, fetchStackingInfo, + removeStackingTx, + selectActiveStackingTxId, } from '@store/stacking'; export const App: FC = ({ children }) => { const dispatch = useDispatch(); - const { address, activeNode, pendingTxs } = useSelector((state: RootState) => ({ + const { address, activeNode, pendingTxs, activeStackingTx } = useSelector((state: RootState) => ({ address: selectAddress(state), activeNode: selectActiveNodeApi(state), pendingTxs: selectPendingTransactions(state), + activeStackingTx: selectActiveStackingTxId(state), })); const initAppWithStxAddressInfo = useCallback(() => { @@ -69,6 +72,7 @@ export const App: FC = ({ children }) => { dispatch(fetchStackingInfo()); dispatch(fetchCoreDetails()); dispatch(fetchBlockTimeInfo()); + dispatch(fetchStackerInfo(address)); } }, [address, activeNode, initAppWithStxAddressInfo, dispatch]); @@ -80,28 +84,19 @@ export const App: FC = ({ children }) => { dispatch(fetchCoreDetails()); }, 5_000); - useEffect(() => { - if (address) dispatch(fetchStackerInfo(address)); - }, [dispatch, address]); - useEffect(() => { async function run() { - console.log('new stacking call'); - const [firstTxId] = pendingTxs.filter(tx => tx.isStackingCall); - if (!firstTxId) return; - console.log({ firstTxId }); - const result = await watchContractExecution({ + if (!activeStackingTx || !address) return; + await watchContractExecution({ nodeUrl: activeNode.url, - txId: firstTxId.tx_id, + txId: activeStackingTx, }); - dispatch(fetchStackingInfo()); - dispatch(fetchCoreDetails()); - dispatch(fetchBlockTimeInfo()); - console.log(result); + dispatch(fetchStackerInfo(address)); + setTimeout(() => dispatch(removeStackingTx()), 2000); } void run(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pendingTxs]); + }, [activeStackingTx]); useEffect(() => { const wsUrl = new URL(activeNode.url); diff --git a/app/store/home/home.reducer.ts b/app/store/home/home.reducer.ts index a409ad0ba..ae1ea87d1 100644 --- a/app/store/home/home.reducer.ts +++ b/app/store/home/home.reducer.ts @@ -1,7 +1,12 @@ import { createSlice, createSelector } from '@reduxjs/toolkit'; import { selectAddressBalance } from '@store/address'; import { selectIsStackingCallPending } from '@store/pending-transaction'; -import { selectLoadingStacking, selectPoxInfo, selectStackerInfo } from '@store/stacking'; +import { + selectActiveStackingTxId, + selectLoadingStacking, + selectPoxInfo, + selectStackerInfo, +} from '@store/stacking'; import BigNumber from 'bignumber.js'; import { RootState } from '..'; @@ -64,13 +69,15 @@ const selectMeetsMinStackingThreshold = createSelector( export const selectHomeCardState = createSelector( selectLoadingCardResources, + selectActiveStackingTxId, selectMeetsMinStackingThreshold, selectIsStackingCallPending, selectStackerInfo, - (loadingResources, meetsMinThreshold, stackingCallPending, stackerInfo) => { + (loadingResources, activeStackingTxId, meetsMinThreshold, stackingCallPending, stackerInfo) => { if (loadingResources) return HomeCardState.LoadingResources; if (!meetsMinThreshold) return HomeCardState.NotEnoughStx; - if (stackingCallPending) return HomeCardState.StackingPendingContactCall; + if (stackingCallPending || typeof activeStackingTxId === 'string') + return HomeCardState.StackingPendingContactCall; if (stackerInfo?.isPreStackingPeriodStart) return HomeCardState.StackingPreCycle; if (stackerInfo?.isCurrentlyStacking) return HomeCardState.StackingActive; if (meetsMinThreshold) return HomeCardState.EligibleToParticipate; diff --git a/app/store/stacking/stacking.actions.ts b/app/store/stacking/stacking.actions.ts index 9a33a9b8f..75481c723 100644 --- a/app/store/stacking/stacking.actions.ts +++ b/app/store/stacking/stacking.actions.ts @@ -1,4 +1,4 @@ -import { createAsyncThunk } from '@reduxjs/toolkit'; +import { createAsyncThunk, createAction } from '@reduxjs/toolkit'; import { selectActiveNodeApi } from '@store/stacks-node/stacks-node.reducer'; import { RootState } from '@store/index'; @@ -43,3 +43,9 @@ export const fetchStackerInfo = createAsyncThunk( throw new Error(); } ); + +export const activeStackingTx = createAction<{ txId: string }>( + 'stacking/call-stacking-contract-tx' +); + +export const removeStackingTx = createAction('stacking/remove-active-tx'); diff --git a/app/store/stacking/stacking.reducer.ts b/app/store/stacking/stacking.reducer.ts index e407ff0fe..be10c0794 100644 --- a/app/store/stacking/stacking.reducer.ts +++ b/app/store/stacking/stacking.reducer.ts @@ -8,15 +8,17 @@ import { createSlice, PayloadAction, createSelector } from '@reduxjs/toolkit'; import { RootState } from '..'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; +import { NETWORK } from '@constants/index'; +import { StackerInfo, StackerInfoSuccess } from '@utils/stacking/pox'; +import { selectIsStackingCallPending } from '@store/pending-transaction'; import { fetchStackingInfo, fetchCoreDetails, fetchBlockTimeInfo, fetchStackerInfo, + activeStackingTx, + removeStackingTx, } from './stacking.actions'; -import { NETWORK } from '@constants/index'; -import { StackerInfo, StackerInfoSuccess } from '@utils/stacking/pox'; -import { selectIsStackingCallPending } from '@store/pending-transaction'; export enum StackingStatus { NotStacking = 'NotStacking', @@ -26,7 +28,12 @@ export enum StackingStatus { } export interface StackingState { + initialRequestsComplete: Record< + 'poxInfo' | 'coreNodeInfo' | 'blockTimeInfo' | 'stackerInfo', + boolean + >; error: string | null; + contractCallTx: string | null; poxInfo: CoreNodePoxResponse | null; coreNodeInfo: CoreNodeInfoResponse | null; blockTimeInfo: NetworkBlockTimesResponse | null; @@ -34,7 +41,14 @@ export interface StackingState { } const initialState: StackingState = { + initialRequestsComplete: { + poxInfo: false, + coreNodeInfo: false, + blockTimeInfo: false, + stackerInfo: false, + }, error: null, + contractCallTx: null, poxInfo: null, coreNodeInfo: null, blockTimeInfo: null, @@ -46,27 +60,42 @@ export const stackingSlice = createSlice({ initialState, reducers: {}, extraReducers: { - [fetchStackingInfo.fulfilled.toString()]: (state, a: PayloadAction) => { - state.poxInfo = a.payload; + [fetchStackingInfo.fulfilled.toString()]: ( + state, + action: PayloadAction + ) => { + state.initialRequestsComplete.poxInfo = true; + state.poxInfo = action.payload; }, - [fetchCoreDetails.fulfilled.toString()]: (state, a: PayloadAction) => { - state.coreNodeInfo = a.payload; + [fetchCoreDetails.fulfilled.toString()]: ( + state, + action: PayloadAction + ) => { + state.initialRequestsComplete.coreNodeInfo = true; + state.coreNodeInfo = action.payload; }, [fetchBlockTimeInfo.fulfilled.toString()]: ( state, - a: PayloadAction + action: PayloadAction ) => { - state.blockTimeInfo = a.payload; + state.initialRequestsComplete.blockTimeInfo = true; + state.blockTimeInfo = action.payload; }, - [fetchStackerInfo.fulfilled.toString()]: (state, a: PayloadAction) => { - // if (a.payload === ) - if ('error' in a.payload) { - state.error = a.payload.error; + [fetchStackerInfo.fulfilled.toString()]: (state, action: PayloadAction) => { + state.initialRequestsComplete.stackerInfo = true; + if ('error' in action.payload) { + state.error = action.payload.error; return; } - state.stackerInfo = a.payload; + state.stackerInfo = action.payload; state.error = null; }, + [activeStackingTx.toString()]: (state, action: PayloadAction<{ txId: string }>) => { + state.contractCallTx = action.payload.txId; + }, + [removeStackingTx.toString()]: state => { + state.contractCallTx = null; + }, }, }); @@ -81,14 +110,15 @@ export const selectBlockTimeInfo = createSelector( export const selectStackingError = createSelector(selectStackingState, state => state.error); -export const selectLoadingStacking = createSelector(selectStackingState, state => { - return ( - state.coreNodeInfo === null && - state.poxInfo === null && - state.error === null && - state.stackerInfo === null - ); -}); +export const selectLoadingStacking = createSelector( + selectStackingState, + state => !Object.values(state.initialRequestsComplete).every(value => value === true) +); + +export const selectActiveStackingTxId = createSelector( + selectStackingState, + state => state.contractCallTx +); export const selectPoxInfo = createSelector(selectStackingState, state => state.poxInfo); diff --git a/app/store/transaction/transaction.actions.ts b/app/store/transaction/transaction.actions.ts index 75357490b..709bfd4f1 100644 --- a/app/store/transaction/transaction.actions.ts +++ b/app/store/transaction/transaction.actions.ts @@ -11,7 +11,6 @@ import { safelyFormatHexTxid } from '@utils/safe-handle-txid'; import { addPendingTransaction } from '@store/pending-transaction'; import { Dispatch, GetState } from '@store/index'; import { selectActiveNodeApi } from '@store/stacks-node'; -import { isStackingTx } from '../../utils/tx-utils'; export const pendingTransactionSuccessful = createAction( 'transactions/pending-transaction-successful' @@ -54,7 +53,7 @@ export const broadcastTxFail = createAction( 'transactions/broadcast-transactions-fail' ); -interface BroadcastTransactionArgs { +export interface BroadcastTransactionArgs { transaction: StacksTransaction; amount: BigNumber; isStackingCall?: boolean;