@btc-vision/transaction 1.1.12 → 1.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/_version.d.ts +1 -1
- package/browser/buffer/BinaryReader.d.ts +2 -2
- package/browser/buffer/BinaryWriter.d.ts +2 -2
- package/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +2 -0
- package/browser/opnet.d.ts +26 -24
- package/browser/transaction/browser/extensions/XverseSigner.d.ts +36 -0
- package/browser/transaction/browser/types/Xverse.d.ts +79 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +1 -0
- package/browser/utils/lengths.d.ts +16 -0
- package/browser/utils/types.d.ts +0 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryReader.d.ts +2 -2
- package/build/buffer/BinaryReader.js +12 -12
- package/build/buffer/BinaryWriter.d.ts +2 -2
- package/build/buffer/BinaryWriter.js +12 -12
- package/build/deterministic/AddressMap.js +1 -1
- package/build/keypair/Address.js +10 -8
- package/build/opnet.d.ts +26 -24
- package/build/opnet.js +26 -24
- package/build/transaction/TransactionFactory.js +17 -5
- package/build/transaction/browser/extensions/XverseSigner.d.ts +36 -0
- package/build/transaction/browser/extensions/XverseSigner.js +299 -0
- package/build/transaction/browser/types/Xverse.d.ts +79 -0
- package/build/transaction/browser/types/Xverse.js +6 -0
- package/build/transaction/builders/CustomScriptTransaction.js +1 -1
- package/build/transaction/builders/DeploymentTransaction.js +1 -1
- package/build/transaction/builders/FundingTransaction.js +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.js +2 -1
- package/build/transaction/builders/TransactionBuilder.d.ts +1 -0
- package/build/transaction/builders/TransactionBuilder.js +10 -1
- package/build/utils/BufferHelper.js +2 -1
- package/build/utils/lengths.d.ts +16 -0
- package/build/utils/lengths.js +15 -0
- package/build/utils/types.d.ts +0 -1
- package/build/utils/types.js +1 -1
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryReader.ts +21 -12
- package/src/buffer/BinaryWriter.ts +23 -15
- package/src/deterministic/AddressMap.ts +1 -1
- package/src/keypair/Address.ts +11 -9
- package/src/opnet.ts +26 -24
- package/src/transaction/TransactionFactory.ts +18 -6
- package/src/transaction/browser/extensions/XverseSigner.ts +429 -0
- package/src/transaction/browser/types/Xverse.ts +104 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +1 -1
- package/src/transaction/builders/DeploymentTransaction.ts +1 -1
- package/src/transaction/builders/FundingTransaction.ts +1 -1
- package/src/transaction/builders/SharedInteractionTransaction.ts +2 -1
- package/src/transaction/builders/TransactionBuilder.ts +12 -2
- package/src/utils/BufferHelper.ts +2 -1
- package/src/utils/lengths.ts +20 -0
- package/src/utils/types.ts +0 -2
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import {
|
|
2
|
+
crypto as bitCrypto,
|
|
3
|
+
script as bitScript,
|
|
4
|
+
Network,
|
|
5
|
+
networks,
|
|
6
|
+
opcodes,
|
|
7
|
+
Psbt,
|
|
8
|
+
PsbtInput,
|
|
9
|
+
TapScriptSig,
|
|
10
|
+
} from '@btc-vision/bitcoin';
|
|
11
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
12
|
+
import { PartialSig } from 'bip174/src/lib/interfaces.js';
|
|
13
|
+
import { ECPairInterface } from 'ecpair';
|
|
14
|
+
import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
|
|
15
|
+
import { CustomKeypair } from '../BrowserSignerBase.js';
|
|
16
|
+
import { PsbtSignatureOptions } from '../types/Unisat.js';
|
|
17
|
+
import { Xverse, XverseRPCGetAccountResponse, XverseRPCSignPsbtResponse } from '../types/Xverse.js';
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface Window {
|
|
21
|
+
BitcoinProvider?: Xverse;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class XverseSigner extends CustomKeypair {
|
|
26
|
+
private isInitialized: boolean = false;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
super();
|
|
30
|
+
|
|
31
|
+
if (!window) {
|
|
32
|
+
throw new Error('XverseSigner can only be used in a browser environment');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private _p2tr: string | undefined;
|
|
37
|
+
|
|
38
|
+
public get p2tr(): string {
|
|
39
|
+
if (!this._p2tr) {
|
|
40
|
+
throw new Error('P2TR address not set');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this._p2tr;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private _p2wpkh: string | undefined;
|
|
47
|
+
|
|
48
|
+
public get p2wpkh(): string {
|
|
49
|
+
if (!this._p2wpkh) {
|
|
50
|
+
throw new Error('P2PKH address not set');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this._p2wpkh;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private _addresses: string[] | undefined;
|
|
57
|
+
|
|
58
|
+
public get addresses(): string[] {
|
|
59
|
+
if (!this._addresses) {
|
|
60
|
+
throw new Error('Addresses not set');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this._addresses;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private _publicKey: Buffer | undefined;
|
|
67
|
+
|
|
68
|
+
public get publicKey(): Buffer {
|
|
69
|
+
if (!this._publicKey) {
|
|
70
|
+
throw new Error('Public key not set');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this._publicKey;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public _network: Network | undefined;
|
|
77
|
+
|
|
78
|
+
public get network(): Network {
|
|
79
|
+
if (!this._network) {
|
|
80
|
+
throw new Error('Network not set');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this._network;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public get BitcoinProvider(): Xverse {
|
|
87
|
+
const module = window.BitcoinProvider;
|
|
88
|
+
if (!module) {
|
|
89
|
+
throw new Error('Xverse Wallet extension not found');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return module;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public async init(): Promise<void> {
|
|
96
|
+
if (this.isInitialized) return;
|
|
97
|
+
|
|
98
|
+
const connectResult = (await this.BitcoinProvider.request(
|
|
99
|
+
'wallet_connect',
|
|
100
|
+
null,
|
|
101
|
+
)) as XverseRPCGetAccountResponse;
|
|
102
|
+
|
|
103
|
+
if ('error' in connectResult) throw new Error(connectResult.error.message);
|
|
104
|
+
|
|
105
|
+
const payementAddress = connectResult.result.addresses.find(
|
|
106
|
+
(address) => address.purpose === 'payment',
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (!payementAddress) {
|
|
110
|
+
throw new Error('Payment address not found');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const network = payementAddress.address.startsWith('tb')
|
|
114
|
+
? networks.testnet
|
|
115
|
+
: payementAddress.address.startsWith('bc')
|
|
116
|
+
? networks.bitcoin
|
|
117
|
+
: null;
|
|
118
|
+
|
|
119
|
+
if (!network) throw new Error('Network not supported');
|
|
120
|
+
|
|
121
|
+
this._network = network;
|
|
122
|
+
|
|
123
|
+
this._publicKey = Buffer.from(payementAddress.publicKey, 'hex');
|
|
124
|
+
|
|
125
|
+
this._p2wpkh = EcKeyPair.getP2WPKHAddress(this as unknown as ECPairInterface, this.network);
|
|
126
|
+
|
|
127
|
+
this._p2tr = EcKeyPair.getTaprootAddress(this as unknown as ECPairInterface, this.network);
|
|
128
|
+
|
|
129
|
+
this._addresses = [this._p2wpkh, this._p2tr];
|
|
130
|
+
|
|
131
|
+
this.isInitialized = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public getPublicKey(): Buffer {
|
|
135
|
+
if (!this.isInitialized) {
|
|
136
|
+
throw new Error('UnisatSigner not initialized');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return this.publicKey;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public sign(_hash: Buffer, _lowR?: boolean): Buffer {
|
|
143
|
+
throw new Error('Not implemented: sign');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public signSchnorr(_hash: Buffer): Buffer {
|
|
147
|
+
throw new Error('Not implemented: signSchnorr');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public verify(_hash: Buffer, _signature: Buffer): boolean {
|
|
151
|
+
throw new Error('Not implemented: verify');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public async signTaprootInput(
|
|
155
|
+
transaction: Psbt,
|
|
156
|
+
i: number,
|
|
157
|
+
sighashTypes: number[],
|
|
158
|
+
): Promise<void> {
|
|
159
|
+
const input = transaction.data.inputs[i];
|
|
160
|
+
if (
|
|
161
|
+
input.tapKeySig ||
|
|
162
|
+
input.finalScriptSig ||
|
|
163
|
+
(Array.isArray(input.partialSig) &&
|
|
164
|
+
input.partialSig.length &&
|
|
165
|
+
this.hasAlreadyPartialSig(input.partialSig)) ||
|
|
166
|
+
(Array.isArray(input.tapScriptSig) &&
|
|
167
|
+
input.tapScriptSig.length &&
|
|
168
|
+
this.hasAlreadySignedTapScriptSig(input.tapScriptSig))
|
|
169
|
+
) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const firstSignature = await this.signAllTweaked(transaction, sighashTypes, false);
|
|
174
|
+
this.combine(transaction, firstSignature, i);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public async signInput(transaction: Psbt, i: number, sighashTypes: number[]): Promise<void> {
|
|
178
|
+
const input = transaction.data.inputs[i];
|
|
179
|
+
if (
|
|
180
|
+
input.tapKeySig ||
|
|
181
|
+
input.finalScriptSig ||
|
|
182
|
+
(Array.isArray(input.partialSig) &&
|
|
183
|
+
input.partialSig.length &&
|
|
184
|
+
this.hasAlreadyPartialSig(input.partialSig)) ||
|
|
185
|
+
(Array.isArray(input.tapScriptSig) &&
|
|
186
|
+
input.tapScriptSig.length &&
|
|
187
|
+
this.hasAlreadySignedTapScriptSig(input.tapScriptSig))
|
|
188
|
+
) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const firstSignature = await this.signAllTweaked(transaction, sighashTypes, true);
|
|
193
|
+
this.combine(transaction, firstSignature, i);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async multiSignPsbt(transactions: Psbt[]): Promise<void> {
|
|
197
|
+
const toSignPsbts: string[] = [];
|
|
198
|
+
const options: PsbtSignatureOptions[] = [];
|
|
199
|
+
|
|
200
|
+
for (const psbt of transactions) {
|
|
201
|
+
const hex = psbt.toHex();
|
|
202
|
+
toSignPsbts.push(hex);
|
|
203
|
+
|
|
204
|
+
const toSignInputs = psbt.data.inputs
|
|
205
|
+
.map((input, i) => {
|
|
206
|
+
let needsToSign = false;
|
|
207
|
+
let viaTaproot = false;
|
|
208
|
+
|
|
209
|
+
if (isTaprootInput(input)) {
|
|
210
|
+
if (input.tapLeafScript && input.tapLeafScript.length > 0) {
|
|
211
|
+
for (const tapLeafScript of input.tapLeafScript) {
|
|
212
|
+
if (pubkeyInScript(this.publicKey, tapLeafScript.script)) {
|
|
213
|
+
needsToSign = true;
|
|
214
|
+
viaTaproot = false; // for opnet, we use original keys.
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!needsToSign && input.tapInternalKey) {
|
|
221
|
+
const tapInternalKey = input.tapInternalKey;
|
|
222
|
+
const xOnlyPubKey = toXOnly(this.publicKey);
|
|
223
|
+
|
|
224
|
+
if (tapInternalKey.equals(xOnlyPubKey)) {
|
|
225
|
+
needsToSign = true;
|
|
226
|
+
viaTaproot = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
// Non-Taproot input
|
|
231
|
+
const script = getInputRelevantScript(input);
|
|
232
|
+
|
|
233
|
+
if (script && pubkeyInScript(this.publicKey, script)) {
|
|
234
|
+
needsToSign = true;
|
|
235
|
+
viaTaproot = false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (needsToSign) {
|
|
240
|
+
return {
|
|
241
|
+
index: i,
|
|
242
|
+
publicKey: this.publicKey.toString('hex'),
|
|
243
|
+
disableTweakSigner: !viaTaproot,
|
|
244
|
+
};
|
|
245
|
+
} else {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
.filter((v) => v !== null);
|
|
250
|
+
|
|
251
|
+
options.push({
|
|
252
|
+
autoFinalized: false,
|
|
253
|
+
toSignInputs: toSignInputs,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// const signed = await this.unisat.signPsbt(toSignPsbts[0], options[0]);
|
|
258
|
+
|
|
259
|
+
const callSign = (await this.BitcoinProvider.request('signPsbt', {
|
|
260
|
+
psbt: toSignPsbts[0],
|
|
261
|
+
signInputs: options[0].toSignInputs,
|
|
262
|
+
})) as XverseRPCSignPsbtResponse;
|
|
263
|
+
|
|
264
|
+
if ('error' in callSign) throw new Error(callSign.error.message);
|
|
265
|
+
|
|
266
|
+
const signedPsbts = Psbt.fromBase64(callSign.result.psbt); //signed.map((hex) => Psbt.fromHex(hex));
|
|
267
|
+
|
|
268
|
+
/*for (let i = 0; i < signedPsbts.length; i++) {
|
|
269
|
+
const psbtOriginal = transactions[i];
|
|
270
|
+
const psbtSigned = signedPsbts[i];
|
|
271
|
+
|
|
272
|
+
psbtOriginal.combine(psbtSigned);
|
|
273
|
+
}*/
|
|
274
|
+
|
|
275
|
+
transactions[0].combine(signedPsbts);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private hasAlreadySignedTapScriptSig(input: TapScriptSig[]): boolean {
|
|
279
|
+
for (let i = 0; i < input.length; i++) {
|
|
280
|
+
const item = input[i];
|
|
281
|
+
const buf = Buffer.from(item.pubkey);
|
|
282
|
+
if (buf.equals(this.publicKey) && item.signature) {
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private hasAlreadyPartialSig(input: PartialSig[]): boolean {
|
|
291
|
+
for (let i = 0; i < input.length; i++) {
|
|
292
|
+
const item = input[i];
|
|
293
|
+
const buf = Buffer.from(item.pubkey);
|
|
294
|
+
if (buf.equals(this.publicKey) && item.signature) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private combine(transaction: Psbt, newPsbt: Psbt, i: number): void {
|
|
303
|
+
const signedInput = newPsbt.data.inputs[i];
|
|
304
|
+
const originalInput = transaction.data.inputs[i];
|
|
305
|
+
|
|
306
|
+
if (signedInput.partialSig) {
|
|
307
|
+
transaction.updateInput(i, { partialSig: signedInput.partialSig });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (signedInput.tapKeySig && !originalInput.tapKeySig) {
|
|
311
|
+
transaction.updateInput(i, { tapKeySig: signedInput.tapKeySig });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (signedInput.tapScriptSig?.length) {
|
|
315
|
+
const lastScriptSig = originalInput.tapScriptSig;
|
|
316
|
+
if (lastScriptSig) {
|
|
317
|
+
const getNonDuplicate = this.getNonDuplicateScriptSig(
|
|
318
|
+
lastScriptSig,
|
|
319
|
+
signedInput.tapScriptSig,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
if (getNonDuplicate.length) {
|
|
323
|
+
transaction.updateInput(i, { tapScriptSig: getNonDuplicate });
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
transaction.updateInput(i, { tapScriptSig: signedInput.tapScriptSig });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private async signAllTweaked(
|
|
332
|
+
transaction: Psbt,
|
|
333
|
+
sighashTypes: number[],
|
|
334
|
+
disableTweakSigner: boolean = false,
|
|
335
|
+
): Promise<Psbt> {
|
|
336
|
+
const pubKey = this.publicKey.toString('hex');
|
|
337
|
+
const toSign = transaction.data.inputs.map((_, i) => {
|
|
338
|
+
return [
|
|
339
|
+
{
|
|
340
|
+
index: i,
|
|
341
|
+
publicKey: pubKey,
|
|
342
|
+
sighashTypes,
|
|
343
|
+
disableTweakSigner: disableTweakSigner,
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const opts: PsbtSignatureOptions = {
|
|
349
|
+
autoFinalized: false,
|
|
350
|
+
toSignInputs: toSign.flat(),
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const psbt = transaction.toBase64();
|
|
354
|
+
const callSign = (await this.BitcoinProvider.request('signPsbt', {
|
|
355
|
+
psbt,
|
|
356
|
+
signInputs: opts.toSignInputs,
|
|
357
|
+
})) as XverseRPCSignPsbtResponse;
|
|
358
|
+
|
|
359
|
+
if ('error' in callSign) throw new Error(callSign.error.message);
|
|
360
|
+
|
|
361
|
+
return Psbt.fromBase64(callSign.result.psbt);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private getNonDuplicateScriptSig(
|
|
365
|
+
scriptSig1: TapScriptSig[],
|
|
366
|
+
scriptSig2: TapScriptSig[],
|
|
367
|
+
): TapScriptSig[] {
|
|
368
|
+
const nonDuplicate: TapScriptSig[] = [];
|
|
369
|
+
for (let i = 0; i < scriptSig2.length; i++) {
|
|
370
|
+
const found = scriptSig1.find((item) => item.pubkey.equals(scriptSig2[i].pubkey));
|
|
371
|
+
if (!found) {
|
|
372
|
+
nonDuplicate.push(scriptSig2[i]);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return nonDuplicate;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Helper functions
|
|
381
|
+
function isTaprootInput(input: PsbtInput): boolean {
|
|
382
|
+
if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (input.witnessUtxo) {
|
|
387
|
+
const script = input.witnessUtxo.script;
|
|
388
|
+
return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function getInputRelevantScript(input: PsbtInput): Buffer | null {
|
|
395
|
+
if (input.redeemScript) {
|
|
396
|
+
return input.redeemScript;
|
|
397
|
+
}
|
|
398
|
+
if (input.witnessScript) {
|
|
399
|
+
return input.witnessScript;
|
|
400
|
+
}
|
|
401
|
+
if (input.witnessUtxo) {
|
|
402
|
+
return input.witnessUtxo.script;
|
|
403
|
+
}
|
|
404
|
+
if (input.nonWitnessUtxo) {
|
|
405
|
+
// Additional logic can be added here if needed
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
|
|
412
|
+
return pubkeyPositionInScript(pubkey, script) !== -1;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
|
|
416
|
+
const pubkeyHash = bitCrypto.hash160(pubkey);
|
|
417
|
+
const pubkeyXOnly = toXOnly(pubkey);
|
|
418
|
+
|
|
419
|
+
const decompiled = bitScript.decompile(script);
|
|
420
|
+
if (decompiled === null) throw new Error('Unknown script error');
|
|
421
|
+
|
|
422
|
+
return decompiled.findIndex((element) => {
|
|
423
|
+
if (typeof element === 'number') return false;
|
|
424
|
+
return (
|
|
425
|
+
Buffer.isBuffer(element) &&
|
|
426
|
+
(element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly))
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export enum XverseNetwork {
|
|
2
|
+
mainnet = 'mainnet',
|
|
3
|
+
testnet = 'testnet',
|
|
4
|
+
signet = 'Signet',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type XverseRPCResponse<T = unknown> = {
|
|
8
|
+
id: string;
|
|
9
|
+
jsonrpc: string;
|
|
10
|
+
result: T;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type XverseRPCError = {
|
|
14
|
+
id: string;
|
|
15
|
+
jsonrpc: string;
|
|
16
|
+
error: {
|
|
17
|
+
code: number;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type XverseRPCGetAccountResponse =
|
|
23
|
+
| XverseRPCResponse<{
|
|
24
|
+
addresses: {
|
|
25
|
+
address: string;
|
|
26
|
+
addressType: string;
|
|
27
|
+
publicKey: string;
|
|
28
|
+
purpose: string; // ordinals, payment or stacks (we only care about payment)
|
|
29
|
+
}[];
|
|
30
|
+
}>
|
|
31
|
+
| XverseRPCError;
|
|
32
|
+
|
|
33
|
+
export type XverseRPCSignPsbtResponse =
|
|
34
|
+
| XverseRPCResponse<{
|
|
35
|
+
psbt: string;
|
|
36
|
+
}>
|
|
37
|
+
| XverseRPCError;
|
|
38
|
+
|
|
39
|
+
interface InscriptionData {
|
|
40
|
+
address: string;
|
|
41
|
+
amount: number;
|
|
42
|
+
asset: string;
|
|
43
|
+
fee: number;
|
|
44
|
+
nonce: number;
|
|
45
|
+
recipient: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface InscriptionResult {
|
|
49
|
+
txid: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface RepeatInscriptionsData {
|
|
53
|
+
inscriptions: InscriptionData[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface TransactionResult {
|
|
57
|
+
txid: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface SignedMessageResult {
|
|
61
|
+
signature: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface BtcTransaction {
|
|
65
|
+
inputs: {
|
|
66
|
+
address: string;
|
|
67
|
+
amount: number;
|
|
68
|
+
asset: string;
|
|
69
|
+
nonce: number;
|
|
70
|
+
}[];
|
|
71
|
+
outputs: {
|
|
72
|
+
address: string;
|
|
73
|
+
amount: number;
|
|
74
|
+
asset: string;
|
|
75
|
+
}[];
|
|
76
|
+
fee: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface SignedTransactionResult {
|
|
80
|
+
txid: string;
|
|
81
|
+
raw: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface Xverse {
|
|
85
|
+
connect: () => Promise<void>;
|
|
86
|
+
|
|
87
|
+
addListener: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
88
|
+
|
|
89
|
+
createInscription: (data: InscriptionData) => Promise<InscriptionResult>;
|
|
90
|
+
|
|
91
|
+
createRepeatInscriptions: (data: RepeatInscriptionsData) => Promise<InscriptionResult[]>;
|
|
92
|
+
|
|
93
|
+
request: (method: string, params: unknown) => Promise<XverseRPCResponse>;
|
|
94
|
+
|
|
95
|
+
sendBtcTransaction: (transaction: BtcTransaction) => Promise<TransactionResult>;
|
|
96
|
+
|
|
97
|
+
signMessage: (message: string) => Promise<SignedMessageResult>;
|
|
98
|
+
|
|
99
|
+
signMultipleTransactions: (
|
|
100
|
+
transactions: BtcTransaction[],
|
|
101
|
+
) => Promise<SignedTransactionResult[]>;
|
|
102
|
+
|
|
103
|
+
signTransaction: (transaction: BtcTransaction) => Promise<SignedTransactionResult>;
|
|
104
|
+
}
|
|
@@ -177,7 +177,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
177
177
|
address: this.to,
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
await this.addRefundOutput(amountSpent);
|
|
180
|
+
await this.addRefundOutput(amountSpent + this.addOptionalOutputsAndGetAmount());
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/**
|
|
@@ -201,7 +201,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
201
201
|
address: this.contractAddress.p2tr(this.network),
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
-
await this.addRefundOutput(amountSpent);
|
|
204
|
+
await this.addRefundOutput(amountSpent + this.addOptionalOutputsAndGetAmount());
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
protected override async signInputsWalletBased(transaction: Psbt): Promise<void> {
|
|
@@ -40,7 +40,7 @@ export class FundingTransaction extends TransactionBuilder<TransactionType.FUNDI
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
await this.addRefundOutput(this.amount);
|
|
43
|
+
await this.addRefundOutput(this.amount + this.addOptionalOutputsAndGetAmount());
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
protected splitInputs(amountSpent: bigint): void {
|
|
@@ -170,8 +170,9 @@ export abstract class SharedInteractionTransaction<
|
|
|
170
170
|
address: this.to,
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
+
const amount = this.addOptionalOutputsAndGetAmount();
|
|
173
174
|
if (!this.disableAutoRefund) {
|
|
174
|
-
await this.addRefundOutput(amountSpent);
|
|
175
|
+
await this.addRefundOutput(amountSpent + amount);
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -444,6 +444,17 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
444
444
|
return outputs;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
public getOptionalOutputValue(): bigint {
|
|
448
|
+
if (!this.optionalOutputs) return 0n;
|
|
449
|
+
|
|
450
|
+
let total = 0n;
|
|
451
|
+
for (let i = 0; i < this.optionalOutputs.length; i++) {
|
|
452
|
+
total += BigInt(this.optionalOutputs[i].value);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return total;
|
|
456
|
+
}
|
|
457
|
+
|
|
447
458
|
/**
|
|
448
459
|
* @description Adds the refund output to the transaction
|
|
449
460
|
* @param {bigint} amountSpent - The amount spent
|
|
@@ -452,8 +463,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
452
463
|
*/
|
|
453
464
|
protected async addRefundOutput(amountSpent: bigint): Promise<void> {
|
|
454
465
|
/** Add the refund output */
|
|
455
|
-
const sendBackAmount: bigint =
|
|
456
|
-
this.totalInputAmount - amountSpent - this.addOptionalOutputsAndGetAmount();
|
|
466
|
+
const sendBackAmount: bigint = this.totalInputAmount - amountSpent;
|
|
457
467
|
if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
|
|
458
468
|
if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
|
|
459
469
|
await this.setFeeOutput({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { U256_BYTE_LENGTH } from './lengths.js';
|
|
1
2
|
import { MemorySlotPointer } from './types.js';
|
|
2
3
|
|
|
3
4
|
export class BufferHelper {
|
|
@@ -54,7 +55,7 @@ export class BufferHelper {
|
|
|
54
55
|
return BigInt('0x' + hex);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
public static valueToUint8Array(value: bigint, length: number =
|
|
58
|
+
public static valueToUint8Array(value: bigint, length: number = U256_BYTE_LENGTH): Uint8Array {
|
|
58
59
|
const valueHex = value.toString(16).padStart(length * 2, '0');
|
|
59
60
|
|
|
60
61
|
return BufferHelper.hexToUint8Array(valueHex);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { i32 } from './types';
|
|
2
|
+
|
|
3
|
+
export const ADDRESS_BYTE_LENGTH: i32 = 32;
|
|
4
|
+
export const SELECTOR_BYTE_LENGTH: i32 = 4;
|
|
5
|
+
|
|
6
|
+
export const U256_BYTE_LENGTH: i32 = 32;
|
|
7
|
+
export const U128_BYTE_LENGTH: i32 = 16;
|
|
8
|
+
export const U64_BYTE_LENGTH: i32 = 8;
|
|
9
|
+
export const U32_BYTE_LENGTH: i32 = 4;
|
|
10
|
+
export const U16_BYTE_LENGTH: i32 = 2;
|
|
11
|
+
export const U8_BYTE_LENGTH: i32 = 1;
|
|
12
|
+
|
|
13
|
+
export const I256_BYTE_LENGTH: i32 = 32;
|
|
14
|
+
export const I128_BYTE_LENGTH: i32 = 16;
|
|
15
|
+
export const I64_BYTE_LENGTH: i32 = 8;
|
|
16
|
+
export const I32_BYTE_LENGTH: i32 = 4;
|
|
17
|
+
export const I16_BYTE_LENGTH: i32 = 2;
|
|
18
|
+
export const I8_BYTE_LENGTH: i32 = 1;
|
|
19
|
+
|
|
20
|
+
export const BOOLEAN_BYTE_LENGTH: i32 = 1;
|