diff --git a/package-lock.json b/package-lock.json index 70f12849a..ab8679dc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,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", @@ -1732,9 +1732,9 @@ } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "6.0.1-3d747a3", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/6.0.1-3d747a3/219b63e085fd01f85abaa42902b9759e050e0236", - "integrity": "sha512-oRwgA69GYsmknpSZ6evDtbAR3wOdxcMKPbi+sea8/8KHXLTbA6m1qhmBKypGsIALSS38dFQ9JZuEKfVpwQ6iig==", + "version": "7.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/7.0.0/a9d4470c0bee31b2b751b0f2a3e064d51453ddd4", + "integrity": "sha512-8z5g5dHFin0d9695EwI0t6a/Ji7vzUenCq1AHpTtsj5Z1/SlH6Oa7p95fyJ1usBH99M5NQAYFMBGkjOcNE+jQw==", "license": "ISC", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", @@ -15372,9 +15372,9 @@ } }, "@secretkeylabs/xverse-core": { - "version": "6.0.1-3d747a3", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/6.0.1-3d747a3/219b63e085fd01f85abaa42902b9759e050e0236", - "integrity": "sha512-oRwgA69GYsmknpSZ6evDtbAR3wOdxcMKPbi+sea8/8KHXLTbA6m1qhmBKypGsIALSS38dFQ9JZuEKfVpwQ6iig==", + "version": "7.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/7.0.0/a9d4470c0bee31b2b751b0f2a3e064d51453ddd4", + "integrity": "sha512-8z5g5dHFin0d9695EwI0t6a/Ji7vzUenCq1AHpTtsj5Z1/SlH6Oa7p95fyJ1usBH99M5NQAYFMBGkjOcNE+jQw==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", diff --git a/package.json b/package.json index e59854e31..d4d5692f3 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/components/transactionSetting/editNonce.tsx b/src/app/components/transactionSetting/editNonce.tsx index 54d30d1c9..5f28bf038 100644 --- a/src/app/components/transactionSetting/editNonce.tsx +++ b/src/app/components/transactionSetting/editNonce.tsx @@ -58,7 +58,7 @@ function EditNonce({ nonce, setNonce }: Props) { useEffect(() => { setNonce(nonceInput); - }, [nonceInput]); + }, [nonceInput, setNonce]); return ( diff --git a/src/app/hooks/queries/useStxWalletData.ts b/src/app/hooks/queries/useStxWalletData.ts index 0b33b3580..bd4d17a5e 100644 --- a/src/app/hooks/queries/useStxWalletData.ts +++ b/src/app/hooks/queries/useStxWalletData.ts @@ -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(); diff --git a/src/app/screens/transactionRequest/index.tsx b/src/app/screens/transactionRequest/index.tsx index 1b8260262..504c99eee 100644 --- a/src/app/screens/transactionRequest/index.tsx +++ b/src/app/screens/transactionRequest/index.tsx @@ -5,6 +5,7 @@ import useStxTransactionRequest from '@hooks/useStxTransactionRequest'; import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; import { + Account, buf2hex, Coin, ContractFunction, @@ -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'; @@ -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(); @@ -42,17 +43,23 @@ function TransactionRequest() { const [coinsMetaData, setCoinsMetaData] = useState(null); const [codeBody, setCodeBody] = useState(undefined); const [contractName, setContractName] = useState(undefined); - const [hasSwitchedAccount, setHasSwitchedAccount] = useState(false); const [attachment, setAttachment] = useState(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, @@ -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); @@ -104,13 +114,16 @@ 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); @@ -118,7 +131,44 @@ function TransactionRequest() { 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: { @@ -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: { @@ -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 ( <> diff --git a/src/app/stores/wallet/reducer.ts b/src/app/stores/wallet/reducer.ts index 28000ab51..6e626e48c 100644 --- a/src/app/stores/wallet/reducer.ts +++ b/src/app/stores/wallet/reducer.ts @@ -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: '',