From c486744aa9160fa39d1322d7027a925f16a3261d Mon Sep 17 00:00:00 2001 From: weatherstar Date: Thu, 14 Mar 2024 19:08:02 +0800 Subject: [PATCH] v4 fix: several fee issues OK-26141 OK-25859 OK-26144 (#4243) * fix: use fee in dapp tx * feat: add priority fee to sol dapp tx by default * chore: update comments * chore: update comments --- packages/engine/src/vaults/impl/evm/Vault.ts | 33 -------- packages/engine/src/vaults/impl/sol/Vault.ts | 78 +++++++++++++++++-- packages/engine/src/vaults/types.ts | 1 + .../views/Send/modals/SendConfirmFromDapp.tsx | 72 ++++++++++++----- .../Send/utils/prepareSendConfirmEncodedTx.ts | 9 ++- .../views/Send/utils/useFeeInfoPayload.tsx | 24 +++++- 6 files changed, 149 insertions(+), 68 deletions(-) diff --git a/packages/engine/src/vaults/impl/evm/Vault.ts b/packages/engine/src/vaults/impl/evm/Vault.ts index df9a59f193a..0f641a33f17 100644 --- a/packages/engine/src/vaults/impl/evm/Vault.ts +++ b/packages/engine/src/vaults/impl/evm/Vault.ts @@ -1294,31 +1294,6 @@ export default class Vault extends VaultBase { prices?.length && prices?.every((price) => typeof price === 'object'), ); - const gasLimitInTx = new BigNumber( - encodedTx.gas ?? encodedTx.gasLimit ?? 0, - ).toFixed(); - // NOTE: gasPrice deleted in removeFeeInfoInTx() if encodedTx build by DAPP - let gasPriceInTx: string | EIP1559Fee | undefined = encodedTx.gasPrice - ? this._toNormalAmount(encodedTx.gasPrice, network.feeDecimals) - : undefined; - if (eip1559) { - gasPriceInTx = merge( - { - ...(prices[0] as EIP1559Fee), - }, - { - maxPriorityFeePerGas: encodedTx.maxPriorityFeePerGas - ? this._toNormalAmount( - encodedTx.maxPriorityFeePerGas, - network.feeDecimals, - ) - : undefined, - maxFeePerGas: encodedTx.maxFeePerGas - ? this._toNormalAmount(encodedTx.maxFeePerGas, network.feeDecimals) - : undefined, - }, - ) as EIP1559Fee; - } // [{baseFee: '928.361757873', maxPriorityFeePerGas: '11.36366', maxFeePerGas: '939.725417873'}] // [10] const limit = BigNumber.max( @@ -1343,14 +1318,6 @@ export default class Vault extends VaultBase { prices, defaultPresetIndex: '1', - // feeInfo in original tx - tx: { - eip1559, - limit: gasLimitInTx, - ...(eip1559 - ? { price1559: gasPriceInTx as EIP1559Fee } - : { price: gasPriceInTx as string }), - }, baseFeeValue, extraInfo: { networkCongestion, diff --git a/packages/engine/src/vaults/impl/sol/Vault.ts b/packages/engine/src/vaults/impl/sol/Vault.ts index fdd8a104140..0b32a424c32 100644 --- a/packages/engine/src/vaults/impl/sol/Vault.ts +++ b/packages/engine/src/vaults/impl/sol/Vault.ts @@ -27,12 +27,15 @@ import { getAssociatedTokenAddressSync, } from '@solana/spl-token'; import { + ComputeBudgetInstruction, ComputeBudgetProgram, + MessageAccountKeys, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SystemInstruction, SystemProgram, Transaction, + TransactionMessage, VersionedTransaction, } from '@solana/web3.js'; import axios from 'axios'; @@ -1037,14 +1040,73 @@ export default class Vault extends VaultBase { return Promise.resolve(encodedTx); } - const nativeTx = (await this.helper.parseToNativeTx( - encodedTx, - )) as Transaction; - const [instruction] = nativeTx.instructions; + const nativeTx = await this.helper.parseToNativeTx(encodedTx); + + // add priority fees to DApp tx by default + try { + if (options.type === IEncodedTxUpdateType.priorityFees) { + const isVersionedTransaction = nativeTx instanceof VersionedTransaction; + let instructions: TransactionInstruction[] = []; + let unitPrice; + let transactionMessage; + if (isVersionedTransaction) { + transactionMessage = TransactionMessage.decompile(nativeTx.message); + instructions = transactionMessage.instructions; + } else { + instructions = nativeTx.instructions; + } + + // try to find if the transaction has already set the compute unit price(priority fee) + try { + for (const instruction of instructions) { + unitPrice = + ComputeBudgetInstruction.decodeSetComputeUnitPrice(instruction); + } + } catch { + // pass + } + + // if not set, add the compute unit price(priority fee) to the transaction + if (isNil(unitPrice)) { + const client = await this.getClient(); + const accountAddress = await this.getAccountAddress(); + const prioritizationFee = await client.getRecentMaxPrioritizationFees( + [accountAddress], + ); + + const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: Math.max(MIN_PRIORITY_FEE, prioritizationFee), + }); + + if (isVersionedTransaction) { + transactionMessage?.instructions.push(addPriorityFee); + } else { + (nativeTx as Transaction).add(addPriorityFee); + } + } + + if (isVersionedTransaction) { + return bs58.encode( + new VersionedTransaction( + (transactionMessage as TransactionMessage).compileToV0Message(), + ).serialize(), + ); + } + + return bs58.encode( + (nativeTx as Transaction).serialize({ requireAllSignatures: false }), + ); + } + } catch (e) { + return encodedTx; + } + + const nativeTxForUpdateTransfer = nativeTx as Transaction; + const [instruction] = nativeTxForUpdateTransfer.instructions; // max native token transfer update if ( options.type === 'transfer' && - nativeTx.instructions.length === 1 && + nativeTxForUpdateTransfer.instructions.length === 1 && instruction.programId.toString() === SystemProgram.programId.toString() ) { const instructionType = @@ -1056,7 +1118,7 @@ export default class Vault extends VaultBase { this.networkId, ); const { amount } = payload as IEncodedTxUpdatePayloadTransfer; - nativeTx.instructions = [ + nativeTxForUpdateTransfer.instructions = [ SystemProgram.transfer({ fromPubkey, toPubkey, @@ -1065,7 +1127,9 @@ export default class Vault extends VaultBase { ), }), ]; - return bs58.encode(nativeTx.serialize({ requireAllSignatures: false })); + return bs58.encode( + nativeTxForUpdateTransfer.serialize({ requireAllSignatures: false }), + ); } } return Promise.resolve(encodedTx); diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index 42c36fe9e54..1d70947fe93 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -316,6 +316,7 @@ export enum IEncodedTxUpdateType { cancel = 'cancel', advancedSettings = 'advancedSettings', customData = 'customData', + priorityFees = 'priorityFees', } export type IEncodedTxUpdateOptions = { diff --git a/packages/kit/src/views/Send/modals/SendConfirmFromDapp.tsx b/packages/kit/src/views/Send/modals/SendConfirmFromDapp.tsx index 8df68505e69..6d727ca3728 100644 --- a/packages/kit/src/views/Send/modals/SendConfirmFromDapp.tsx +++ b/packages/kit/src/views/Send/modals/SendConfirmFromDapp.tsx @@ -1,10 +1,13 @@ -import { useEffect, useRef } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { StackActions, useNavigation } from '@react-navigation/native'; import { AppState } from 'react-native'; import type { IEncodedTxBtc } from '@onekeyhq/engine/src/vaults/impl/btc/types'; +import { IEncodedTxUpdateType } from '@onekeyhq/engine/src/vaults/types'; +import { IMPL_SOL } from '@onekeyhq/shared/src/engine/engineConsts'; +import backgroundApiProxy from '../../../background/instance/backgroundApiProxy'; import { getActiveWalletAccount } from '../../../hooks'; import useDappParams from '../../../hooks/useDappParams'; import { useReduxReady } from '../../../hooks/useReduxReady'; @@ -35,37 +38,37 @@ export function SendConfirmFromDapp() { _$t = undefined, networkId: dappNetworkId, } = useDappParams(); - useEffect(() => { - if (!isReady) { - return; - } - // OK-16560: navigate when app in background would cause modal render in wrong size - const appStateListener = AppState.addEventListener('change', (state) => { - if (state === 'active') { - setTimeout(() => { - if (pendingAction.current) { - navigation.dispatch(pendingAction.current); - pendingAction.current = undefined; - } - }); - } - }); + + const navigateToSendConfirm = useCallback(async () => { let action: any; // TODO get network and account from dapp connections - const { networkId, accountId } = getActiveWalletAccount(); + const { networkId, accountId, networkImpl } = getActiveWalletAccount(); // alert(JSON.stringify({ networkId, accountId, isReady })); // TODO providerName if (encodedTx) { - const isPsbt = (encodedTx as IEncodedTxBtc).psbtHex; + let newEncodedTx = encodedTx; + const isPsbt = (newEncodedTx as IEncodedTxBtc).psbtHex; + + if (networkImpl === IMPL_SOL) { + newEncodedTx = await backgroundApiProxy.engine.updateEncodedTx({ + accountId, + networkId, + encodedTx, + payload: {}, + options: { + type: IEncodedTxUpdateType.priorityFees, + }, + }); + } const params: SendConfirmParams = { networkId: dappNetworkId ?? networkId, accountId, sourceInfo, - encodedTx, + encodedTx: newEncodedTx, feeInfoEditable: !isPsbt, - feeInfoUseFeeInTx: !!isPsbt, + feeInfoUseFeeInTx: true, ignoreFetchFeeCalling: !!isPsbt, signOnly, // @ts-ignore @@ -95,6 +98,34 @@ export function SendConfirmFromDapp() { pendingAction.current = action; } } + }, [ + _$t, + dappNetworkId, + encodedTx, + navigation, + signOnly, + sourceInfo, + unsignedMessage, + ]); + + useEffect(() => { + if (!isReady) { + return; + } + // OK-16560: navigate when app in background would cause modal render in wrong size + const appStateListener = AppState.addEventListener('change', (state) => { + if (state === 'active') { + setTimeout(() => { + if (pendingAction.current) { + navigation.dispatch(pendingAction.current); + pendingAction.current = undefined; + } + }); + } + }); + + navigateToSendConfirm(); + return () => { appStateListener.remove(); }; @@ -107,6 +138,7 @@ export function SendConfirmFromDapp() { unsignedMessage, signOnly, dappNetworkId, + navigateToSendConfirm, ]); return null; diff --git a/packages/kit/src/views/Send/utils/prepareSendConfirmEncodedTx.ts b/packages/kit/src/views/Send/utils/prepareSendConfirmEncodedTx.ts index 572d9074faf..3b701868047 100644 --- a/packages/kit/src/views/Send/utils/prepareSendConfirmEncodedTx.ts +++ b/packages/kit/src/views/Send/utils/prepareSendConfirmEncodedTx.ts @@ -77,10 +77,11 @@ export async function prepareSendConfirmEncodedTx({ // routeParams is not editable, so should create new one let tx = { ...encodedTxEvm }; tx.from = tx.from || address; - // remove gas price if encodedTx build by DAPP - if (sendConfirmParams.sourceInfo) { - tx = removeFeeInfoInTx(tx); - } + // keep gas price if encodedTx build by DAPP + + // if (sendConfirmParams.sourceInfo) { + // tx = removeFeeInfoInTx(tx); + // } // Ensure IEncodedTxEvm's value is hex string. if (tx.value && tx.value.startsWith && !tx.value.startsWith('0x')) { diff --git a/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx b/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx index c0507ad8630..87264449f93 100644 --- a/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx +++ b/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx @@ -7,6 +7,7 @@ import BigNumber from 'bignumber.js'; import { ToastManager } from '@onekeyhq/components'; import { FailedToEstimatedGasError } from '@onekeyhq/engine/src/errors'; +import type { EIP1559Fee } from '@onekeyhq/engine/src/types/network'; import type { IEncodedTxEvm } from '@onekeyhq/engine/src/vaults/impl/evm/Vault'; import type { IEncodedTx, @@ -163,7 +164,6 @@ export function useFeeInfoPayload({ gasPrice, } = encodedTx as IEncodedTxEvm; const limit = gasLimit || gas; - if (maxFeePerGas && maxPriorityFeePerGas) { const price1559 = { baseFee: new BigNumber(gasPrice ?? 0) @@ -177,23 +177,39 @@ export function useFeeInfoPayload({ .toFixed(), }; info.eip1559 = true; - info.limit = limit; + info.limit = limit || info.limit; info.prices = [price1559]; info.tx = { eip1559: true, limit, price1559, }; - } else { + } else if (gasPrice) { const price = new BigNumber(gasPrice ?? 0) .shiftedBy(-(feeDecimals ?? 0)) .toFixed(); - info.limit = limit; + info.limit = limit || info.limit; info.prices = [price]; info.tx = { limit, price, }; + } else if (limit) { + info.tx = { + limit, + eip1559: info.eip1559, + ...(info.eip1559 + ? { + price1559: info.prices[ + Number(info.defaultPresetIndex) + ] as EIP1559Fee, + } + : { + price: info.prices[ + Number(info.defaultPresetIndex) + ] as string, + }), + }; } } }