@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.
Files changed (80) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/crypto/crypto-browser.d.ts +1 -1
  3. package/browser/generators/builders/CalldataGenerator.d.ts +1 -1
  4. package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
  5. package/browser/generators/builders/MineableReward.d.ts +7 -0
  6. package/browser/index.js +1 -1
  7. package/browser/opnet.d.ts +4 -0
  8. package/browser/transaction/TransactionFactory.d.ts +19 -4
  9. package/browser/transaction/browser/WalletConnection.d.ts +18 -0
  10. package/browser/transaction/browser/types/Xverse.d.ts +24 -10
  11. package/browser/transaction/builders/ChallengeSolutionTransaction.d.ts +18 -0
  12. package/browser/transaction/builders/DeploymentTransaction.d.ts +4 -0
  13. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -0
  14. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -0
  15. package/browser/transaction/enums/TransactionType.d.ts +3 -4
  16. package/browser/transaction/interfaces/ITransactionParameters.d.ts +6 -0
  17. package/browser/transaction/mineable/ChallengeGenerator.d.ts +9 -0
  18. package/browser/utxo/interfaces/IUTXO.d.ts +1 -1
  19. package/browser/verification/TapscriptVerificator.d.ts +1 -1
  20. package/build/_version.d.ts +1 -1
  21. package/build/_version.js +1 -1
  22. package/build/generators/Generator.js +1 -1
  23. package/build/generators/builders/CalldataGenerator.d.ts +1 -1
  24. package/build/generators/builders/CalldataGenerator.js +7 -26
  25. package/build/generators/builders/DeploymentGenerator.d.ts +1 -1
  26. package/build/generators/builders/DeploymentGenerator.js +9 -6
  27. package/build/generators/builders/LegacyCalldataGenerator.js +5 -1
  28. package/build/generators/builders/MineableReward.d.ts +7 -0
  29. package/build/generators/builders/MineableReward.js +48 -0
  30. package/build/opnet.d.ts +4 -0
  31. package/build/opnet.js +4 -0
  32. package/build/transaction/TransactionFactory.d.ts +19 -4
  33. package/build/transaction/TransactionFactory.js +32 -0
  34. package/build/transaction/browser/WalletConnection.d.ts +18 -0
  35. package/build/transaction/browser/WalletConnection.js +95 -0
  36. package/build/transaction/browser/extensions/UnisatSigner.js +5 -2
  37. package/build/transaction/browser/extensions/XverseSigner.js +15 -9
  38. package/build/transaction/browser/types/Xverse.d.ts +24 -10
  39. package/build/transaction/builders/ChallengeSolutionTransaction.d.ts +18 -0
  40. package/build/transaction/builders/ChallengeSolutionTransaction.js +51 -0
  41. package/build/transaction/builders/DeploymentTransaction.d.ts +4 -0
  42. package/build/transaction/builders/DeploymentTransaction.js +23 -4
  43. package/build/transaction/builders/InteractionTransaction.js +1 -1
  44. package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -0
  45. package/build/transaction/builders/SharedInteractionTransaction.js +35 -13
  46. package/build/transaction/builders/TransactionBuilder.d.ts +2 -0
  47. package/build/transaction/builders/TransactionBuilder.js +2 -0
  48. package/build/transaction/enums/TransactionType.d.ts +3 -4
  49. package/build/transaction/enums/TransactionType.js +3 -4
  50. package/build/transaction/interfaces/ITransactionParameters.d.ts +6 -0
  51. package/build/transaction/mineable/ChallengeGenerator.d.ts +9 -0
  52. package/build/transaction/mineable/ChallengeGenerator.js +28 -0
  53. package/build/transaction/shared/TweakedTransaction.js +1 -1
  54. package/build/utxo/interfaces/IUTXO.d.ts +1 -1
  55. package/build/verification/TapscriptVerificator.d.ts +1 -1
  56. package/build/verification/TapscriptVerificator.js +2 -8
  57. package/package.json +3 -3
  58. package/src/_version.ts +1 -1
  59. package/src/generators/Generator.ts +1 -1
  60. package/src/generators/builders/CalldataGenerator.ts +10 -41
  61. package/src/generators/builders/DeploymentGenerator.ts +18 -7
  62. package/src/generators/builders/LegacyCalldataGenerator.ts +5 -1
  63. package/src/generators/builders/MineableReward.ts +66 -0
  64. package/src/opnet.ts +5 -0
  65. package/src/transaction/TransactionFactory.ts +66 -3
  66. package/src/transaction/browser/WalletConnection.ts +110 -0
  67. package/src/transaction/browser/extensions/UnisatSigner.ts +7 -3
  68. package/src/transaction/browser/extensions/XverseSigner.ts +24 -23
  69. package/src/transaction/browser/types/Xverse.ts +50 -36
  70. package/src/transaction/builders/ChallengeSolutionTransaction.ts +88 -0
  71. package/src/transaction/builders/DeploymentTransaction.ts +46 -3
  72. package/src/transaction/builders/InteractionTransaction.ts +1 -0
  73. package/src/transaction/builders/SharedInteractionTransaction.ts +54 -14
  74. package/src/transaction/builders/TransactionBuilder.ts +3 -0
  75. package/src/transaction/enums/TransactionType.ts +3 -4
  76. package/src/transaction/interfaces/ITransactionParameters.ts +8 -15
  77. package/src/transaction/mineable/ChallengeGenerator.ts +39 -0
  78. package/src/transaction/shared/TweakedTransaction.ts +1 -1
  79. package/src/utxo/interfaces/IUTXO.ts +1 -1
  80. 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 = (await this.BitcoinProvider.request(
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.toHex();
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
- // const signed = await this.unisat.signPsbt(toSignPsbts[0], options[0]);
245
+ const toSignInputs: {
246
+ [x: string]: number[];
247
+ } = {
248
+ [this.p2wpkh]: options[0].toSignInputs?.map((input) => input.index) || [],
249
+ };
249
250
 
250
- const callSign = (await this.BitcoinProvider.request('signPsbt', {
251
+ const callSign = await this.BitcoinProvider.request('signPsbt', {
251
252
  psbt: toSignPsbts[0],
252
- signInputs: options[0].toSignInputs,
253
- })) as XverseRPCSignPsbtResponse;
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); //signed.map((hex) => Psbt.fromHex(hex));
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
- const callSign = (await this.BitcoinProvider.request('signPsbt', {
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: opts.toSignInputs,
348
- })) as XverseRPCSignPsbtResponse;
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
- 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
- };
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
- connect: () => Promise<void>;
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 { TransactionBuilder } from './TransactionBuilder.js';
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(amountSpent),
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
  ];
@@ -30,6 +30,7 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
30
30
  this.compiledTargetScript = this.calldataGenerator.compile(
31
31
  this.calldata,
32
32
  this.contractSecret,
33
+ this.preimage,
33
34
  );
34
35
 
35
36
  this.scriptTree = this.getScriptTree();
@@ -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
- const amountSpent: bigint = this.getTransactionOPNetFee();
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