From 42c46d3b51f24c1ad5cccee0043ea31c2f4bbe5c Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 4 Dec 2024 10:54:15 +0100 Subject: [PATCH] fix(tooling-wallet): Fix staking with ledger (#4296) * feat(wallet): Fix speculos default port * feat(wallet): Update outdated comment * feat(wallet): Replace one useDryRunTransaction with new hook to estimate gas budget in StakeForm. Make useDryRunTransaction in StakeForm not trigger a ledger wallet interaction * feat(wallet): Revert speculos default port change --- apps/core/src/hooks/stake/index.ts | 1 + .../stake/useStakingGasBudgetEstimation.ts | 46 +++++++++++++++++++ apps/wallet/src/ui/app/WalletSigner.ts | 10 ++-- .../src/ui/app/staking/stake/StakeForm.tsx | 25 +++++----- 4 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 apps/core/src/hooks/stake/useStakingGasBudgetEstimation.ts diff --git a/apps/core/src/hooks/stake/index.ts b/apps/core/src/hooks/stake/index.ts index 2477e671351..2bfe4d462e8 100644 --- a/apps/core/src/hooks/stake/index.ts +++ b/apps/core/src/hooks/stake/index.ts @@ -6,3 +6,4 @@ export * from './useTotalDelegatedRewards'; export * from './useTotalDelegatedStake'; export * from './useValidatorInfo'; export * from './useStakeTxnInfo'; +export * from './useStakingGasBudgetEstimation'; diff --git a/apps/core/src/hooks/stake/useStakingGasBudgetEstimation.ts b/apps/core/src/hooks/stake/useStakingGasBudgetEstimation.ts new file mode 100644 index 00000000000..9c2cdf825e8 --- /dev/null +++ b/apps/core/src/hooks/stake/useStakingGasBudgetEstimation.ts @@ -0,0 +1,46 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useMemo } from 'react'; +import { useIotaClient } from '@iota/dapp-kit'; +import { useQuery } from '@tanstack/react-query'; +import { createStakeTransaction } from '../../utils'; + +interface UseStakingGasBudgetEstimationOptions { + senderAddress: string | null; + validatorAddress: string; + amount: bigint; +} + +export function useStakingGasBudgetEstimation({ + senderAddress, + validatorAddress, + amount, +}: UseStakingGasBudgetEstimationOptions) { + const client = useIotaClient(); + + const transaction = useMemo(() => { + return createStakeTransaction(amount, validatorAddress); + }, [amount, validatorAddress]); + + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: [ + 'staking-tx-gas-budget-estimate', + senderAddress, + validatorAddress, + transaction.getData(), + ], + queryFn: async () => { + if (!senderAddress || !transaction) { + return null; + } + + transaction.setSender(senderAddress); + + await transaction.build({ client }); + + return transaction.getData().gasData.budget; + }, + }); +} diff --git a/apps/wallet/src/ui/app/WalletSigner.ts b/apps/wallet/src/ui/app/WalletSigner.ts index 2be83cb253e..6ed5b0a9f71 100644 --- a/apps/wallet/src/ui/app/WalletSigner.ts +++ b/apps/wallet/src/ui/app/WalletSigner.ts @@ -52,7 +52,10 @@ export abstract class WalletSigner { if (isTransaction(transaction)) { // If the sender has not yet been set on the transaction, then set it. // NOTE: This allows for signing transactions with mismatched senders, which is important for sponsored transactions. - transaction.setSenderIfNotSet(await this.getAddress()); + if (!transaction.getData().sender) { + transaction.setSender(await this.getAddress()); + } + return await transaction.build({ client: this.client, }); @@ -84,13 +87,12 @@ export abstract class WalletSigner { transactionBlock: Uint8Array | Transaction; options?: IotaTransactionBlockResponseOptions; }): Promise { - const bytes = await this.prepareTransaction(input.transactionBlock); const signed = await this.signTransaction({ - transaction: bytes, + transaction: input.transactionBlock, }); return this.client.executeTransactionBlock({ - transactionBlock: bytes, + transactionBlock: signed.bytes, signature: signed.signature, options: input.options, }); diff --git a/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx b/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx index 19e6617977e..83a70ef19a8 100644 --- a/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx +++ b/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx @@ -9,6 +9,7 @@ import { parseAmount, useCoinMetadata, useFormatCoin, + useStakingGasBudgetEstimation, } from '@iota/core'; import { Field, type FieldProps, Form, useFormikContext } from 'formik'; import { memo, useEffect, useMemo } from 'react'; @@ -36,7 +37,12 @@ function StakeForm({ validatorAddress, coinBalance, coinType, epoch }: StakeFrom if (!values.amount || !decimals) return null; if (Number(values.amount) < 0) return null; const amountWithoutDecimals = parseAmount(values.amount, decimals); - return createStakeTransaction(amountWithoutDecimals, validatorAddress); + const transaction = createStakeTransaction(amountWithoutDecimals, validatorAddress); + if (activeAddress) { + transaction.setSender(activeAddress); + } + + return transaction; }, [values.amount, validatorAddress, decimals]); const { data: txDryRunResponse } = useTransactionDryRun( @@ -46,18 +52,15 @@ function StakeForm({ validatorAddress, coinBalance, coinType, epoch }: StakeFrom const gasSummary = txDryRunResponse ? getGasSummary(txDryRunResponse) : undefined; - const stakeAllTransaction = useMemo(() => { - return createStakeTransaction(coinBalance, validatorAddress); - }, [coinBalance, validatorAddress]); - - const { data: stakeAllTransactionDryRun } = useTransactionDryRun( - activeAddress ?? undefined, - stakeAllTransaction, - ); + const { data: stakeAllGasBudget } = useStakingGasBudgetEstimation({ + senderAddress: activeAddress, + amount: coinBalance, + validatorAddress, + }); - const gasBudget = BigInt(stakeAllTransactionDryRun?.input.gasData.budget ?? 0); + const gasBudget = BigInt(stakeAllGasBudget ?? 0); - // do not remove: gasBudget field is used in the validation schema apps/wallet/src/ui/app/staking/stake/utils/validation.ts + // do not remove: gasBudget field is used in the validation schema apps/core/src/utils/stake/createValidationSchema.ts useEffect(() => { setFieldValue('gasBudget', gasBudget); }, [gasBudget]);