@btc-vision/transaction 1.2.2 → 1.2.4
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/crypto/crypto-browser.d.ts +1 -1
- package/browser/generators/builders/CalldataGenerator.d.ts +1 -1
- package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
- package/browser/generators/builders/MineableReward.d.ts +7 -0
- package/browser/index.js +1 -1
- package/browser/opnet.d.ts +4 -0
- package/browser/transaction/TransactionFactory.d.ts +19 -4
- package/browser/transaction/browser/WalletConnection.d.ts +18 -0
- package/browser/transaction/browser/types/Xverse.d.ts +24 -10
- package/browser/transaction/builders/ChallengeSolutionTransaction.d.ts +18 -0
- package/browser/transaction/builders/DeploymentTransaction.d.ts +4 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -0
- package/browser/transaction/enums/TransactionType.d.ts +3 -4
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +6 -0
- package/browser/transaction/mineable/ChallengeGenerator.d.ts +9 -0
- package/browser/utxo/interfaces/IUTXO.d.ts +1 -1
- package/browser/verification/TapscriptVerificator.d.ts +1 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/Generator.js +1 -1
- package/build/generators/builders/CalldataGenerator.d.ts +1 -1
- package/build/generators/builders/CalldataGenerator.js +7 -26
- package/build/generators/builders/DeploymentGenerator.d.ts +1 -1
- package/build/generators/builders/DeploymentGenerator.js +9 -6
- package/build/generators/builders/LegacyCalldataGenerator.js +5 -1
- package/build/generators/builders/MineableReward.d.ts +7 -0
- package/build/generators/builders/MineableReward.js +48 -0
- package/build/opnet.d.ts +4 -0
- package/build/opnet.js +4 -0
- package/build/transaction/TransactionFactory.d.ts +19 -4
- package/build/transaction/TransactionFactory.js +32 -0
- package/build/transaction/browser/WalletConnection.d.ts +18 -0
- package/build/transaction/browser/WalletConnection.js +95 -0
- package/build/transaction/browser/extensions/UnisatSigner.js +5 -2
- package/build/transaction/browser/extensions/XverseSigner.js +15 -9
- package/build/transaction/browser/types/Xverse.d.ts +24 -10
- package/build/transaction/builders/ChallengeSolutionTransaction.d.ts +18 -0
- package/build/transaction/builders/ChallengeSolutionTransaction.js +51 -0
- package/build/transaction/builders/DeploymentTransaction.d.ts +4 -0
- package/build/transaction/builders/DeploymentTransaction.js +23 -4
- package/build/transaction/builders/InteractionTransaction.js +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +35 -13
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -0
- package/build/transaction/builders/TransactionBuilder.js +2 -0
- package/build/transaction/enums/TransactionType.d.ts +3 -4
- package/build/transaction/enums/TransactionType.js +3 -4
- package/build/transaction/interfaces/ITransactionParameters.d.ts +6 -0
- package/build/transaction/mineable/ChallengeGenerator.d.ts +9 -0
- package/build/transaction/mineable/ChallengeGenerator.js +28 -0
- package/build/transaction/shared/TweakedTransaction.js +1 -1
- package/build/utxo/interfaces/IUTXO.d.ts +1 -1
- package/build/verification/TapscriptVerificator.d.ts +1 -1
- package/build/verification/TapscriptVerificator.js +2 -8
- package/package.json +3 -3
- package/src/_version.ts +1 -1
- package/src/generators/Generator.ts +1 -1
- package/src/generators/builders/CalldataGenerator.ts +10 -41
- package/src/generators/builders/DeploymentGenerator.ts +18 -7
- package/src/generators/builders/LegacyCalldataGenerator.ts +5 -1
- package/src/generators/builders/MineableReward.ts +66 -0
- package/src/opnet.ts +5 -0
- package/src/transaction/TransactionFactory.ts +66 -3
- package/src/transaction/browser/WalletConnection.ts +110 -0
- package/src/transaction/browser/extensions/UnisatSigner.ts +7 -3
- package/src/transaction/browser/extensions/XverseSigner.ts +24 -23
- package/src/transaction/browser/types/Xverse.ts +50 -36
- package/src/transaction/builders/ChallengeSolutionTransaction.ts +88 -0
- package/src/transaction/builders/DeploymentTransaction.ts +46 -3
- package/src/transaction/builders/InteractionTransaction.ts +1 -0
- package/src/transaction/builders/SharedInteractionTransaction.ts +54 -14
- package/src/transaction/builders/TransactionBuilder.ts +3 -0
- package/src/transaction/enums/TransactionType.ts +3 -4
- package/src/transaction/interfaces/ITransactionParameters.ts +8 -15
- package/src/transaction/mineable/ChallengeGenerator.ts +39 -0
- package/src/transaction/shared/TweakedTransaction.ts +1 -1
- package/src/utxo/interfaces/IUTXO.ts +1 -1
- package/src/verification/TapscriptVerificator.ts +3 -18
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Address } from '../../opnet.js';
|
|
2
|
+
import { UnisatSigner } from './extensions/UnisatSigner.js';
|
|
3
|
+
import { XverseSigner } from './extensions/XverseSigner.js';
|
|
4
|
+
|
|
5
|
+
export enum SupportedWallets {
|
|
6
|
+
Unisat = 'unisat',
|
|
7
|
+
Xverse = 'xverse',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class WalletConnection {
|
|
11
|
+
public wallet_type: SupportedWallets | null = null;
|
|
12
|
+
|
|
13
|
+
private unisatSigner: UnisatSigner | null = null; // OP_WALLET and Unisat wallet
|
|
14
|
+
private xverseSigner: XverseSigner | null = null;
|
|
15
|
+
|
|
16
|
+
public async connect(): Promise<void> {
|
|
17
|
+
if (this.wallet_type) {
|
|
18
|
+
throw new Error('Wallet already connected');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.unisatSigner = new UnisatSigner();
|
|
22
|
+
this.xverseSigner = new XverseSigner();
|
|
23
|
+
|
|
24
|
+
if (window.opnet || window.unisat) {
|
|
25
|
+
try {
|
|
26
|
+
await this.unisatSigner.init();
|
|
27
|
+
this.wallet_type = SupportedWallets.Unisat;
|
|
28
|
+
return;
|
|
29
|
+
} catch (error: unknown) {
|
|
30
|
+
if (error instanceof Error) {
|
|
31
|
+
throw new Error(error.message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw new Error('Error connecting wallet');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (window.BitcoinProvider) {
|
|
39
|
+
try {
|
|
40
|
+
await this.xverseSigner.init();
|
|
41
|
+
this.wallet_type = SupportedWallets.Xverse;
|
|
42
|
+
return;
|
|
43
|
+
} catch (error: unknown) {
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
throw new Error(error.message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
throw new Error('Error connecting wallet');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error('Wallet not found');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async disconnect(): Promise<void> {
|
|
56
|
+
if (!this.unisatSigner || !this.xverseSigner) {
|
|
57
|
+
throw new Error('Wallet not connected');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.wallet_type === SupportedWallets.Unisat) {
|
|
61
|
+
this.unisatSigner.unisat.disconnect();
|
|
62
|
+
} else {
|
|
63
|
+
await this.xverseSigner.BitcoinProvider.request('wallet_disconnect', null);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.wallet_type = null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async switchTo(walletType: SupportedWallets): Promise<void> {
|
|
70
|
+
if (!this.unisatSigner || !this.xverseSigner) {
|
|
71
|
+
throw new Error('Wallet not connected');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.wallet_type === walletType) return;
|
|
75
|
+
|
|
76
|
+
if (walletType === SupportedWallets.Unisat) {
|
|
77
|
+
await this.unisatSigner.init();
|
|
78
|
+
this.wallet_type = SupportedWallets.Unisat;
|
|
79
|
+
} else {
|
|
80
|
+
await this.xverseSigner.init();
|
|
81
|
+
this.wallet_type = SupportedWallets.Xverse;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public getAddress(): Address {
|
|
86
|
+
if (!this.unisatSigner || !this.xverseSigner) {
|
|
87
|
+
throw new Error('Wallet not connected');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (this.wallet_type === SupportedWallets.Unisat) {
|
|
91
|
+
return Address.fromString(this.unisatSigner.getPublicKey().toString('hex'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Address.fromString(this.xverseSigner.getPublicKey().toString('hex'));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public getSigner(): UnisatSigner | XverseSigner {
|
|
98
|
+
if (!this.unisatSigner || !this.xverseSigner) {
|
|
99
|
+
throw new Error('Wallet not connected');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.wallet_type === SupportedWallets.Unisat) {
|
|
103
|
+
return this.unisatSigner;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return this.xverseSigner;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default WalletConnection;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
crypto as bitCrypto,
|
|
3
|
+
script as bitScript,
|
|
3
4
|
Network,
|
|
4
5
|
networks,
|
|
5
6
|
Psbt,
|
|
6
|
-
script as bitScript,
|
|
7
7
|
TapScriptSig,
|
|
8
8
|
toXOnly,
|
|
9
9
|
} from '@btc-vision/bitcoin';
|
|
10
|
+
import { PartialSig } from 'bip174/src/lib/interfaces.js';
|
|
10
11
|
import { ECPairInterface } from 'ecpair';
|
|
11
12
|
import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
|
|
13
|
+
import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUtils.js';
|
|
12
14
|
import { CustomKeypair } from '../BrowserSignerBase.js';
|
|
13
15
|
import { PsbtSignatureOptions, Unisat, UnisatNetwork } from '../types/Unisat.js';
|
|
14
|
-
import { PartialSig } from 'bip174/src/lib/interfaces.js';
|
|
15
|
-
import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUtils.js';
|
|
16
16
|
|
|
17
17
|
declare global {
|
|
18
18
|
interface Window {
|
|
@@ -112,6 +112,10 @@ export class UnisatSigner extends CustomKeypair {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const publicKey = await this.unisat.getPublicKey();
|
|
115
|
+
if (publicKey === '') {
|
|
116
|
+
throw new Error('Unlock your wallet first');
|
|
117
|
+
}
|
|
118
|
+
|
|
115
119
|
this._publicKey = Buffer.from(publicKey, 'hex');
|
|
116
120
|
|
|
117
121
|
this._p2wpkh = EcKeyPair.getP2WPKHAddress(this as unknown as ECPairInterface, this.network);
|
|
@@ -2,14 +2,14 @@ import { Network, networks, Psbt, TapScriptSig, toXOnly } from '@btc-vision/bitc
|
|
|
2
2
|
import { PartialSig } from 'bip174/src/lib/interfaces.js';
|
|
3
3
|
import { ECPairInterface } from 'ecpair';
|
|
4
4
|
import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
|
|
5
|
-
import { CustomKeypair } from '../BrowserSignerBase.js';
|
|
6
|
-
import { PsbtSignatureOptions } from '../types/Unisat.js';
|
|
7
|
-
import { Xverse, XverseRPCGetAccountResponse, XverseRPCSignPsbtResponse } from '../types/Xverse.js';
|
|
8
5
|
import {
|
|
9
6
|
canSignNonTaprootInput,
|
|
10
7
|
isTaprootInput,
|
|
11
8
|
pubkeyInScript,
|
|
12
9
|
} from '../../../signer/SignerUtils.js';
|
|
10
|
+
import { CustomKeypair } from '../BrowserSignerBase.js';
|
|
11
|
+
import { PsbtSignatureOptions } from '../types/Unisat.js';
|
|
12
|
+
import { Xverse } from '../types/Xverse.js';
|
|
13
13
|
|
|
14
14
|
declare global {
|
|
15
15
|
interface Window {
|
|
@@ -90,10 +90,7 @@ export class XverseSigner extends CustomKeypair {
|
|
|
90
90
|
public async init(): Promise<void> {
|
|
91
91
|
if (this.isInitialized) return;
|
|
92
92
|
|
|
93
|
-
const connectResult =
|
|
94
|
-
'wallet_connect',
|
|
95
|
-
null,
|
|
96
|
-
)) as XverseRPCGetAccountResponse;
|
|
93
|
+
const connectResult = await this.BitcoinProvider.request('wallet_connect', null);
|
|
97
94
|
|
|
98
95
|
if ('error' in connectResult) throw new Error(connectResult.error.message);
|
|
99
96
|
|
|
@@ -193,7 +190,7 @@ export class XverseSigner extends CustomKeypair {
|
|
|
193
190
|
const options: PsbtSignatureOptions[] = [];
|
|
194
191
|
|
|
195
192
|
for (const psbt of transactions) {
|
|
196
|
-
const hex = psbt.
|
|
193
|
+
const hex = psbt.toBase64();
|
|
197
194
|
toSignPsbts.push(hex);
|
|
198
195
|
|
|
199
196
|
const toSignInputs = psbt.data.inputs
|
|
@@ -245,23 +242,20 @@ export class XverseSigner extends CustomKeypair {
|
|
|
245
242
|
});
|
|
246
243
|
}
|
|
247
244
|
|
|
248
|
-
|
|
245
|
+
const toSignInputs: {
|
|
246
|
+
[x: string]: number[];
|
|
247
|
+
} = {
|
|
248
|
+
[this.p2wpkh]: options[0].toSignInputs?.map((input) => input.index) || [],
|
|
249
|
+
};
|
|
249
250
|
|
|
250
|
-
const callSign =
|
|
251
|
+
const callSign = await this.BitcoinProvider.request('signPsbt', {
|
|
251
252
|
psbt: toSignPsbts[0],
|
|
252
|
-
signInputs:
|
|
253
|
-
})
|
|
253
|
+
signInputs: toSignInputs,
|
|
254
|
+
});
|
|
254
255
|
|
|
255
256
|
if ('error' in callSign) throw new Error(callSign.error.message);
|
|
256
257
|
|
|
257
|
-
const signedPsbts = Psbt.fromBase64(callSign.result.psbt);
|
|
258
|
-
|
|
259
|
-
/*for (let i = 0; i < signedPsbts.length; i++) {
|
|
260
|
-
const psbtOriginal = transactions[i];
|
|
261
|
-
const psbtSigned = signedPsbts[i];
|
|
262
|
-
|
|
263
|
-
psbtOriginal.combine(psbtSigned);
|
|
264
|
-
}*/
|
|
258
|
+
const signedPsbts = Psbt.fromBase64(callSign.result.psbt);
|
|
265
259
|
|
|
266
260
|
transactions[0].combine(signedPsbts);
|
|
267
261
|
}
|
|
@@ -342,10 +336,17 @@ export class XverseSigner extends CustomKeypair {
|
|
|
342
336
|
};
|
|
343
337
|
|
|
344
338
|
const psbt = transaction.toBase64();
|
|
345
|
-
|
|
339
|
+
|
|
340
|
+
const toSignInputs: {
|
|
341
|
+
[x: string]: number[];
|
|
342
|
+
} = {
|
|
343
|
+
[this.p2wpkh]: opts.toSignInputs?.map((input) => input.index) || [],
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const callSign = await this.BitcoinProvider.request('signPsbt', {
|
|
346
347
|
psbt,
|
|
347
|
-
signInputs:
|
|
348
|
-
})
|
|
348
|
+
signInputs: toSignInputs,
|
|
349
|
+
});
|
|
349
350
|
|
|
350
351
|
if ('error' in callSign) throw new Error(callSign.error.message);
|
|
351
352
|
|
|
@@ -4,38 +4,50 @@ export enum XverseNetwork {
|
|
|
4
4
|
signet = 'Signet',
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
type XverseRPCResponse<T = unknown> =
|
|
8
|
+
| {
|
|
9
|
+
id: string;
|
|
10
|
+
jsonrpc: string;
|
|
11
|
+
result: T;
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
id: string;
|
|
15
|
+
jsonrpc: string;
|
|
16
|
+
error: {
|
|
17
|
+
code: number;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type XverseRPCGetAccountResponse = XverseRPCResponse<{
|
|
23
|
+
addresses: {
|
|
24
|
+
address: string;
|
|
25
|
+
addressType: string;
|
|
26
|
+
publicKey: string;
|
|
27
|
+
purpose: 'stacks' | 'payment' | 'ordinals'; // we only care about payment
|
|
28
|
+
}[];
|
|
29
|
+
walletType: string;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
type XverseRPCSignPsbtResponse = XverseRPCResponse<{
|
|
33
|
+
psbt: string;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
type XverseRPCGetBalanceResponse = XverseRPCResponse<{
|
|
37
|
+
confirmed: string;
|
|
38
|
+
total: string;
|
|
39
|
+
unconfirmed: string;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export type XVersePSBTInput = {
|
|
43
|
+
psbt: string;
|
|
44
|
+
signInputs:
|
|
45
|
+
| {
|
|
46
|
+
[x: string]: number[];
|
|
47
|
+
}
|
|
48
|
+
| undefined;
|
|
20
49
|
};
|
|
21
50
|
|
|
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
51
|
interface InscriptionData {
|
|
40
52
|
address: string;
|
|
41
53
|
amount: number;
|
|
@@ -82,23 +94,25 @@ interface SignedTransactionResult {
|
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
export interface Xverse {
|
|
85
|
-
|
|
97
|
+
request(method: string, params: unknown): Promise<XverseRPCResponse>;
|
|
98
|
+
request(
|
|
99
|
+
method: 'wallet_connect' | 'wallet_getAccount',
|
|
100
|
+
params: null,
|
|
101
|
+
): Promise<XverseRPCGetAccountResponse>;
|
|
102
|
+
request(method: 'wallet_disconnect', params: null): Promise<XverseRPCResponse<null>>;
|
|
103
|
+
request(method: 'getBalance', params: null): Promise<XverseRPCGetBalanceResponse>;
|
|
104
|
+
request(method: 'signPsbt', params: XVersePSBTInput): Promise<XverseRPCSignPsbtResponse>;
|
|
86
105
|
|
|
87
106
|
addListener: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
88
107
|
|
|
89
108
|
createInscription: (data: InscriptionData) => Promise<InscriptionResult>;
|
|
90
|
-
|
|
91
109
|
createRepeatInscriptions: (data: RepeatInscriptionsData) => Promise<InscriptionResult[]>;
|
|
92
110
|
|
|
93
|
-
request: (method: string, params: unknown) => Promise<XverseRPCResponse>;
|
|
94
|
-
|
|
95
111
|
sendBtcTransaction: (transaction: BtcTransaction) => Promise<TransactionResult>;
|
|
96
112
|
|
|
97
113
|
signMessage: (message: string) => Promise<SignedMessageResult>;
|
|
98
|
-
|
|
99
114
|
signMultipleTransactions: (
|
|
100
115
|
transactions: BtcTransaction[],
|
|
101
116
|
) => Promise<SignedTransactionResult[]>;
|
|
102
|
-
|
|
103
117
|
signTransaction: (transaction: BtcTransaction) => Promise<SignedTransactionResult>;
|
|
104
118
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
2
|
+
import { IChallengeSolutionTransactionParameters } from '../interfaces/ITransactionParameters.js';
|
|
3
|
+
import { getFinalScripts, opcodes, Psbt, PsbtInput, script, Signer } from '@btc-vision/bitcoin';
|
|
4
|
+
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
5
|
+
import { ECPairInterface } from 'ecpair';
|
|
6
|
+
|
|
7
|
+
export class ChallengeSolutionTransaction extends TransactionBuilder<TransactionType.CHALLENGE_SOLUTION> {
|
|
8
|
+
public readonly type: TransactionType.CHALLENGE_SOLUTION = TransactionType.CHALLENGE_SOLUTION;
|
|
9
|
+
|
|
10
|
+
protected amount: bigint;
|
|
11
|
+
protected readonly challengeSolution: Buffer;
|
|
12
|
+
|
|
13
|
+
constructor(parameters: IChallengeSolutionTransactionParameters) {
|
|
14
|
+
super(parameters);
|
|
15
|
+
|
|
16
|
+
this.amount = parameters.amount;
|
|
17
|
+
this.challengeSolution = parameters.challengeSolution;
|
|
18
|
+
|
|
19
|
+
this.internalInit();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
protected override async buildTransaction(): Promise<void> {
|
|
23
|
+
if (!this.to) {
|
|
24
|
+
throw new Error('Recipient address is required');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.addInputsFromUTXO();
|
|
28
|
+
|
|
29
|
+
if (this.isPubKeyDestination) {
|
|
30
|
+
const pubKeyScript = script.compile([
|
|
31
|
+
Buffer.from(this.to.replace('0x', ''), 'hex'),
|
|
32
|
+
opcodes.OP_CHECKSIG,
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
this.addOutput({
|
|
36
|
+
value: Number(this.amount),
|
|
37
|
+
script: pubKeyScript,
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
this.addOutput({
|
|
41
|
+
value: Number(this.amount),
|
|
42
|
+
address: this.to,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await this.addRefundOutput(this.amount + this.addOptionalOutputsAndGetAmount());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected override async signInput(
|
|
50
|
+
transaction: Psbt,
|
|
51
|
+
input: PsbtInput,
|
|
52
|
+
i: number,
|
|
53
|
+
signer: Signer | ECPairInterface,
|
|
54
|
+
reverse: boolean = false,
|
|
55
|
+
errored: boolean = false,
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
// do nothing.
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected override customFinalizerP2SH = (
|
|
61
|
+
inputIndex: number,
|
|
62
|
+
input: PsbtInput,
|
|
63
|
+
scriptA: Buffer,
|
|
64
|
+
isSegwit: boolean,
|
|
65
|
+
isP2SH: boolean,
|
|
66
|
+
isP2WSH: boolean,
|
|
67
|
+
): {
|
|
68
|
+
finalScriptSig: Buffer | undefined;
|
|
69
|
+
finalScriptWitness: Buffer | undefined;
|
|
70
|
+
} => {
|
|
71
|
+
const inputDecoded = this.inputs[inputIndex];
|
|
72
|
+
|
|
73
|
+
if (isP2SH && inputDecoded && inputDecoded.redeemScript) {
|
|
74
|
+
const scriptSig = script.compile([this.challengeSolution, inputDecoded.redeemScript]);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
finalScriptSig: scriptSig,
|
|
78
|
+
finalScriptWitness: undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return getFinalScripts(inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH, false);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
protected override getSignerKey(): Signer | ECPairInterface {
|
|
86
|
+
return this.signer;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
Taptree,
|
|
10
10
|
toXOnly,
|
|
11
11
|
} from '@btc-vision/bitcoin';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
MINIMUM_AMOUNT_CA,
|
|
14
|
+
MINIMUM_AMOUNT_REWARD,
|
|
15
|
+
TransactionBuilder,
|
|
16
|
+
} from './TransactionBuilder.js';
|
|
13
17
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
14
18
|
import { DeploymentGenerator } from '../../generators/builders/DeploymentGenerator.js';
|
|
15
19
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
@@ -19,12 +23,17 @@ import { SharedInteractionTransaction } from './SharedInteractionTransaction.js'
|
|
|
19
23
|
import { ECPairInterface } from 'ecpair';
|
|
20
24
|
import { Address } from '../../keypair/Address.js';
|
|
21
25
|
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
26
|
+
import { ChallengeGenerator, IMineableReward } from '../mineable/ChallengeGenerator.js';
|
|
22
27
|
|
|
23
28
|
const p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
24
29
|
|
|
25
30
|
export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
|
|
26
31
|
public static readonly MAXIMUM_CONTRACT_SIZE = 128 * 1024;
|
|
27
32
|
public type: TransactionType.DEPLOYMENT = TransactionType.DEPLOYMENT;
|
|
33
|
+
|
|
34
|
+
protected readonly preimage: Buffer; // ALWAYS 128 bytes for the preimage
|
|
35
|
+
protected readonly rewardChallenge: IMineableReward;
|
|
36
|
+
|
|
28
37
|
/**
|
|
29
38
|
* The contract address
|
|
30
39
|
* @protected
|
|
@@ -110,6 +119,12 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
122
|
+
this.preimage = parameters.preimage || BitcoinUtils.getSafeRandomValues(128);
|
|
123
|
+
|
|
124
|
+
this.rewardChallenge = ChallengeGenerator.generateMineableReward(
|
|
125
|
+
this.preimage,
|
|
126
|
+
this.network,
|
|
127
|
+
);
|
|
113
128
|
|
|
114
129
|
this.contractSeed = this.getContractSeed();
|
|
115
130
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
|
|
@@ -123,6 +138,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
123
138
|
this.compiledTargetScript = this.deploymentGenerator.compile(
|
|
124
139
|
this.bytecode,
|
|
125
140
|
this.randomBytes,
|
|
141
|
+
this.preimage,
|
|
126
142
|
this.calldata,
|
|
127
143
|
);
|
|
128
144
|
|
|
@@ -163,6 +179,14 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
163
179
|
return this.randomBytes;
|
|
164
180
|
}
|
|
165
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Get the contract bytecode
|
|
184
|
+
* @returns {Buffer} The contract bytecode
|
|
185
|
+
*/
|
|
186
|
+
public getPreimage(): Buffer {
|
|
187
|
+
return this.preimage;
|
|
188
|
+
}
|
|
189
|
+
|
|
166
190
|
/**
|
|
167
191
|
* Get the contract signer public key
|
|
168
192
|
* @protected
|
|
@@ -205,11 +229,31 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
205
229
|
this.addInputsFromUTXO();
|
|
206
230
|
|
|
207
231
|
const amountSpent: bigint = this.getTransactionOPNetFee();
|
|
232
|
+
|
|
233
|
+
let amountToCA: bigint;
|
|
234
|
+
if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
|
|
235
|
+
amountToCA = MINIMUM_AMOUNT_CA;
|
|
236
|
+
} else {
|
|
237
|
+
amountToCA = amountSpent;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ALWAYS THE FIRST INPUT.
|
|
208
241
|
this.addOutput({
|
|
209
|
-
value: Number(
|
|
242
|
+
value: Number(amountToCA),
|
|
210
243
|
address: this.contractAddress.p2tr(this.network),
|
|
211
244
|
});
|
|
212
245
|
|
|
246
|
+
// ALWAYS SECOND.
|
|
247
|
+
if (
|
|
248
|
+
amountToCA === MINIMUM_AMOUNT_CA &&
|
|
249
|
+
amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD
|
|
250
|
+
) {
|
|
251
|
+
this.addOutput({
|
|
252
|
+
value: Number(amountSpent - amountToCA),
|
|
253
|
+
address: this.rewardChallenge.address,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
213
257
|
await this.addRefundOutput(amountSpent + this.addOptionalOutputsAndGetAmount());
|
|
214
258
|
}
|
|
215
259
|
|
|
@@ -351,7 +395,6 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
351
395
|
|
|
352
396
|
const scriptSolution = [
|
|
353
397
|
this.randomBytes,
|
|
354
|
-
this.internalPubKeyToXOnly(),
|
|
355
398
|
input.tapScriptSig[0].signature,
|
|
356
399
|
input.tapScriptSig[1].signature,
|
|
357
400
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { address, Payment, Psbt, PsbtInput, Signer, Taptree, toXOnly } from '@btc-vision/bitcoin';
|
|
2
2
|
import { ECPairInterface } from 'ecpair';
|
|
3
|
-
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
3
|
+
import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder } from './TransactionBuilder.js';
|
|
4
4
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
5
5
|
import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
|
|
6
6
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
@@ -8,6 +8,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
|
|
|
8
8
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
9
9
|
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
10
10
|
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
11
|
+
import { ChallengeGenerator, IMineableReward } from '../mineable/ChallengeGenerator.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Shared interaction transaction
|
|
@@ -30,6 +31,9 @@ export abstract class SharedInteractionTransaction<
|
|
|
30
31
|
protected abstract readonly compiledTargetScript: Buffer;
|
|
31
32
|
protected abstract readonly scriptTree: Taptree;
|
|
32
33
|
|
|
34
|
+
protected readonly preimage: Buffer; // ALWAYS 128 bytes for the preimage
|
|
35
|
+
protected readonly rewardChallenge: IMineableReward;
|
|
36
|
+
|
|
33
37
|
protected calldataGenerator: CalldataGenerator;
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -63,7 +67,13 @@ export abstract class SharedInteractionTransaction<
|
|
|
63
67
|
throw new Error('Calldata is required');
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
this.preimage = parameters.preimage || BitcoinUtils.getSafeRandomValues(128);
|
|
71
|
+
|
|
66
72
|
this.disableAutoRefund = parameters.disableAutoRefund || false;
|
|
73
|
+
this.rewardChallenge = ChallengeGenerator.generateMineableReward(
|
|
74
|
+
this.preimage,
|
|
75
|
+
this.network,
|
|
76
|
+
);
|
|
67
77
|
|
|
68
78
|
this.calldata = Compressor.compress(parameters.calldata);
|
|
69
79
|
|
|
@@ -93,6 +103,13 @@ export abstract class SharedInteractionTransaction<
|
|
|
93
103
|
return this.randomBytes;
|
|
94
104
|
}
|
|
95
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Get the preimage
|
|
108
|
+
*/
|
|
109
|
+
public getPreimage(): Buffer {
|
|
110
|
+
return this.preimage;
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
/**
|
|
97
114
|
* Generate the secret for the interaction
|
|
98
115
|
* @protected
|
|
@@ -134,8 +151,6 @@ export abstract class SharedInteractionTransaction<
|
|
|
134
151
|
* @throws {Error} If the to address is required
|
|
135
152
|
*/
|
|
136
153
|
protected override async buildTransaction(): Promise<void> {
|
|
137
|
-
if (!this.to) throw new Error('To address is required');
|
|
138
|
-
|
|
139
154
|
const selectedRedeem = this.scriptSigner
|
|
140
155
|
? this.targetScriptRedeem
|
|
141
156
|
: this.leftOverFundsScriptRedeem;
|
|
@@ -162,16 +177,7 @@ export abstract class SharedInteractionTransaction<
|
|
|
162
177
|
this.addInputsFromUTXO();
|
|
163
178
|
}
|
|
164
179
|
|
|
165
|
-
|
|
166
|
-
this.addOutput({
|
|
167
|
-
value: Number(amountSpent),
|
|
168
|
-
address: this.to,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const amount = this.addOptionalOutputsAndGetAmount();
|
|
172
|
-
if (!this.disableAutoRefund) {
|
|
173
|
-
await this.addRefundOutput(amountSpent + amount);
|
|
174
|
-
}
|
|
180
|
+
await this.createMineableRewardOutputs();
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
/**
|
|
@@ -236,7 +242,6 @@ export abstract class SharedInteractionTransaction<
|
|
|
236
242
|
|
|
237
243
|
return [
|
|
238
244
|
this.contractSecret,
|
|
239
|
-
this.internalPubKeyToXOnly(),
|
|
240
245
|
input.tapScriptSig[0].signature,
|
|
241
246
|
input.tapScriptSig[1].signature,
|
|
242
247
|
] as Buffer[];
|
|
@@ -311,6 +316,41 @@ export abstract class SharedInteractionTransaction<
|
|
|
311
316
|
}
|
|
312
317
|
}
|
|
313
318
|
|
|
319
|
+
private async createMineableRewardOutputs(): Promise<void> {
|
|
320
|
+
if (!this.to) throw new Error('To address is required');
|
|
321
|
+
|
|
322
|
+
const amountSpent: bigint = this.getTransactionOPNetFee();
|
|
323
|
+
|
|
324
|
+
let amountToCA: bigint;
|
|
325
|
+
if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
|
|
326
|
+
amountToCA = MINIMUM_AMOUNT_CA;
|
|
327
|
+
} else {
|
|
328
|
+
amountToCA = amountSpent;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ALWAYS THE FIRST INPUT.
|
|
332
|
+
this.addOutput({
|
|
333
|
+
value: Number(amountToCA),
|
|
334
|
+
address: this.to,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// ALWAYS SECOND.
|
|
338
|
+
if (
|
|
339
|
+
amountToCA === MINIMUM_AMOUNT_CA &&
|
|
340
|
+
amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD
|
|
341
|
+
) {
|
|
342
|
+
this.addOutput({
|
|
343
|
+
value: Number(amountSpent - amountToCA),
|
|
344
|
+
address: this.rewardChallenge.address,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const amount = this.addOptionalOutputsAndGetAmount();
|
|
349
|
+
if (!this.disableAutoRefund) {
|
|
350
|
+
await this.addRefundOutput(amountSpent + amount);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
314
354
|
private async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
|
|
315
355
|
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
316
356
|
if (i === 0) {
|
|
@@ -26,6 +26,9 @@ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
|
26
26
|
|
|
27
27
|
initEccLib(ecc);
|
|
28
28
|
|
|
29
|
+
export const MINIMUM_AMOUNT_REWARD: bigint = 540n;
|
|
30
|
+
export const MINIMUM_AMOUNT_CA: bigint = 330n;
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
33
|
* Allows to build a transaction like you would on Ethereum.
|
|
31
34
|
* @description The transaction builder class
|