mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-01-12 22:53:27 +08:00
refactor: psbt ui to inputs and outputs
This commit is contained in:
@@ -9,7 +9,7 @@ import { Tooltip } from '@app/components/tooltip';
|
||||
|
||||
interface PsbtDecodedNodeLayoutProps {
|
||||
hoverLabel?: string;
|
||||
image?: JSX.Element;
|
||||
image?: React.JSX.Element;
|
||||
subtitle?: string;
|
||||
subValue?: string;
|
||||
subValueAction?(): void;
|
||||
@@ -2,7 +2,7 @@ import { Box, Text } from '@stacks/ui';
|
||||
|
||||
import { PsbtDecodedNodeLayout } from './psbt-decoded-node.layout';
|
||||
|
||||
export function PsbtInputOutputPlaceholder() {
|
||||
export function PsbtPlaceholderNode() {
|
||||
return (
|
||||
<Box background="white" borderRadius="16px" p="loose">
|
||||
<Text fontWeight={500}>Inputs</Text>
|
||||
@@ -0,0 +1,46 @@
|
||||
import { truncateMiddle } from '@stacks/ui-utils';
|
||||
|
||||
import { i18nFormatCurrency } from '@app/common/money/format-money';
|
||||
import { satToBtc } from '@app/common/money/unit-conversion';
|
||||
import { OrdApiInscriptionTxOutput } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
import { TaprootUtxo } from '@app/query/bitcoin/ordinals/use-taproot-address-utxos.query';
|
||||
import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks';
|
||||
|
||||
import { PsbtDecodedNodeLayout } from '../../psbt-decoded-node.layout';
|
||||
import { PsbtUnsignedInputWithInscription } from './psbt-unsigned-input-with-inscription';
|
||||
|
||||
interface PsbtUnsignedInputItemProps {
|
||||
addressNativeSegwit: string;
|
||||
addressTaproot: string;
|
||||
utxo: TaprootUtxo & OrdApiInscriptionTxOutput;
|
||||
}
|
||||
export function PsbtUnsignedInputItem({
|
||||
addressNativeSegwit,
|
||||
addressTaproot,
|
||||
utxo,
|
||||
}: PsbtUnsignedInputItemProps) {
|
||||
const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue();
|
||||
|
||||
const isInputCurrentAddress =
|
||||
utxo.address === addressNativeSegwit || utxo.address === addressTaproot;
|
||||
const inputValue = satToBtc(utxo.value).toString();
|
||||
const fiatValue = i18nFormatCurrency(calculateBitcoinFiatValue(utxo.value));
|
||||
const inscription = utxo.inscriptions;
|
||||
|
||||
if (!utxo.address) return null;
|
||||
|
||||
return inscription ? (
|
||||
<PsbtUnsignedInputWithInscription
|
||||
address={utxo.address}
|
||||
inputValue={inputValue}
|
||||
path={inscription}
|
||||
/>
|
||||
) : (
|
||||
<PsbtDecodedNodeLayout
|
||||
hoverLabel={utxo.address}
|
||||
subtitle={truncateMiddle(utxo.address)}
|
||||
subValue={`${fiatValue} USD`}
|
||||
value={`${isInputCurrentAddress ? '-' : '+'}${inputValue}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +1,49 @@
|
||||
import { Box } from '@stacks/ui';
|
||||
import { truncateMiddle } from '@stacks/ui-utils';
|
||||
|
||||
import { isUndefined } from '@shared/utils';
|
||||
|
||||
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
|
||||
import { OrdinalIcon } from '@app/components/icons/ordinal-icon';
|
||||
import { InscriptionPreview } from '@app/components/inscription-preview-card/components/inscription-preview';
|
||||
import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { useInscription } from '@app/query/bitcoin/ordinals/inscription.hooks';
|
||||
|
||||
import { PsbtDecodedNodeLayout } from './psbt-decoded-node.layout';
|
||||
import { PsbtDecodedNodeLayout } from '../../psbt-decoded-node.layout';
|
||||
|
||||
interface PsbtInputWithInscriptionProps {
|
||||
interface PsbtUnsignedInputWithInscriptionProps {
|
||||
address: string;
|
||||
inputValue: string;
|
||||
path: string;
|
||||
}
|
||||
export function PsbtInputWithInscription({
|
||||
export function PsbtUnsignedInputWithInscription({
|
||||
address,
|
||||
inputValue,
|
||||
path,
|
||||
}: PsbtInputWithInscriptionProps) {
|
||||
}: PsbtUnsignedInputWithInscriptionProps) {
|
||||
const {
|
||||
isLoading,
|
||||
isError,
|
||||
data: inscription,
|
||||
} = useInscription(path.replace('/inscription/', ''));
|
||||
|
||||
if (isLoading || isError) return null;
|
||||
if (isLoading)
|
||||
return (
|
||||
<Box my="loose">
|
||||
<LoadingSpinner />
|
||||
</Box>
|
||||
);
|
||||
if (isError || isUndefined(inscription))
|
||||
return (
|
||||
<PsbtDecodedNodeLayout
|
||||
hoverLabel={address}
|
||||
image={<OrdinalIcon />}
|
||||
subtitle={truncateMiddle(address)}
|
||||
subValue="# Unknown"
|
||||
title="No data"
|
||||
value={`-${inputValue}`}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<PsbtDecodedNodeLayout
|
||||
@@ -32,7 +53,7 @@ export function PsbtInputWithInscription({
|
||||
subValue={`#${inscription.number}`}
|
||||
subValueAction={() => openInNewTab(inscription.infoUrl)}
|
||||
title="Ordinal inscription"
|
||||
value={`- ${inputValue}`}
|
||||
value={`-${inputValue}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import { Box, Text } from '@stacks/ui';
|
||||
|
||||
import { isUndefined } from '@shared/utils';
|
||||
|
||||
import { useOrdinalsAwareUtxoQueries } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
|
||||
import { PsbtDecodedNodeLayout } from '../psbt-decoded-node.layout';
|
||||
import { PsbtPlaceholderNode } from '../psbt-placeholder-node';
|
||||
import { PsbtUnsignedInputItem } from './components/psbt-unsigned-input-item';
|
||||
|
||||
interface PsbtUnsignedInputListProps {
|
||||
addressNativeSegwit: string;
|
||||
addressTaproot: string;
|
||||
inputs: btc.TransactionInputRequired[];
|
||||
showPlaceholder: boolean;
|
||||
}
|
||||
export function PsbtUnsignedInputList({
|
||||
addressNativeSegwit,
|
||||
addressTaproot,
|
||||
inputs,
|
||||
showPlaceholder,
|
||||
}: PsbtUnsignedInputListProps) {
|
||||
const unsignedUtxos = useOrdinalsAwareUtxoQueries(inputs).map(query => query.data);
|
||||
|
||||
return (
|
||||
<Box background="white" borderTopLeftRadius="16px" borderTopRightRadius="16px" p="loose">
|
||||
<Text fontWeight={500}>Inputs</Text>
|
||||
{showPlaceholder ? (
|
||||
<PsbtPlaceholderNode />
|
||||
) : (
|
||||
unsignedUtxos.map(utxo => {
|
||||
if (isUndefined(utxo)) return <PsbtDecodedNodeLayout value="No input data found" />;
|
||||
|
||||
return (
|
||||
<PsbtUnsignedInputItem
|
||||
addressNativeSegwit={addressNativeSegwit}
|
||||
addressTaproot={addressTaproot}
|
||||
key={utxo.transaction}
|
||||
utxo={utxo}
|
||||
/>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import { truncateMiddle } from '@stacks/ui-utils';
|
||||
|
||||
import { getAddressFromOutScript } from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
|
||||
import { i18nFormatCurrency } from '@app/common/money/format-money';
|
||||
import { satToBtc } from '@app/common/money/unit-conversion';
|
||||
import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks';
|
||||
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
|
||||
|
||||
import { PsbtDecodedNodeLayout } from '../../psbt-decoded-node.layout';
|
||||
|
||||
interface PsbtUnsignedOutputItemProps {
|
||||
addressNativeSegwit: string;
|
||||
addressTaproot: string;
|
||||
output: btc.TransactionOutputRequired;
|
||||
}
|
||||
export function PsbtUnsignedOutputItem({
|
||||
addressNativeSegwit,
|
||||
addressTaproot,
|
||||
output,
|
||||
}: PsbtUnsignedOutputItemProps) {
|
||||
const network = useCurrentNetwork();
|
||||
const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue();
|
||||
|
||||
const addressFromScript = getAddressFromOutScript(output.script, network.chain.bitcoin.network);
|
||||
|
||||
const isOutputCurrentAddress =
|
||||
addressFromScript === addressNativeSegwit || addressFromScript === addressTaproot;
|
||||
const outputValue = satToBtc(Number(output.amount)).toString();
|
||||
|
||||
return (
|
||||
<PsbtDecodedNodeLayout
|
||||
hoverLabel={addressFromScript}
|
||||
subtitle={truncateMiddle(addressFromScript)}
|
||||
subValue={`${i18nFormatCurrency(calculateBitcoinFiatValue(outputValue))} USD`}
|
||||
value={`${isOutputCurrentAddress ? '+' : ' '}${outputValue}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import { Box, Text } from '@stacks/ui';
|
||||
|
||||
import { PsbtPlaceholderNode } from '../psbt-placeholder-node';
|
||||
import { PsbtUnsignedOutputItem } from './components/psbt-unsigned-output-item';
|
||||
|
||||
interface PsbtUnsignedOutputListProps {
|
||||
addressNativeSegwit: string;
|
||||
addressTaproot: string;
|
||||
outputs: btc.TransactionOutputRequired[];
|
||||
showPlaceholder: boolean;
|
||||
}
|
||||
export function PsbtUnsignedOutputList({
|
||||
addressNativeSegwit,
|
||||
addressTaproot,
|
||||
outputs,
|
||||
showPlaceholder,
|
||||
}: PsbtUnsignedOutputListProps) {
|
||||
return (
|
||||
<Box background="white" borderBottomLeftRadius="16px" borderBottomRightRadius="16px" p="loose">
|
||||
<Text fontWeight={500}>Outputs</Text>
|
||||
{showPlaceholder ? (
|
||||
<PsbtPlaceholderNode />
|
||||
) : (
|
||||
outputs.map((output, i) => {
|
||||
return (
|
||||
<PsbtUnsignedOutputItem
|
||||
addressNativeSegwit={addressNativeSegwit}
|
||||
addressTaproot={addressTaproot}
|
||||
key={i}
|
||||
output={output}
|
||||
/>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +1,36 @@
|
||||
import { InputsOutputPair } from '@app/pages/psbt-request/hooks/match-inputs-and-outputs';
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { PsbtInputOutputPair } from '../psbt-input-output-pair/psbt-input-output-pair';
|
||||
import { PsbtInputOutputPlaceholder } from '../psbt-input-output-pair/psbt-input-output-placeholder';
|
||||
import { PsbtUnsignedInputList } from '../psbt-decoded-request-node/psbt-unsigned-input-list/psbt-unsigned-input-list';
|
||||
import { PsbtUnsignedOutputList } from '../psbt-decoded-request-node/psbt-unsigned-output-list/psbt-unsigned-output-list';
|
||||
|
||||
interface PsbtDecodedRequestSimpleProps {
|
||||
bitcoinAddressNativeSegwit: string;
|
||||
bitcoinAddressTaproot: string;
|
||||
inputOutputPairs: InputsOutputPair[];
|
||||
inputs: btc.TransactionInputRequired[];
|
||||
outputs: btc.TransactionOutputRequired[];
|
||||
showPlaceholder: boolean;
|
||||
}
|
||||
export function PsbtDecodedRequestSimple({
|
||||
bitcoinAddressNativeSegwit,
|
||||
bitcoinAddressTaproot,
|
||||
inputOutputPairs,
|
||||
inputs,
|
||||
outputs,
|
||||
showPlaceholder,
|
||||
}: PsbtDecodedRequestSimpleProps) {
|
||||
if (showPlaceholder) return <PsbtInputOutputPlaceholder />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{inputOutputPairs.map((pair, i) => {
|
||||
return (
|
||||
<PsbtInputOutputPair
|
||||
addressNativeSegwit={bitcoinAddressNativeSegwit}
|
||||
addressTaproot={bitcoinAddressTaproot}
|
||||
inputOutputPair={pair}
|
||||
isFirstPair={i === 0}
|
||||
isLastPair={i === inputOutputPairs.length - 1}
|
||||
key={i}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<PsbtUnsignedInputList
|
||||
addressNativeSegwit={bitcoinAddressNativeSegwit}
|
||||
addressTaproot={bitcoinAddressTaproot}
|
||||
inputs={inputs}
|
||||
showPlaceholder={showPlaceholder}
|
||||
/>
|
||||
<PsbtUnsignedOutputList
|
||||
addressNativeSegwit={bitcoinAddressNativeSegwit}
|
||||
addressTaproot={bitcoinAddressTaproot}
|
||||
outputs={outputs}
|
||||
showPlaceholder={showPlaceholder}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import { Stack, color } from '@stacks/ui';
|
||||
|
||||
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentAccountTaprootAddressIndexZeroPayment } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks';
|
||||
|
||||
import { PsbtInput, usePsbtDecodedRequest } from '../../hooks/use-psbt-decoded-request';
|
||||
import { usePsbtDecodedRequest } from '../../hooks/use-psbt-decoded-request';
|
||||
import { PsbtDecodedRequestAdvanced } from './psbt-decoded-request-views/psbt-decoded-request-advanced';
|
||||
import { PsbtDecodedRequestSimple } from './psbt-decoded-request-views/psbt-decoded-request-simple';
|
||||
import { PsbtDecodedRequestViewToggle } from './psbt-decoded-request-views/psbt-decoded-request-view-toggle';
|
||||
@@ -13,20 +13,17 @@ interface PsbtDecodedRequestProps {
|
||||
psbt: any;
|
||||
}
|
||||
export function PsbtDecodedRequest({ psbt }: PsbtDecodedRequestProps) {
|
||||
const bitcoinAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero();
|
||||
const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner();
|
||||
const { address: bitcoinAddressTaproot } = useCurrentAccountTaprootAddressIndexZeroPayment();
|
||||
const psbtInputs: PsbtInput[] = psbt.inputs;
|
||||
const unsignedInputs: btc.TransactionInputRequired[] = psbt.global.unsignedTx.inputs;
|
||||
const unsignedOutputs: btc.TransactionOutputRequired[] = psbt.global.unsignedTx.outputs;
|
||||
|
||||
const {
|
||||
inputOutputPairs,
|
||||
onSetShowAdvancedView,
|
||||
shouldDefaultToAdvancedView,
|
||||
shouldShowPlaceholder,
|
||||
showAdvancedView,
|
||||
} = usePsbtDecodedRequest({
|
||||
psbtInputs,
|
||||
unsignedInputs,
|
||||
unsignedOutputs,
|
||||
});
|
||||
@@ -45,9 +42,10 @@ export function PsbtDecodedRequest({ psbt }: PsbtDecodedRequestProps) {
|
||||
<PsbtDecodedRequestAdvanced psbt={psbt} />
|
||||
) : (
|
||||
<PsbtDecodedRequestSimple
|
||||
bitcoinAddressNativeSegwit={bitcoinAddressNativeSegwit}
|
||||
bitcoinAddressNativeSegwit={nativeSegwitSigner.address}
|
||||
bitcoinAddressTaproot={bitcoinAddressTaproot}
|
||||
inputOutputPairs={inputOutputPairs}
|
||||
inputs={unsignedInputs}
|
||||
outputs={unsignedOutputs}
|
||||
showPlaceholder={shouldShowPlaceholder}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { Box, Text } from '@stacks/ui';
|
||||
import { truncateMiddle } from '@stacks/ui-utils';
|
||||
|
||||
import { getAddressFromOutScript } from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
import { createMoneyFromDecimal } from '@shared/models/money.model';
|
||||
|
||||
import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
|
||||
import { i18nFormatCurrency } from '@app/common/money/format-money';
|
||||
import { satToBtc } from '@app/common/money/unit-conversion';
|
||||
import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks';
|
||||
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
|
||||
|
||||
import { InputsOutputPair } from '../../../hooks/match-inputs-and-outputs';
|
||||
import { PsbtDecodedNodeLayout } from './psbt-decoded-node.layout';
|
||||
import { PsbtInputWithInscription } from './psbt-input-with-inscription';
|
||||
|
||||
interface PsbtInputOutputPairProps {
|
||||
addressNativeSegwit: string;
|
||||
addressTaproot: string;
|
||||
inputOutputPair: InputsOutputPair;
|
||||
isFirstPair: boolean;
|
||||
isLastPair: boolean;
|
||||
}
|
||||
export function PsbtInputOutputPair({
|
||||
addressNativeSegwit,
|
||||
addressTaproot,
|
||||
inputOutputPair,
|
||||
isFirstPair,
|
||||
isLastPair,
|
||||
}: PsbtInputOutputPairProps) {
|
||||
const { inputs, output } = inputOutputPair;
|
||||
const network = useCurrentNetwork();
|
||||
const btcMarketData = useCryptoCurrencyMarketData('BTC');
|
||||
const addressFromScript = getAddressFromOutScript(output.script, network.chain.bitcoin.network);
|
||||
|
||||
const getFiatValue = useCallback(
|
||||
(value: string) =>
|
||||
i18nFormatCurrency(
|
||||
baseCurrencyAmountInQuote(createMoneyFromDecimal(Number(value), 'BTC'), btcMarketData)
|
||||
),
|
||||
[btcMarketData]
|
||||
);
|
||||
|
||||
const isOutputCurrentAddress =
|
||||
addressFromScript === addressNativeSegwit || addressFromScript === addressTaproot;
|
||||
const outputValue = satToBtc(Number(output.amount)).toString();
|
||||
|
||||
return (
|
||||
<Box
|
||||
background="white"
|
||||
borderBottomLeftRadius={isLastPair ? '16px' : 'unset'}
|
||||
borderBottomRightRadius={isLastPair ? '16px' : 'unset'}
|
||||
borderTopLeftRadius={isFirstPair ? '16px' : 'unset'}
|
||||
borderTopRightRadius={isFirstPair ? '16px' : 'unset'}
|
||||
p="loose"
|
||||
>
|
||||
<Text fontWeight={500}>Inputs</Text>
|
||||
{inputs.map(input => {
|
||||
const isInputCurrentAddress =
|
||||
input.address === addressNativeSegwit || input.address === addressTaproot;
|
||||
const inputValue = satToBtc(input.value).toString();
|
||||
const fiatValue = getFiatValue(inputValue);
|
||||
const inscription = input.unsignedUtxo?.inscriptions;
|
||||
|
||||
return inscription ? (
|
||||
<PsbtInputWithInscription
|
||||
address={input.address}
|
||||
inputValue={inputValue}
|
||||
path={inscription}
|
||||
/>
|
||||
) : (
|
||||
<PsbtDecodedNodeLayout
|
||||
hoverLabel={input.address}
|
||||
subtitle={truncateMiddle(input.address)}
|
||||
subValue={`${fiatValue} USD`}
|
||||
value={`${isInputCurrentAddress ? '-' : '+'} ${inputValue}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<hr />
|
||||
<Text fontWeight={500} mt="loose">
|
||||
Output
|
||||
</Text>
|
||||
<PsbtDecodedNodeLayout
|
||||
hoverLabel={addressFromScript}
|
||||
subtitle={truncateMiddle(addressFromScript)}
|
||||
subValue={`${getFiatValue(outputValue)} USD`}
|
||||
value={`${isOutputCurrentAddress ? '+' : ' '} ${outputValue}`}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { BitcoinNetworkModes } from '@shared/constants';
|
||||
import { getAddressFromOutScript } from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
import { isDefined } from '@shared/utils';
|
||||
|
||||
import { OrdApiInscriptionTxOutput } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
|
||||
import { PsbtInput, PsbtInputForUi } from './use-psbt-decoded-request';
|
||||
|
||||
function getPsbtInputValue(input: PsbtInput, unsignedUtxo?: OrdApiInscriptionTxOutput) {
|
||||
if (isDefined(input.witnessUtxo)) return Number(input.witnessUtxo.amount);
|
||||
if (isDefined(input.nonWitnessUtxo) && isDefined(unsignedUtxo)) return Number(unsignedUtxo.value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getPsbtInputAddress(
|
||||
input: PsbtInput,
|
||||
network: BitcoinNetworkModes,
|
||||
unsignedUtxo?: OrdApiInscriptionTxOutput
|
||||
) {
|
||||
if (isDefined(input.witnessUtxo))
|
||||
return getAddressFromOutScript(input.witnessUtxo.script, network);
|
||||
if (isDefined(input.nonWitnessUtxo) && isDefined(unsignedUtxo)) return unsignedUtxo.address ?? '';
|
||||
return '';
|
||||
}
|
||||
|
||||
interface BuildPsbtInputForUiArgs {
|
||||
network: BitcoinNetworkModes;
|
||||
psbtInputs: PsbtInput[];
|
||||
unsignedUtxos: (OrdApiInscriptionTxOutput | undefined)[];
|
||||
}
|
||||
export function buildPsbtInputsForUi({
|
||||
network,
|
||||
psbtInputs,
|
||||
unsignedUtxos,
|
||||
}: BuildPsbtInputForUiArgs): PsbtInputForUi[] {
|
||||
return psbtInputs.map((input, i) => {
|
||||
const utxoAddress = getPsbtInputAddress(input, network, unsignedUtxos[i]);
|
||||
const utxoValue = getPsbtInputValue(input, unsignedUtxos[i]);
|
||||
return {
|
||||
...input,
|
||||
address: utxoAddress,
|
||||
value: utxoValue,
|
||||
unsignedUtxo: unsignedUtxos[i],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import {
|
||||
mockInputOutputPairs,
|
||||
mockInputOutputPairsWithNonWitnessOnly,
|
||||
mockPsbtInputs,
|
||||
mockPsbtInputsWithNonWitnessOnly,
|
||||
mockPsbtUnsignedOutputs,
|
||||
} from '@tests/mocks/mock-psbts';
|
||||
|
||||
import { matchInputsOutputs } from './match-inputs-and-outputs';
|
||||
|
||||
describe('matching psbt inputs and outputs', () => {
|
||||
test('that psbt inputs and outputs can be paired correctly when witness data is provided', () => {
|
||||
const { inputOutputPairs } = matchInputsOutputs({
|
||||
psbtInputs: mockPsbtInputs,
|
||||
unsignedOutputs: mockPsbtUnsignedOutputs,
|
||||
});
|
||||
expect(inputOutputPairs).toEqual(mockInputOutputPairs);
|
||||
});
|
||||
|
||||
test('that psbt inputs and outputs can be paired correctly when only non-witness data is provided', () => {
|
||||
const { inputOutputPairs } = matchInputsOutputs({
|
||||
psbtInputs: mockPsbtInputsWithNonWitnessOnly,
|
||||
unsignedOutputs: mockPsbtUnsignedOutputs,
|
||||
});
|
||||
expect(inputOutputPairs).toEqual(mockInputOutputPairsWithNonWitnessOnly);
|
||||
});
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { isDefined } from '@shared/utils';
|
||||
|
||||
import { PsbtInputForUi } from './use-psbt-decoded-request';
|
||||
|
||||
export interface InputsOutputPair {
|
||||
inputs: PsbtInputForUi[];
|
||||
output: btc.TransactionOutputRequired;
|
||||
}
|
||||
|
||||
function consolidateInputsWithSameAddress(inputs: PsbtInputForUi[]): PsbtInputForUi[] {
|
||||
const utxo: Record<string, PsbtInputForUi> = {};
|
||||
inputs.forEach(({ address, value, ...rest }) => {
|
||||
if (isDefined(utxo[address])) {
|
||||
utxo[address] = {
|
||||
...utxo[address],
|
||||
value: utxo[address].value + value,
|
||||
};
|
||||
} else {
|
||||
utxo[address] = { address, value, ...rest };
|
||||
}
|
||||
});
|
||||
return Object.values(utxo);
|
||||
}
|
||||
|
||||
interface MatchInputsOutputsArgs {
|
||||
psbtInputs: PsbtInputForUi[];
|
||||
unsignedOutputs: btc.TransactionOutputRequired[];
|
||||
}
|
||||
export function matchInputsOutputs({ psbtInputs, unsignedOutputs }: MatchInputsOutputsArgs): {
|
||||
fee: number;
|
||||
inputOutputPairs: InputsOutputPair[];
|
||||
} {
|
||||
const pairs: InputsOutputPair[] = [];
|
||||
const availableInputs = [...psbtInputs];
|
||||
// Keep track of the input value available across multiple outputs
|
||||
let remainingInputValue = 0;
|
||||
|
||||
for (const output of unsignedOutputs) {
|
||||
// Note here that the inputs/output `pair` uses an inputs array so
|
||||
// multiple inputs can be used to fulfill an output
|
||||
const inputsOutputPair: InputsOutputPair = {
|
||||
inputs: [],
|
||||
output: output,
|
||||
};
|
||||
|
||||
// Calculate the remaining value needed for this output
|
||||
let remainingOutputValue = Number(output.amount);
|
||||
|
||||
// Iterate through the available inputs and add them until the output is fulfilled
|
||||
for (let i = 0; i < availableInputs.length; i++) {
|
||||
const input = availableInputs[i];
|
||||
|
||||
if (remainingInputValue === 0) remainingInputValue = input.value;
|
||||
|
||||
if (input.value <= remainingOutputValue) {
|
||||
// Use this entire input and subtract from output value
|
||||
inputsOutputPair.inputs.push(input);
|
||||
remainingOutputValue -= input.value;
|
||||
remainingInputValue = 0;
|
||||
availableInputs.splice(i, 1);
|
||||
i--;
|
||||
} else {
|
||||
// Use this input but keep track of remainder for future outputs
|
||||
remainingInputValue -= remainingOutputValue;
|
||||
inputsOutputPair.inputs.push(input);
|
||||
remainingOutputValue = 0;
|
||||
}
|
||||
|
||||
if (remainingOutputValue === 0) break;
|
||||
}
|
||||
|
||||
pairs.push(inputsOutputPair);
|
||||
}
|
||||
|
||||
const inputOutputPairs: InputsOutputPair[] = pairs.map(pair => {
|
||||
return { inputs: consolidateInputsWithSameAddress(pair.inputs), output: pair.output };
|
||||
});
|
||||
|
||||
return { fee: remainingInputValue, inputOutputPairs };
|
||||
}
|
||||
@@ -1,47 +1,18 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { BitcoinNetworkModes } from '@shared/constants';
|
||||
import { getAddressFromOutScript } from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
import { logger } from '@shared/logger';
|
||||
import { isEmpty, isUndefined } from '@shared/utils';
|
||||
import { isUndefined } from '@shared/utils';
|
||||
|
||||
import {
|
||||
OrdApiInscriptionTxOutput,
|
||||
useOrdinalsAwareUtxoQueries,
|
||||
} from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
|
||||
|
||||
import { buildPsbtInputsForUi } from './build-psbt-input-for-ui';
|
||||
import { matchInputsOutputs } from './match-inputs-and-outputs';
|
||||
|
||||
export interface NonWitnessUtxo {
|
||||
version: number;
|
||||
segwitFlag: boolean;
|
||||
inputs: btc.TransactionInputRequired[];
|
||||
outputs: btc.TransactionOutputRequired[];
|
||||
witnesses: string[][];
|
||||
lockTime: number;
|
||||
}
|
||||
|
||||
interface WitnessUtxo {
|
||||
script: Uint8Array;
|
||||
amount: bigint;
|
||||
}
|
||||
|
||||
export interface PsbtInput {
|
||||
nonWitnessUtxo?: NonWitnessUtxo;
|
||||
witnessUtxo?: WitnessUtxo;
|
||||
}
|
||||
|
||||
export interface PsbtInputForUi extends PsbtInput {
|
||||
address: string;
|
||||
unsignedUtxo?: OrdApiInscriptionTxOutput;
|
||||
value: number;
|
||||
}
|
||||
|
||||
function isPlaceholderTransaction(
|
||||
address: string,
|
||||
inputs: (OrdApiInscriptionTxOutput | undefined)[],
|
||||
@@ -59,67 +30,34 @@ function isPlaceholderTransaction(
|
||||
}
|
||||
|
||||
interface UsePsbtDecodedRequestArgs {
|
||||
psbtInputs: PsbtInput[];
|
||||
unsignedInputs: btc.TransactionInputRequired[];
|
||||
unsignedOutputs: btc.TransactionOutputRequired[];
|
||||
}
|
||||
export function usePsbtDecodedRequest({
|
||||
psbtInputs,
|
||||
unsignedInputs,
|
||||
unsignedOutputs,
|
||||
}: UsePsbtDecodedRequestArgs) {
|
||||
const [showAdvancedView, setShowAdvancedView] = useState(false);
|
||||
const network = useCurrentNetwork();
|
||||
const bitcoinAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero();
|
||||
const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner();
|
||||
const unsignedUtxos = useOrdinalsAwareUtxoQueries(unsignedInputs).map(query => query.data);
|
||||
|
||||
const inputs = useMemo(() => {
|
||||
if (isUndefined(unsignedUtxos)) {
|
||||
logger.error('No UTXOs to sign');
|
||||
return [];
|
||||
}
|
||||
return buildPsbtInputsForUi({
|
||||
network: network.chain.bitcoin.network,
|
||||
psbtInputs,
|
||||
unsignedUtxos,
|
||||
});
|
||||
}, [network.chain.bitcoin.network, psbtInputs, unsignedUtxos]);
|
||||
|
||||
const { fee, inputOutputPairs } = useMemo(
|
||||
() =>
|
||||
matchInputsOutputs({
|
||||
psbtInputs: inputs,
|
||||
unsignedOutputs,
|
||||
}),
|
||||
[inputs, unsignedOutputs]
|
||||
);
|
||||
|
||||
const defaultToAdvancedView = useCallback(() => {
|
||||
const pairsWithNoInputs = inputOutputPairs.filter(pair => {
|
||||
return isUndefined(pair.inputs) || !pair.inputs.length;
|
||||
});
|
||||
const pairsWithNoOutputs = inputOutputPairs.filter(pair => {
|
||||
return isUndefined(pair.output) || isEmpty(pair.output);
|
||||
});
|
||||
return !!(
|
||||
inputOutputPairs.length === 0 ||
|
||||
pairsWithNoInputs.length ||
|
||||
pairsWithNoOutputs.length
|
||||
);
|
||||
}, [inputOutputPairs]);
|
||||
const noInputs = isUndefined(unsignedUtxos) || !unsignedUtxos.length;
|
||||
const noOutputs = isUndefined(unsignedOutputs) || !unsignedOutputs.length;
|
||||
return noInputs || noOutputs;
|
||||
}, [unsignedOutputs, unsignedUtxos]);
|
||||
|
||||
const showPlaceholder = useCallback(() => {
|
||||
return isPlaceholderTransaction(
|
||||
bitcoinAddressNativeSegwit,
|
||||
nativeSegwitSigner.address,
|
||||
unsignedUtxos,
|
||||
unsignedOutputs,
|
||||
network.chain.bitcoin.network
|
||||
);
|
||||
}, [bitcoinAddressNativeSegwit, network.chain.bitcoin.network, unsignedOutputs, unsignedUtxos]);
|
||||
}, [nativeSegwitSigner.address, network.chain.bitcoin.network, unsignedOutputs, unsignedUtxos]);
|
||||
|
||||
return {
|
||||
fee,
|
||||
inputOutputPairs,
|
||||
onSetShowAdvancedView: () => setShowAdvancedView(!showAdvancedView),
|
||||
shouldDefaultToAdvancedView: defaultToAdvancedView(),
|
||||
shouldShowPlaceholder: showPlaceholder(),
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { CryptoCurrencies } from '@shared/models/currencies.model';
|
||||
import { MarketData, createMarketData, createMarketPair } from '@shared/models/market.model';
|
||||
import { createMoney, currencyDecimalsMap } from '@shared/models/money.model';
|
||||
import { createMoneyFromDecimal } from '@shared/models/money.model';
|
||||
|
||||
import { calculateMeanAverage } from '@app/common/math/calculate-averages';
|
||||
import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money';
|
||||
import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
|
||||
|
||||
import {
|
||||
selectBinanceUsdPrice,
|
||||
@@ -50,3 +53,13 @@ export function useCryptoCurrencyMarketData(currency: CryptoCurrencies): MarketD
|
||||
return createMarketData(createMarketPair(currency, 'USD'), createMoney(meanStxPrice, 'USD'));
|
||||
}, [binance, coincap, coingecko, currency]);
|
||||
}
|
||||
|
||||
export function useCalculateBitcoinFiatValue() {
|
||||
const btcMarketData = useCryptoCurrencyMarketData('BTC');
|
||||
|
||||
return useCallback(
|
||||
(value: string) =>
|
||||
baseCurrencyAmountInQuote(createMoneyFromDecimal(Number(value), 'BTC'), btcMarketData),
|
||||
[btcMarketData]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
import { hexToBytes } from '@stacks/common';
|
||||
|
||||
import { OrdApiInscriptionTxOutput } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
|
||||
import {
|
||||
NonWitnessUtxo,
|
||||
PsbtInputForUi,
|
||||
} from '../../src/app/pages/psbt-request/hooks/use-psbt-decoded-request';
|
||||
|
||||
export const mockPsbtUnsignedOutputs = [
|
||||
{
|
||||
amount: BigInt(1200),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(10000),
|
||||
script: hexToBytes('5120286626028a2d352bae8dcdfa750025d04ce7f5eb6649a4ddb9ef98eba6315f47'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(98500),
|
||||
script: hexToBytes('a914fdb87fb236e8530cd4dd97ad9ebe810c265aa0ef87'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(1500),
|
||||
script: hexToBytes('0014213e3440d21efb9e8e4000fadb90e9c8ef5e3c8d'),
|
||||
},
|
||||
];
|
||||
|
||||
const mockPsbtInputNonWitnessUtxo: NonWitnessUtxo = {
|
||||
version: 2,
|
||||
segwitFlag: true,
|
||||
inputs: [
|
||||
{
|
||||
txid: hexToBytes('e41ca83d77ab711923f25fc88e13d273e4f3828f2d1d6e2576d4b29bcbb32f38'),
|
||||
index: 1,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967295,
|
||||
},
|
||||
{
|
||||
txid: hexToBytes('f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f'),
|
||||
index: 0,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967295,
|
||||
},
|
||||
{
|
||||
txid: hexToBytes('80de4905b7de5acf913dd68e9d2953df04231854ee4b5e06e7b0821a991200de'),
|
||||
index: 0,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967295,
|
||||
},
|
||||
{
|
||||
txid: hexToBytes('f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f'),
|
||||
index: 1,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967295,
|
||||
},
|
||||
{
|
||||
txid: hexToBytes('f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f'),
|
||||
index: 2,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967295,
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
amount: BigInt(600),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(600),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(112568),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
{
|
||||
amount: BigInt(224743),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
],
|
||||
witnesses: [
|
||||
[
|
||||
'30440220437e1fd865a1c7201a54ce4fe9729d2966563681812b2c7f71f78c8344b3931502204503703f957f0701cfc208b05b8deb396a6a6fcaae5b51bbc6d3ec1cb832d55e01',
|
||||
'039aa0f222ca60f0a4d03ae8047d5972856af82ff186d1b99186fc7b44f0d737c6',
|
||||
],
|
||||
[
|
||||
'3045022100e32250233e40fc4f89a0f1d427d46aaea0a2b1f677f81d6e6542002efb52a21e02206430455c2098f82ec17f347832d022acc4d9bc7f4486582c3bb0b47ab254c9a901',
|
||||
'039aa0f222ca60f0a4d03ae8047d5972856af82ff186d1b99186fc7b44f0d737c6',
|
||||
],
|
||||
[
|
||||
'304402201dfb85876c7ccc56a2f8691780dd8e0ddb6c117f8cf46441a0a26a4b8f6711ca022033c4d5b59304dbe2828d5498795909ee6143de86ed67aeea928b4e39bf3496f601',
|
||||
'039aa0f222ca60f0a4d03ae8047d5972856af82ff186d1b99186fc7b44f0d737c6',
|
||||
],
|
||||
[
|
||||
'3044022027c01031326e714276c22c65713a9d0b38f082dbf36afd1050da13f1af44f384022070219f1b496eea8f14cd038c6962e5b4ef6b0a6c1439ffeec7c2c80b540a838a01',
|
||||
'039aa0f222ca60f0a4d03ae8047d5972856af82ff186d1b99186fc7b44f0d737c6',
|
||||
],
|
||||
[
|
||||
'3045022100acf58365b40c5cafdb7707dacf0921dc80072dc3c81953b234f1de301b597cbf02205776a4a3cc840cf923be5c5579a977607e74f2c00b6c2c099c26810b51f040ae01',
|
||||
'039aa0f222ca60f0a4d03ae8047d5972856af82ff186d1b99186fc7b44f0d737c6',
|
||||
],
|
||||
],
|
||||
lockTime: 0,
|
||||
};
|
||||
|
||||
const mockPsbtInputNonWitnessUtxoWithInscription: NonWitnessUtxo = {
|
||||
version: 1,
|
||||
segwitFlag: true,
|
||||
inputs: [
|
||||
{
|
||||
txid: hexToBytes('c4c9c967162e72f4a0414dfc4f499fe75f423da8c6fbda2645962fe504a76b74'),
|
||||
index: 0,
|
||||
finalScriptSig: hexToBytes(''),
|
||||
sequence: 4294967293,
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
amount: BigInt(9556),
|
||||
script: hexToBytes('512078f5386cce73363387d7904d5cb25d52bf42247baaaf92de03094eefbeb7d2fa'),
|
||||
},
|
||||
],
|
||||
witnesses: [
|
||||
[
|
||||
'0cf260fbf470c7128f2ce76bdcbf0d00cbfa79cfabf4277d64c0a58f66d1e4027395e9cabf09ff88bf3df3dace1fa6d0e112fa371acfd09144794e28e21ec0ad',
|
||||
],
|
||||
],
|
||||
lockTime: 0,
|
||||
};
|
||||
|
||||
const mockUnsignedUtxoInscription: OrdApiInscriptionTxOutput = {
|
||||
address: 'bc1p3rfd76c37af87e23g4z6tts0zu52u6frjh92m9uq5evxy0sr7hvslly59y',
|
||||
all_inscriptions: [],
|
||||
inscriptions: '/inscription/ff4503ab9048d6d0ff4e23def81b614d5270d341ce993992e93902ceb0d4ed79i0',
|
||||
script_pubkey:
|
||||
'OP_PUSHNUM_1 OP_PUSHBYTES_32 88d2df6b11f7527f65514545a5ae0f1728ae692395caad9780a658623e03f5d9',
|
||||
transaction: '/tx/ff4503ab9048d6d0ff4e23def81b614d5270d341ce993992e93902ceb0d4ed79',
|
||||
value: '9556',
|
||||
};
|
||||
|
||||
const mockPsbtUnsignedUtxos: OrdApiInscriptionTxOutput[] = [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
all_inscriptions: [],
|
||||
inscriptions: '',
|
||||
script_pubkey: 'OP_0 OP_PUSHBYTES_20 66124290d2fc62f8cb83c0e15836a548e43dcade',
|
||||
transaction: '/tx/f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f',
|
||||
value: '600',
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
all_inscriptions: [],
|
||||
inscriptions: '',
|
||||
script_pubkey: 'OP_0 OP_PUSHBYTES_20 66124290d2fc62f8cb83c0e15836a548e43dcade',
|
||||
transaction: '/tx/f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f',
|
||||
value: '600',
|
||||
},
|
||||
mockUnsignedUtxoInscription,
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
all_inscriptions: [],
|
||||
inscriptions: '',
|
||||
script_pubkey: 'OP_0 OP_PUSHBYTES_20 66124290d2fc62f8cb83c0e15836a548e43dcade',
|
||||
transaction: '/tx/f177c3d4ba24e18356c479d660bbb584d8a78be344c82a3e86b32aa6abd0195f',
|
||||
value: '292891',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockPsbtInputs: PsbtInputForUi[] = [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: undefined,
|
||||
value: 600,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(600),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: undefined,
|
||||
value: 600,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(600),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'bc1p0r6nsmxwwvmr8p7hjpx4evja22l5yfrm42he9hsrp98wl04h6taq4t0af2',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxoWithInscription,
|
||||
unsignedUtxo: mockUnsignedUtxoInscription,
|
||||
value: 9556,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(9556),
|
||||
script: hexToBytes('512078f5386cce73363387d7904d5cb25d52bf42247baaaf92de03094eefbeb7d2fa'),
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: undefined,
|
||||
value: 115166,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(112568),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const mockInputOutputPairs = [
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(600),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
unsignedUtxo: undefined,
|
||||
value: 1200,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(1200),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1p0r6nsmxwwvmr8p7hjpx4evja22l5yfrm42he9hsrp98wl04h6taq4t0af2',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxoWithInscription,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(9556),
|
||||
script: hexToBytes(
|
||||
'512078f5386cce73363387d7904d5cb25d52bf42247baaaf92de03094eefbeb7d2fa'
|
||||
),
|
||||
},
|
||||
unsignedUtxo: mockUnsignedUtxoInscription,
|
||||
value: 9556,
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(112568),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
unsignedUtxo: undefined,
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(10000),
|
||||
script: hexToBytes('5120286626028a2d352bae8dcdfa750025d04ce7f5eb6649a4ddb9ef98eba6315f47'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(112568),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
unsignedUtxo: undefined,
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(98500),
|
||||
script: hexToBytes('a914fdb87fb236e8530cd4dd97ad9ebe810c265aa0ef87'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(112568),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
unsignedUtxo: undefined,
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(1500),
|
||||
script: hexToBytes('0014213e3440d21efb9e8e4000fadb90e9c8ef5e3c8d'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const mockPsbtInputsWithNonWitnessOnly: PsbtInputForUi[] = [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[0],
|
||||
witnessUtxo: undefined,
|
||||
value: 600,
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[1],
|
||||
witnessUtxo: undefined,
|
||||
value: 600,
|
||||
},
|
||||
{
|
||||
address: 'bc1p0r6nsmxwwvmr8p7hjpx4evja22l5yfrm42he9hsrp98wl04h6taq4t0af2',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxoWithInscription,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[2],
|
||||
witnessUtxo: undefined,
|
||||
value: 9556,
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[3],
|
||||
witnessUtxo: undefined,
|
||||
value: 115166,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockInputOutputPairsWithNonWitnessOnly = [
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: undefined,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[0],
|
||||
value: 1200,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(1200),
|
||||
script: hexToBytes('001466124290d2fc62f8cb83c0e15836a548e43dcade'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1p0r6nsmxwwvmr8p7hjpx4evja22l5yfrm42he9hsrp98wl04h6taq4t0af2',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxoWithInscription,
|
||||
witnessUtxo: undefined,
|
||||
unsignedUtxo: mockUnsignedUtxoInscription,
|
||||
value: 9556,
|
||||
},
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: undefined,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[3],
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(10000),
|
||||
script: hexToBytes('5120286626028a2d352bae8dcdfa750025d04ce7f5eb6649a4ddb9ef98eba6315f47'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: undefined,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[3],
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(98500),
|
||||
script: hexToBytes('a914fdb87fb236e8530cd4dd97ad9ebe810c265aa0ef87'),
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
address: 'bc1qvcfy9yxjl3303jurcrs4sd49frjrmjk7x045r6',
|
||||
nonWitnessUtxo: mockPsbtInputNonWitnessUtxo,
|
||||
witnessUtxo: undefined,
|
||||
unsignedUtxo: mockPsbtUnsignedUtxos[3],
|
||||
value: 115166,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
amount: BigInt(1500),
|
||||
script: hexToBytes('0014213e3440d21efb9e8e4000fadb90e9c8ef5e3c8d'),
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user