From 0dacac604f5d11a616e9decfa1bb06c7c87862b2 Mon Sep 17 00:00:00 2001 From: alter-eggo Date: Mon, 22 Jan 2024 16:19:45 +0400 Subject: [PATCH 1/2] refactor: rename taproot utxo --- ...use-generate-retrieve-taproot-funds-tx.tsx | 9 ++++++-- .../coinselect/select-inscription-coins.ts | 4 ++-- .../create-utxo-from-inscription.ts | 18 ++++++++++++--- .../components/send-inscription-container.tsx | 18 +++++++++++---- .../hooks/use-generate-ordinal-tx.ts | 20 ++++++++-------- .../hooks/use-send-inscription-fees-list.ts | 4 ++-- .../bitcoin/address/utxos-by-address.hooks.ts | 4 ++-- .../bitcoin/address/utxos-by-address.query.ts | 23 +++++++++++++------ .../balance/btc-taproot-balance.hooks.ts | 4 ++-- src/app/query/bitcoin/bitcoin-client.ts | 4 ++-- 10 files changed, 72 insertions(+), 36 deletions(-) diff --git a/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx b/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx index 4cdf04826f3..e2400f60a73 100644 --- a/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx +++ b/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx @@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react'; import * as btc from '@scure/btc-signer'; +import { extractAddressIndexFromPath } from '@shared/crypto/bitcoin/bitcoin.utils'; import { Money, createMoney } from '@shared/models/money.model'; import { sumNumbers } from '@app/common/math/helpers'; @@ -35,7 +36,8 @@ export function useGenerateRetrieveTaprootFundsTx() { const totalAmount = sumNumbers(uninscribedUtxos.map(utxo => utxo.value)); uninscribedUtxos.forEach(utxo => { - const signer = createSigner?.(utxo.addressIndex); + const addressIndex = extractAddressIndexFromPath(utxo.derivationPath); + const signer = createSigner?.(addressIndex); if (!signer) return; tx.addInput({ @@ -61,7 +63,10 @@ export function useGenerateRetrieveTaprootFundsTx() { tx.addOutputAddress(recipient, paymentAmount, networkMode); - uninscribedUtxos.forEach(utxo => createSigner?.(utxo.addressIndex).sign(tx)); + uninscribedUtxos.forEach(utxo => { + const addressIndex = extractAddressIndexFromPath(utxo.derivationPath); + return createSigner?.(addressIndex).sign(tx); + }); tx.finalize(); return tx.hex; diff --git a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts index 0c06401b2cc..ac1e6185b4a 100644 --- a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts +++ b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts @@ -4,7 +4,7 @@ import { isDefined } from '@shared/utils'; import { sumNumbers } from '@app/common/math/helpers'; import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-size-fee-estimator'; import { createCounter } from '@app/common/utils/counter'; -import { TaprootUtxo, UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; +import { UtxoResponseItem, UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; const idealInscriptionValue = 10_000; @@ -22,7 +22,7 @@ interface SelectInscriptionCoinFailure { type SelectInscriptionCoinResult = SelectInscriptionCoinSuccess | SelectInscriptionCoinFailure; interface SelectInscriptionTransferCoinsArgs { - inscriptionInput: TaprootUtxo; + inscriptionInput: UtxoWithDerivationPath; nativeSegwitUtxos: UtxoResponseItem[]; feeRate: number; recipient: string; diff --git a/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts b/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts index 8d15df29f91..11a76ea009a 100644 --- a/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts +++ b/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts @@ -1,8 +1,20 @@ +import { BitcoinNetworkModes } from '@shared/constants'; +import { getNativeSegwitAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2wpkh-address-gen'; import { Inscription } from '@shared/models/inscription.model'; -import { TaprootUtxo } from '@app/query/bitcoin/bitcoin-client'; +import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; -export function createUtxoFromInscription(inscription: Inscription): TaprootUtxo { +interface CreateUtxoFromInscriptionArgs { + inscription: Inscription; + network: BitcoinNetworkModes; + accountIndex: number; +} + +export function createUtxoFromInscription({ + inscription, + network, + accountIndex, +}: CreateUtxoFromInscriptionArgs): UtxoWithDerivationPath { const { genesis_block_hash, genesis_timestamp, genesis_block_height, value, addressIndex } = inscription; @@ -16,6 +28,6 @@ export function createUtxoFromInscription(inscription: Inscription): TaprootUtxo block_time: genesis_timestamp, }, value: Number(value), - addressIndex, + derivationPath: getNativeSegwitAddressIndexDerivationPath(network, accountIndex, addressIndex), }; } diff --git a/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx b/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx index ddff09d76fa..98983fed764 100644 --- a/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx +++ b/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx @@ -7,7 +7,9 @@ import { AverageBitcoinFeeRates, BtcFeeType } from '@shared/models/fees/bitcoin- import { SupportedInscription } from '@shared/models/inscription.model'; import { useOnMount } from '@app/common/hooks/use-on-mount'; -import { TaprootUtxo } from '@app/query/bitcoin/bitcoin-client'; +import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; +import { useCurrentAccountIndex } from '@app/store/accounts/account'; +import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { useSendInscriptionRouteState } from '../hooks/use-send-inscription-route-state'; import { createUtxoFromInscription } from './create-utxo-from-inscription'; @@ -18,7 +20,7 @@ interface SendInscriptionContextState { inscription: SupportedInscription; selectedFeeType: BtcFeeType; setSelectedFeeType(value: BtcFeeType | null): void; - utxo: TaprootUtxo; + utxo: UtxoWithDerivationPath; } export function useSendInscriptionState() { const location = useLocation(); @@ -29,14 +31,22 @@ export function useSendInscriptionState() { export function SendInscriptionContainer() { const [selectedFeeType, setSelectedFeeType] = useState(null); const [inscription, setInscription] = useState(null); - const [utxo, setUtxo] = useState(null); + const [utxo, setUtxo] = useState(null); const routeState = useSendInscriptionRouteState(); + const network = useCurrentNetwork(); + const currentAccountIndex = useCurrentAccountIndex(); useOnMount(() => { if (!routeState.inscription) return; setInscription(routeState.inscription); - setUtxo(createUtxoFromInscription(routeState.inscription)); + setUtxo( + createUtxoFromInscription({ + inscription: routeState.inscription, + network: network.chain.bitcoin.bitcoinNetwork, + accountIndex: currentAccountIndex, + }) + ); }); if (!inscription || !utxo) return null; diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts index 86561313f14..dbfec69afde 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts @@ -1,6 +1,7 @@ import * as btc from '@scure/btc-signer'; import { AddressType, getAddressInfo } from 'bitcoin-address-validation'; +import { extractAddressIndexFromPath } from '@shared/crypto/bitcoin/bitcoin.utils'; import { BitcoinInputSigningConfig } from '@shared/crypto/bitcoin/signer-config'; import { logger } from '@shared/logger'; import { OrdinalSendFormValues } from '@shared/models/form.model'; @@ -8,14 +9,14 @@ import { OrdinalSendFormValues } from '@shared/models/form.model'; import { determineUtxosForSpend } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { createCounter } from '@app/common/utils/counter'; import { useCurrentNativeSegwitAccountSpendableUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { TaprootUtxo } from '@app/query/bitcoin/bitcoin-client'; +import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { selectInscriptionTransferCoins } from '../coinselect/select-inscription-coins'; -export function useGenerateUnsignedOrdinalTx(taprootInput: TaprootUtxo) { +export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivationPath) { const createTaprootSigner = useCurrentAccountTaprootSigner(); const createNativeSegwitSigner = useCurrentAccountNativeSegwitSigner(); const networkMode = useBitcoinScureLibNetworkConfig(); @@ -30,9 +31,8 @@ export function useGenerateUnsignedOrdinalTx(taprootInput: TaprootUtxo) { } function formTaprootOrdinalTx(values: OrdinalSendFormValues) { - const inscriptionInput = taprootInput; - - const taprootSigner = createTaprootSigner?.(inscriptionInput.addressIndex); + const addressIndex = extractAddressIndexFromPath(inscriptionInput.derivationPath); + const taprootSigner = createTaprootSigner?.(addressIndex); const nativeSegwitSigner = createNativeSegwitSigner?.(0); if (!taprootSigner || !nativeSegwitSigner || !nativeSegwitUtxos || !values.feeRate) return; @@ -58,13 +58,13 @@ export function useGenerateUnsignedOrdinalTx(taprootInput: TaprootUtxo) { // Inscription input tx.addInput({ - txid: taprootInput.txid, - index: taprootInput.vout, + txid: inscriptionInput.txid, + index: inscriptionInput.vout, tapInternalKey: taprootSigner.payment.tapInternalKey, sequence: 0, witnessUtxo: { script: taprootSigner.payment.script, - amount: BigInt(taprootInput.value), + amount: BigInt(inscriptionInput.value), }, }); signingConfig.push({ @@ -122,7 +122,7 @@ export function useGenerateUnsignedOrdinalTx(taprootInput: TaprootUtxo) { const tx = new btc.Transaction(); // Fee-covering Native Segwit inputs - [taprootInput, ...inputs].forEach(input => + [inscriptionInput, ...inputs].forEach(input => tx.addInput({ txid: input.txid, index: input.vout, @@ -135,7 +135,7 @@ export function useGenerateUnsignedOrdinalTx(taprootInput: TaprootUtxo) { ); // Inscription output - tx.addOutputAddress(values.recipient, BigInt(taprootInput.value), networkMode); + tx.addOutputAddress(values.recipient, BigInt(inscriptionInput.value), networkMode); // Recipient and change outputs outputs.forEach(output => { diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts index c7d60fd12d8..3003240cb52 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts @@ -7,7 +7,7 @@ import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { formatMoneyPadded, i18nFormatCurrency } from '@app/common/money/format-money'; import { FeesListItem } from '@app/components/bitcoin-fees-list/bitcoin-fees-list'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { TaprootUtxo } from '@app/query/bitcoin/bitcoin-client'; +import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; @@ -16,7 +16,7 @@ import { selectInscriptionTransferCoins } from '../coinselect/select-inscription interface UseSendInscriptionFeesListArgs { recipient: string; - utxo: TaprootUtxo; + utxo: UtxoWithDerivationPath; } export function useSendInscriptionFeesList({ recipient, utxo }: UseSendInscriptionFeesListArgs) { const createNativeSegwitSigner = useCurrentAccountNativeSegwitSigner(); diff --git a/src/app/query/bitcoin/address/utxos-by-address.hooks.ts b/src/app/query/bitcoin/address/utxos-by-address.hooks.ts index b824117ee05..46534ba95a4 100644 --- a/src/app/query/bitcoin/address/utxos-by-address.hooks.ts +++ b/src/app/query/bitcoin/address/utxos-by-address.hooks.ts @@ -4,14 +4,14 @@ import { InscriptionResponseItem } from '@shared/models/inscription.model'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { TaprootUtxo, UtxoResponseItem } from '../bitcoin-client'; +import { UtxoResponseItem, UtxoWithDerivationPath } from '../bitcoin-client'; import { useInscriptionsByAddressQuery } from '../ordinals/inscriptions.query'; import { useBitcoinPendingTransactionsInputs } from './transactions-by-address.hooks'; import { useGetUtxosByAddressQuery } from './utxos-by-address.query'; export function filterUtxosWithInscriptions( inscriptions: InscriptionResponseItem[], - utxos: TaprootUtxo[] | UtxoResponseItem[] + utxos: UtxoWithDerivationPath[] | UtxoResponseItem[] ) { return utxos.filter( utxo => diff --git a/src/app/query/bitcoin/address/utxos-by-address.query.ts b/src/app/query/bitcoin/address/utxos-by-address.query.ts index d20f2d6577d..efdda83d2ed 100644 --- a/src/app/query/bitcoin/address/utxos-by-address.query.ts +++ b/src/app/query/bitcoin/address/utxos-by-address.query.ts @@ -1,6 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import { getTaprootAddress } from '@shared/crypto/bitcoin/bitcoin.utils'; +import { getNativeSegwitAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2wpkh-address-gen'; import { createCounter } from '@app/common/utils/counter'; import { AppUseQueryConfig } from '@app/query/query-config'; @@ -10,7 +11,7 @@ import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; -import { TaprootUtxo, UtxoResponseItem } from '../bitcoin-client'; +import { UtxoResponseItem, UtxoWithDerivationPath } from '../bitcoin-client'; import { hasInscriptions } from './address.utils'; const staleTime = 3 * 60 * 1000; @@ -50,7 +51,7 @@ export function useTaprootAccountUtxosQuery() { async () => { let currentNumberOfAddressesWithoutUtxos = 0; const addressIndexCounter = createCounter(0); - let foundUnspentTransactions: TaprootUtxo[] = []; + let foundUnspentTransactions: UtxoWithDerivationPath[] = []; while (currentNumberOfAddressesWithoutUtxos < stopSearchAfterNumberAddressesWithoutUtxos) { const address = getTaprootAddress({ index: addressIndexCounter.getValue(), @@ -67,11 +68,19 @@ export function useTaprootAccountUtxosQuery() { } foundUnspentTransactions = [ - ...unspentTransactions.map(utxo => ({ - // adds addresss index of which utxo belongs - ...utxo, - addressIndex: addressIndexCounter.getValue(), - })), + ...unspentTransactions.map(utxo => { + const addressIndex = addressIndexCounter.getValue(); + return { + // adds addresss index of which utxo belongs + ...utxo, + addressIndex, + derivationPath: getNativeSegwitAddressIndexDerivationPath( + network.chain.bitcoin.bitcoinNetwork, + currentAccountIndex, + addressIndex + ), + }; + }), ...foundUnspentTransactions, ]; diff --git a/src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts b/src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts index 6210775f3ed..e2e18589ae0 100644 --- a/src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts +++ b/src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts @@ -6,7 +6,7 @@ import { sumNumbers } from '@app/common/math/helpers'; import { filterUtxosWithInscriptions } from '../address/utxos-by-address.hooks'; import { useTaprootAccountUtxosQuery } from '../address/utxos-by-address.query'; -import { TaprootUtxo } from '../bitcoin-client'; +import { UtxoWithDerivationPath } from '../bitcoin-client'; import { useGetInscriptionsInfiniteQuery } from '../ordinals/inscriptions.query'; export function useCurrentTaprootAccountUninscribedUtxos() { @@ -19,7 +19,7 @@ export function useCurrentTaprootAccountUninscribedUtxos() { return filterUtxosWithInscriptions( inscriptions, utxos.filter(utxo => utxo.status.confirmed) - ) as TaprootUtxo[]; + ) as UtxoWithDerivationPath[]; }, [query.data?.pages, utxos]); } diff --git a/src/app/query/bitcoin/bitcoin-client.ts b/src/app/query/bitcoin/bitcoin-client.ts index 0d536727b8e..b33ec1242cb 100644 --- a/src/app/query/bitcoin/bitcoin-client.ts +++ b/src/app/query/bitcoin/bitcoin-client.ts @@ -16,8 +16,8 @@ export interface UtxoResponseItem { value: number; } -export interface TaprootUtxo extends UtxoResponseItem { - addressIndex: number; +export interface UtxoWithDerivationPath extends UtxoResponseItem { + derivationPath: string; } class AddressApi { From 95b722cfcf03329c221d12fc29ad1141e6e2af80 Mon Sep 17 00:00:00 2001 From: Ordinarius <137325073+ordinariusprof@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:14:48 -0800 Subject: [PATCH 2/2] feat: remove unused url in brc20 request, ref #4833 --- src/app/query/bitcoin/ordinalsbot-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/query/bitcoin/ordinalsbot-client.ts b/src/app/query/bitcoin/ordinalsbot-client.ts index 5ef39fcaf1d..5a0728cd4d8 100644 --- a/src/app/query/bitcoin/ordinalsbot-client.ts +++ b/src/app/query/bitcoin/ordinalsbot-client.ts @@ -125,7 +125,7 @@ class OrdinalsbotClient { async order({ receiveAddress, file, fee, size, name }: InscriptionOrderArgs) { return axios.post(urlJoin(this.baseUrl, 'order'), { receiveAddress, - files: [{ dataURL: file, size, name, type: 'plain/text', url: '' }], + files: [{ dataURL: file, size, name, type: 'plain/text' }], fee, lowPostage: true, });