diff --git a/development/webpackTools.js b/development/webpackTools.js index c801f47abc1..d0be1440160 100644 --- a/development/webpackTools.js +++ b/development/webpackTools.js @@ -126,16 +126,6 @@ function normalizeConfig({ platform === 'web' && !isDev ? new SubresourceIntegrityPlugin() : null, ].filter(Boolean); - // Compile entrypoints and dynamic imports only when they are in use. - if (isDev) { - config.experiments = config.experiments || {}; - config.experiments.lazyCompilation = { - imports: true, - entries: false, - test: /engine/, - }; - } - // add devServer proxy if (config.devServer) { config.devServer.proxy = { diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index 6e0cc721fa0..45b339682bd 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -197,6 +197,7 @@ export type ITransferInfo = { txInterval?: string; ignoreInscriptions?: boolean; useCustomAddressesBalance?: boolean; + opReturn?: string; }; export type IApproveInfo = { from: string; // token owner diff --git a/packages/engine/src/vaults/utils/btcForkChain/KeyringHardware.ts b/packages/engine/src/vaults/utils/btcForkChain/KeyringHardware.ts index cf76f6696e1..df22a53057f 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/KeyringHardware.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/KeyringHardware.ts @@ -1,3 +1,4 @@ +import { bytesToHex } from '@noble/hashes/utils'; import * as BitcoinJS from 'bitcoinjs-lib'; import type { SignedTx } from '@onekeyhq/engine/src/types/provider'; @@ -336,8 +337,15 @@ export class KeyringHardware extends KeyringHardwareBase { private buildHardwareOutput = async ( output: TxOutput, ): Promise => { - const { isCharge, bip44Path } = output.payload || {}; + const { isCharge, bip44Path, opReturn } = output.payload || {}; + if (opReturn && typeof opReturn === 'string' && opReturn.length > 0) { + return { + script_type: 'PAYTOOPRETURN', + amount: '0', + op_return_data: bytesToHex(Buffer.from(opReturn)), + }; + } if (isCharge && bip44Path) { const { getHDPath, getOutputScriptType } = await CoreSDKLoader(); const addressN = getHDPath(bip44Path); diff --git a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts index 7aed78ee59c..f61bf9c1b73 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts @@ -869,7 +869,25 @@ export default class VaultBtcFork extends VaultBase { txid: txId, value: value.toString(), })), - outputs: outputs.map(({ value, address }) => { + outputs: outputs.map(({ value, address, script }) => { + const valueText = value?.toString(); + + // OP_RETURN output + if ( + valueText && + new BigNumber(valueText).eq(0) && + !address && + script === transferInfo.opReturn + ) { + return { + address: '', + value: valueText, + payload: { + opReturn: transferInfo.opReturn, + }, + }; + } + // If there is no address, it should be set to the change address. const addressOrChangeAddress = address || dbAccount.address; if (!addressOrChangeAddress) { @@ -877,7 +895,6 @@ export default class VaultBtcFork extends VaultBase { 'buildEncodedTxFromBatchTransfer ERROR: Invalid change address', ); } - const valueText = value?.toString(); if (!valueText || new BigNumber(valueText).lte(0)) { throw new Error( 'buildEncodedTxFromBatchTransfer ERROR: Invalid value', @@ -1934,12 +1951,25 @@ export default class VaultBtcFork extends VaultBase { value, }, ]; + + if ( + transferInfo.opReturn && + typeof transferInfo.opReturn === 'string' && + transferInfo.opReturn.length + ) { + outputsForCoinSelect.push({ + address: '', + value: 0, + script: transferInfo.opReturn, + }); + } } const algorithm: ICoinSelectAlgorithm | undefined = !isBatchTransfer ? transferInfos[0].coinSelectAlgorithm : undefined; - if (!isBatchTransfer && outputsForCoinSelect.length > 1) { + // transfer output + maybe opReturn output + if (!isBatchTransfer && outputsForCoinSelect.length > 2) { throw new Error('single transfer should only have one output'); } const { inputs, outputs, fee } = isNftTransfer diff --git a/packages/engine/src/vaults/utils/btcForkChain/provider/provider.ts b/packages/engine/src/vaults/utils/btcForkChain/provider/provider.ts index 52c56b47203..190ae6872ca 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/provider/provider.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/provider/provider.ts @@ -608,11 +608,7 @@ class Provider { unsignedTx: IUnsignedTxPro, signers: { [p: string]: Signer }, ) { - const { - inputs, - outputs, - payload: { opReturn }, - } = unsignedTx; + const { inputs, outputs } = unsignedTx; const [inputAddressesEncodings, nonWitnessPrevTxs] = await this.collectInfoForSoftwareSign(unsignedTx); @@ -688,22 +684,27 @@ class Provider { } outputs.forEach((output) => { - psbt.addOutput({ - address: output.address, - value: output.value.integerValue().toNumber(), - }); + const { payload } = output; + if ( + payload?.opReturn && + typeof payload?.opReturn === 'string' && + payload?.opReturn.length > 0 + ) { + const embed = BitcoinJS.payments.embed({ + data: [loadOPReturn(payload?.opReturn)], + }); + psbt.addOutput({ + script: checkIsDefined(embed.output), + value: 0, + }); + } else { + psbt.addOutput({ + address: output.address, + value: output.value.integerValue().toNumber(), + }); + } }); - if (typeof opReturn === 'string') { - const embed = BitcoinJS.payments.embed({ - data: [loadOPReturn(opReturn)], - }); - psbt.addOutput({ - script: checkIsDefined(embed.output), - value: 0, - }); - } - return psbt; } diff --git a/packages/engine/src/vaults/utils/btcForkChain/provider/vsize.ts b/packages/engine/src/vaults/utils/btcForkChain/provider/vsize.ts index e03e4e7c474..9fa58c465a8 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/provider/vsize.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/provider/vsize.ts @@ -25,6 +25,9 @@ const loadOPReturn = ( opReturn: string, opReturnSizeLimit: number = TX_OP_RETURN_SIZE_LIMIT, ) => { + if (opReturn.length > opReturnSizeLimit) { + throw new Error('OP_RETURN data is too large.'); + } const buffer = Buffer.from(opReturn); return buffer.slice(0, opReturnSizeLimit); }; diff --git a/packages/engine/src/vaults/utils/btcForkChain/types.ts b/packages/engine/src/vaults/utils/btcForkChain/types.ts index 38e342513d5..e9359b9e0e1 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/types.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/types.ts @@ -108,7 +108,7 @@ export type IEncodedTxBtc = { outputs: { address: string; value: string; - payload?: { isCharge?: boolean; bip44Path?: string }; + payload?: { isCharge?: boolean; bip44Path?: string; opReturn?: string }; inscriptions?: NFTBTCAssetModel[]; }[]; feeRate: string; @@ -119,6 +119,7 @@ export type IEncodedTxBtc = { address: string; value?: number; isMax?: boolean; + script?: string; }[]; transferInfo: ITransferInfo; transferInfos?: ITransferInfo[]; diff --git a/packages/engine/src/vaults/utils/btcForkChain/utils/coinSelectUtils.ts b/packages/engine/src/vaults/utils/btcForkChain/utils/coinSelectUtils.ts index 26a97a42595..6ab92028684 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/utils/coinSelectUtils.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/utils/coinSelectUtils.ts @@ -210,6 +210,7 @@ export const coinSelect = ({ const finalOutputs = outputsForCoinSelect.map((o) => ({ address: o.address, value: o.isMax ? undefined : o.value, + script: o.script, })); let unspentSelectFn = max ? coinSelectSplit : coinSelectAuto; diff --git a/packages/kit/src/views/Wallet/AccountInfo/index.tsx b/packages/kit/src/views/Wallet/AccountInfo/index.tsx index cc1b35e3318..68bb3303943 100644 --- a/packages/kit/src/views/Wallet/AccountInfo/index.tsx +++ b/packages/kit/src/views/Wallet/AccountInfo/index.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import BigNumber from 'bignumber.js'; import stringify from 'fast-json-stable-stringify'; import { useIntl } from 'react-intl'; @@ -350,8 +351,24 @@ const SummedValueComp = memo( s.overview.overviewStats?.[networkId]?.[accountId]?.summary?.totalValue, ); + const nftTotalValue = useAppSelector( + (s) => + s.overview.overviewStats?.[networkId]?.[accountId]?.nfts?.totalValue, + ); + + const includeNFTsInTotal = useAppSelector( + (s) => s.settings.includeNFTsInTotal, + ); + const isWatching = isWatchingAccount({ accountId }); + const summaryTotalValue = useMemo(() => { + if (!totalValue || includeNFTsInTotal) { + return totalValue; + } + return new BigNumber(totalValue).minus(nftTotalValue ?? 0).toFixed(); + }, [includeNFTsInTotal, nftTotalValue, totalValue]); + return ( {typeof totalValue === 'undefined' ? ( @@ -362,7 +379,7 @@ const SummedValueComp = memo( {isWatching && isBTCNetwork(networkId) ? null : (