Skip to content

Commit

Permalink
Tim/eng 3475 nonce not updating correctly after switching accounts (#725
Browse files Browse the repository at this point in the history
)

* fix: regenerate the unsignedTx with correct stxAddress after switch account

* fix: minor cleanup

* fix tx-request account switching

* updated core version

* added type checking and default values for feeMultipliers

---------

Co-authored-by: Mahmoud Aboelenein <mahmoud@secretkeylabs.com>
  • Loading branch information
teebszet and m-aboelenein committed Dec 27, 2023
1 parent 5e33c8d commit 45d9f63
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 62 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@ledgerhq/hw-transport-webusb": "^6.27.13",
"@phosphor-icons/react": "^2.0.10",
"@react-spring/web": "^9.6.1",
"@secretkeylabs/xverse-core": "6.0.1-3d747a3",
"@secretkeylabs/xverse-core": "7.0.0",
"@stacks/connect": "7.4.1",
"@stacks/stacks-blockchain-api-types": "6.1.1",
"@stacks/transactions": "6.9.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/transactionSetting/editNonce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function EditNonce({ nonce, setNonce }: Props) {

useEffect(() => {
setNonce(nonceInput);
}, [nonceInput]);
}, [nonceInput, setNonce]);

return (
<NonceContainer>
Expand Down
1 change: 1 addition & 0 deletions src/app/hooks/queries/useStxWalletData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux';
import useNetworkSelector from '../useNetwork';
import useWalletSelector from '../useWalletSelector';

// TODO refactor: no need to put this in store. use this hook instead
export const useStxWalletData = () => {
const dispatch = useDispatch();
const { stxAddress } = useWalletSelector();
Expand Down
123 changes: 70 additions & 53 deletions src/app/screens/transactionRequest/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useStxTransactionRequest from '@hooks/useStxTransactionRequest';
import useWalletReducer from '@hooks/useWalletReducer';
import useWalletSelector from '@hooks/useWalletSelector';
import {
Account,
buf2hex,
Coin,
ContractFunction,
Expand All @@ -18,6 +19,7 @@ import { ContractCallPayload, ContractDeployPayload } from '@stacks/connect';
import { StacksTransaction } from '@stacks/transactions';
import { getNetworkType, isHardwareAccount } from '@utils/helper';
import { useEffect, useState } from 'react';
import toast from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
import { MoonLoader } from 'react-spinners';
import styled from 'styled-components';
Expand All @@ -31,8 +33,7 @@ const LoaderContainer = styled.div((props) => ({
}));

function TransactionRequest() {
const { stxAddress, network, stxPublicKey, feeMultipliers, accountsList, selectedAccount } =
useWalletSelector();
const { network, feeMultipliers, accountsList, selectedAccount } = useWalletSelector();
const { payload, tabId, requestToken, stacksTransaction } = useStxTransactionRequest();
const navigate = useNavigate();
const selectedNetwork = useNetworkSelector();
Expand All @@ -42,17 +43,23 @@ function TransactionRequest() {
const [coinsMetaData, setCoinsMetaData] = useState<Coin[] | null>(null);
const [codeBody, setCodeBody] = useState(undefined);
const [contractName, setContractName] = useState(undefined);
const [hasSwitchedAccount, setHasSwitchedAccount] = useState(false);
const [attachment, setAttachment] = useState<Buffer | undefined>(undefined);

const handleTokenTransferRequest = async (tokenTransferPayload: any) => {
const stxPendingTxData = await fetchStxPendingTxData(stxAddress, selectedNetwork);
const handleTokenTransferRequest = async (tokenTransferPayload: any, requestAccount: Account) => {
const stxPendingTxData = await fetchStxPendingTxData(
requestAccount.stxAddress,
selectedNetwork,
);
const unsignedSendStxTx = await getTokenTransferRequest(
tokenTransferPayload.recipient,
tokenTransferPayload.amount,
tokenTransferPayload.memo!,
stxPublicKey,
feeMultipliers!,
requestAccount.stxPublicKey,
{
stxSendTxMultiplier: feeMultipliers?.stxSendTxMultiplier || 1,
poolStackingTxMultiplier: feeMultipliers?.poolStackingTxMultiplier || 1,
otherTxMultiplier: feeMultipliers?.otherTxMultiplier || 1,
},
selectedNetwork,
stxPendingTxData || [],
stacksTransaction?.auth,
Expand All @@ -69,16 +76,19 @@ function TransactionRequest() {
});
};

const handleContractCallRequest = async (contractCallPayload: ContractCallPayload) => {
const handleContractCallRequest = async (
contractCallPayload: ContractCallPayload,
requestAccount: Account,
) => {
const {
unSignedContractCall,
contractInterface,
coinsMetaData: coinMeta,
} = await getContractCallPromises(
contractCallPayload,
stxAddress,
requestAccount.stxAddress,
selectedNetwork,
stxPublicKey,
requestAccount.stxPublicKey,
stacksTransaction?.auth,
);
setUnsignedTx(unSignedContractCall);
Expand All @@ -104,21 +114,61 @@ function TransactionRequest() {
}
};

const handleContractDeployRequest = async (contractDeployPayload: ContractDeployPayload) => {
const handleContractDeployRequest = async (
contractDeployPayload: ContractDeployPayload,
requestAccount: Account,
) => {
const response = await createDeployContractRequest(
contractDeployPayload,
selectedNetwork,
stxPublicKey,
requestAccount.stxPublicKey,
feeMultipliers!,
stxAddress,
requestAccount.stxAddress,
stacksTransaction?.auth,
);
setUnsignedTx(response.contractDeployTx);
setCodeBody(response.codeBody);
setContractName(response.contractName);
};

const switchAccountBasedOnRequest = () => {
const handleTxSigningRequest = async (requestAccount: Account) => {
if (payload.txType === 'contract_call') {
await handleContractCallRequest(payload, requestAccount);
} else if (payload.txType === 'smart_contract') {
await handleContractDeployRequest(payload, requestAccount);
} else {
navigate('/confirm-stx-tx', {
state: {
unsignedTx: payload.txHex,
sponsored: payload.sponsored,
isBrowserTx: true,
tabId,
requestToken,
},
});
}
};

const createRequestTx = async (account: Account) => {
try {
if (!payload.txHex) {
if (payload.txType === 'token_transfer') {
await handleTokenTransferRequest(payload, account);
} else if (payload.txType === 'contract_call') {
await handleContractCallRequest(payload, account);
} else if (payload.txType === 'smart_contract') {
await handleContractDeployRequest(payload, account);
}
} else {
await handleTxSigningRequest(account);
}
} catch (e: unknown) {
console.error(e); // eslint-disable-line
toast.error('Unexpected error creating transaction');
}
};

const handleRequest = async () => {
if (getNetworkType(payload.network) !== network.type) {
navigate('/tx-status', {
state: {
Expand All @@ -134,7 +184,8 @@ function TransactionRequest() {
if (payload.stxAddress !== selectedAccount?.stxAddress && !isHardwareAccount(selectedAccount)) {
const account = accountsList.find((acc) => acc.stxAddress === payload.stxAddress);
if (account) {
switchAccount(account);
await switchAccount(account);
await createRequestTx(account);
} else {
navigate('/tx-status', {
state: {
Expand All @@ -146,48 +197,14 @@ function TransactionRequest() {
},
});
}
}
setHasSwitchedAccount(true);
};

const handleTxSigningRequest = async () => {
if (payload.txType === 'contract_call') {
await handleContractCallRequest(payload);
} else if (payload.txType === 'smart_contract') {
await handleContractDeployRequest(payload);
} else {
navigate('/confirm-stx-tx', {
state: {
unsignedTx: payload.txHex,
sponsored: payload.sponsored,
isBrowserTx: true,
tabId,
requestToken,
},
});
}
};

const createRequestTx = async () => {
if (hasSwitchedAccount) {
if (!payload.txHex) {
if (payload.txType === 'token_transfer') {
await handleTokenTransferRequest(payload);
} else if (payload.txType === 'contract_call') {
await handleContractCallRequest(payload);
} else if (payload.txType === 'smart_contract') {
await handleContractDeployRequest(payload);
}
} else {
await handleTxSigningRequest();
}
} else if (selectedAccount) {
await createRequestTx(selectedAccount!);
}
};

useEffect(() => {
switchAccountBasedOnRequest();
createRequestTx();
}, [hasSwitchedAccount]);
handleRequest();
}, []);

return (
<>
Expand Down
31 changes: 31 additions & 0 deletions src/app/stores/wallet/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,37 @@ import {
WalletState,
} from './actions/types';

/*
* This store should ONLY be used for global app settings such as:
* - hasActivatedOrdinalsKey: undefined,
* - hasActivatedRareSatsKey: undefined,
* - hasActivatedRBFKey: true,
* - rareSatsNoticeDismissed: undefined,
* - showBtcReceiveAlert: true,
* - showOrdinalReceiveAlert: true,
* - showDataCollectionAlert: true,
* - btcApiUrl: '',
* - selectedAccount: null,
* - accountType: 'software',
* - accountName: undefined,
* - walletLockPeriod: WalletSessionPeriods.STANDARD,
* - isUnlocked: false,
* - fiatCurrency: 'USD',
*
* because we get many bugs around caching the wrong values when switching accounts,
* we prefer react-query cache (with the correct cache keys) for all
* account-specific values, and API fetch results such as:
* - btcFiatRate: '0',
* - stxBtcRate: '0',
* - stxBalance: '0',
* - stxAvailableBalance: '0',
* - stxLockedBalance: '0',
* - stxNonce: 0,
* - btcBalance: '0',
* - feeMultipliers: null,
*
* TODO refactor most of these values out of the store and use query cache instead
*/
const initialWalletState: WalletState = {
stxAddress: '',
btcAddress: '',
Expand Down

0 comments on commit 45d9f63

Please sign in to comment.