|
| 1 | +import { bip32, BIP32Interface, bitgo } from '@bitgo/utxo-lib'; |
1 | 2 | import * as utxolib from '@bitgo/utxo-lib'; |
2 | | -import { BitGoBase, IRequestTracer, Triple } from '@bitgo/sdk-core'; |
| 3 | +import { Triple } from '@bitgo/sdk-core'; |
| 4 | + |
3 | 5 | import { |
4 | | - AbstractUtxoCoin, |
5 | 6 | DecoratedExplainTransactionOptions, |
6 | | - WalletOutput, |
7 | 7 | ExplainTransactionOptions, |
8 | | - TransactionExplanation, |
9 | | - TransactionPrebuild, |
10 | 8 | Output, |
11 | | -} from './abstractUtxoCoin'; |
12 | | -import { bip32, BIP32Interface, bitgo } from '@bitgo/utxo-lib'; |
13 | | - |
14 | | -const ScriptRecipientPrefix = 'scriptPubKey:'; |
15 | | - |
16 | | -/** |
17 | | - * Check if the address is a script recipient (starts with `scriptPubKey:`). |
18 | | - * @param address |
19 | | - */ |
20 | | -export function isScriptRecipient(address: string): boolean { |
21 | | - return address.toLowerCase().startsWith(ScriptRecipientPrefix.toLowerCase()); |
22 | | -} |
23 | | - |
24 | | -/** |
25 | | - * An extended address is one that encodes either a regular address or a hex encoded script with the prefix `scriptPubKey:`. |
26 | | - * This function converts the extended address format to either a script or an address. |
27 | | - * @param extendedAddress |
28 | | - */ |
29 | | -export function fromExtendedAddressFormat(extendedAddress: string): { address: string } | { script: string } { |
30 | | - if (isScriptRecipient(extendedAddress)) { |
31 | | - return { script: extendedAddress.slice(ScriptRecipientPrefix.length) }; |
32 | | - } |
33 | | - return { address: extendedAddress }; |
34 | | -} |
35 | | - |
36 | | -/** |
37 | | - * Convert a script or address to the extended address format. |
38 | | - * @param script |
39 | | - * @param network |
40 | | - * @returns if the script is an OP_RETURN script, then it will be prefixed with `scriptPubKey:`, otherwise it will be converted to an address. |
41 | | - */ |
42 | | -export function toExtendedAddressFormat(script: Buffer, network: utxolib.Network): string { |
43 | | - return script[0] === utxolib.opcodes.OP_RETURN |
44 | | - ? `${ScriptRecipientPrefix}${script.toString('hex')}` |
45 | | - : utxolib.address.fromOutputScript(script, network); |
46 | | -} |
47 | | - |
48 | | -export function assertValidTransactionRecipient(output: { amount: bigint | number | string; address?: string }): void { |
49 | | - // In the case that this is an OP_RETURN output or another non-encodable scriptPubkey, we dont have an address. |
50 | | - // We will verify that the amount is zero, and if it isnt then we will throw an error. |
51 | | - if (!output.address || isScriptRecipient(output.address)) { |
52 | | - if (output.amount.toString() !== '0') { |
53 | | - throw new Error(`Only zero amounts allowed for non-encodeable scriptPubkeys: ${JSON.stringify(output)}`); |
54 | | - } |
55 | | - } |
56 | | -} |
57 | | - |
58 | | -/** |
59 | | - * Get the inputs for a psbt from a prebuild. |
60 | | - */ |
61 | | -export function getPsbtTxInputs( |
62 | | - psbtArg: string | utxolib.bitgo.UtxoPsbt, |
63 | | - network: utxolib.Network |
64 | | -): { address: string; value: bigint; valueString: string }[] { |
65 | | - const psbt = psbtArg instanceof utxolib.bitgo.UtxoPsbt ? psbtArg : utxolib.bitgo.createPsbtFromHex(psbtArg, network); |
66 | | - const txInputs = psbt.txInputs; |
67 | | - return psbt.data.inputs.map((input, index) => { |
68 | | - let address: string; |
69 | | - let value: bigint; |
70 | | - if (input.witnessUtxo) { |
71 | | - address = utxolib.address.fromOutputScript(input.witnessUtxo.script, network); |
72 | | - value = input.witnessUtxo.value; |
73 | | - } else if (input.nonWitnessUtxo) { |
74 | | - const tx = utxolib.bitgo.createTransactionFromBuffer<bigint>(input.nonWitnessUtxo, network, { |
75 | | - amountType: 'bigint', |
76 | | - }); |
77 | | - const txId = (Buffer.from(txInputs[index].hash).reverse() as Buffer).toString('hex'); |
78 | | - if (tx.getId() !== txId) { |
79 | | - throw new Error('input transaction hex does not match id'); |
80 | | - } |
81 | | - const prevTxOutputIndex = txInputs[index].index; |
82 | | - address = utxolib.address.fromOutputScript(tx.outs[prevTxOutputIndex].script, network); |
83 | | - value = tx.outs[prevTxOutputIndex].value; |
84 | | - } else { |
85 | | - throw new Error('psbt input is missing both witnessUtxo and nonWitnessUtxo'); |
86 | | - } |
87 | | - return { address, value, valueString: value.toString() }; |
88 | | - }); |
89 | | -} |
| 9 | + TransactionExplanation, |
| 10 | + WalletOutput, |
| 11 | +} from '../abstractUtxoCoin'; |
90 | 12 |
|
91 | | -/** |
92 | | - * Get the inputs for a transaction from a prebuild. |
93 | | - */ |
94 | | -export async function getTxInputs<TNumber extends number | bigint>(params: { |
95 | | - txPrebuild: TransactionPrebuild<TNumber>; |
96 | | - bitgo: BitGoBase; |
97 | | - coin: AbstractUtxoCoin; |
98 | | - disableNetworking: boolean; |
99 | | - reqId?: IRequestTracer; |
100 | | -}): Promise<{ address: string; value: TNumber; valueString: string }[]> { |
101 | | - const { txPrebuild, bitgo, coin, disableNetworking, reqId } = params; |
102 | | - if (!txPrebuild.txHex) { |
103 | | - throw new Error(`txPrebuild.txHex not set`); |
104 | | - } |
105 | | - const transaction = coin.createTransactionFromHex<TNumber>(txPrebuild.txHex); |
106 | | - const transactionCache = {}; |
107 | | - return await Promise.all( |
108 | | - transaction.ins.map(async (currentInput): Promise<{ address: string; value: TNumber; valueString: string }> => { |
109 | | - const transactionId = (Buffer.from(currentInput.hash).reverse() as Buffer).toString('hex'); |
110 | | - const txHex = txPrebuild.txInfo?.txHexes?.[transactionId]; |
111 | | - if (txHex) { |
112 | | - const localTx = coin.createTransactionFromHex<TNumber>(txHex); |
113 | | - if (localTx.getId() !== transactionId) { |
114 | | - throw new Error('input transaction hex does not match id'); |
115 | | - } |
116 | | - const currentOutput = localTx.outs[currentInput.index]; |
117 | | - const address = utxolib.address.fromOutputScript(currentOutput.script, coin.network); |
118 | | - return { |
119 | | - address, |
120 | | - value: currentOutput.value, |
121 | | - valueString: currentOutput.value.toString(), |
122 | | - }; |
123 | | - } else if (!transactionCache[transactionId]) { |
124 | | - if (disableNetworking) { |
125 | | - throw new Error('attempting to retrieve transaction details externally with networking disabled'); |
126 | | - } |
127 | | - if (reqId) { |
128 | | - bitgo.setRequestTracer(reqId); |
129 | | - } |
130 | | - transactionCache[transactionId] = await bitgo.get(coin.url(`/public/tx/${transactionId}`)).result(); |
131 | | - } |
132 | | - const transactionDetails = transactionCache[transactionId]; |
133 | | - return transactionDetails.outputs[currentInput.index]; |
134 | | - }) |
135 | | - ); |
136 | | -} |
| 13 | +import { toExtendedAddressFormat } from './recipient'; |
137 | 14 |
|
138 | 15 | function explainCommon<TNumber extends number | bigint>( |
139 | 16 | tx: bitgo.UtxoTransaction<TNumber>, |
|
0 commit comments