@btc-vision/transaction 1.1.8 → 1.1.9
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/index.js +1 -1
- package/browser/keypair/MessageSigner.d.ts +11 -0
- package/browser/opnet.d.ts +2 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/keypair/EcKeyPair.js +1 -1
- package/build/keypair/MessageSigner.d.ts +11 -0
- package/build/keypair/MessageSigner.js +41 -0
- package/build/opnet.d.ts +2 -1
- package/build/opnet.js +2 -1
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/keypair/EcKeyPair.ts +1 -1
- package/src/keypair/MessageSigner.ts +99 -0
- package/src/opnet.ts +2 -9
- package/src/transaction/builders/UnwrapSegwitTransaction.ts.disabled +0 -371
- package/src/transaction/builders/UnwrapTransaction.ts.disabled +0 -503
- package/src/transaction/builders/WrapTransaction.ts.disabled +0 -338
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ECPairInterface } from 'ecpair';
|
|
2
|
+
import { Network } from '@btc-vision/bitcoin';
|
|
3
|
+
declare class MessageSignerBase {
|
|
4
|
+
sha256(message: Buffer | Uint8Array): Buffer;
|
|
5
|
+
tweakAndSignMessage(keypair: ECPairInterface, message: Uint8Array | Buffer | string, network: Network): Uint8Array;
|
|
6
|
+
signMessage(keypair: ECPairInterface, message: Uint8Array | Buffer | string): Uint8Array;
|
|
7
|
+
verifySignature(publicKey: Uint8Array | Buffer, message: Uint8Array | Buffer | string, signature: Uint8Array | Buffer): boolean;
|
|
8
|
+
tweakAndVerifySignature(publicKey: Uint8Array | Buffer, message: Uint8Array | Buffer | string, signature: Uint8Array | Buffer): boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const MessageSigner: MessageSignerBase;
|
|
11
|
+
export {};
|
package/browser/opnet.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export * from './keypair/EcKeyPair.js';
|
|
|
13
13
|
export * from './keypair/Wallet.js';
|
|
14
14
|
export * from './keypair/interfaces/IWallet.js';
|
|
15
15
|
export * from './keypair/AddressVerificator.js';
|
|
16
|
+
export * from './keypair/MessageSigner.js';
|
|
16
17
|
export * from './metadata/ContractBaseMetadata.js';
|
|
18
|
+
export * from './network/ChainId.js';
|
|
17
19
|
export * from './signer/TweakedSigner.js';
|
|
18
20
|
export * from './transaction/TransactionFactory.js';
|
|
19
21
|
export * from './transaction/interfaces/ITransactionParameters.js';
|
|
@@ -26,7 +28,6 @@ export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
|
26
28
|
export * from './transaction/builders/DeploymentTransaction.js';
|
|
27
29
|
export * from './transaction/builders/CustomScriptTransaction.js';
|
|
28
30
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
29
|
-
export * from './network/ChainId.js';
|
|
30
31
|
export * from './utils/BitcoinUtils.js';
|
|
31
32
|
export * from './utxo/interfaces/IUTXO.js';
|
|
32
33
|
export * from './utxo/OPNetLimitedProvider.js';
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.1.
|
|
1
|
+
export declare const version = "1.1.9";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.1.
|
|
1
|
+
export const version = '1.1.9';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import bip32, { BIP32Factory } from 'bip32';
|
|
3
3
|
import { address, initEccLib, networks, payments } from '@btc-vision/bitcoin';
|
|
4
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
5
4
|
import { ECPairFactory } from 'ecpair';
|
|
6
5
|
import { CURVE, ProjectivePoint as Point } from '@noble/secp256k1';
|
|
7
6
|
import { taggedHash } from '@btc-vision/bitcoin/src/crypto.js';
|
|
7
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
8
8
|
initEccLib(ecc);
|
|
9
9
|
const BIP32factory = typeof bip32 === 'function' ? bip32 : BIP32Factory;
|
|
10
10
|
if (!BIP32factory) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ECPairInterface } from 'ecpair';
|
|
2
|
+
import { Network } from '@btc-vision/bitcoin';
|
|
3
|
+
declare class MessageSignerBase {
|
|
4
|
+
sha256(message: Buffer | Uint8Array): Buffer;
|
|
5
|
+
tweakAndSignMessage(keypair: ECPairInterface, message: Uint8Array | Buffer | string, network: Network): Uint8Array;
|
|
6
|
+
signMessage(keypair: ECPairInterface, message: Uint8Array | Buffer | string): Uint8Array;
|
|
7
|
+
verifySignature(publicKey: Uint8Array | Buffer, message: Uint8Array | Buffer | string, signature: Uint8Array | Buffer): boolean;
|
|
8
|
+
tweakAndVerifySignature(publicKey: Uint8Array | Buffer, message: Uint8Array | Buffer | string, signature: Uint8Array | Buffer): boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const MessageSigner: MessageSignerBase;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
|
+
import { crypto } from '@btc-vision/bitcoin';
|
|
3
|
+
import { TweakedSigner } from '../signer/TweakedSigner.js';
|
|
4
|
+
import { EcKeyPair } from './EcKeyPair.js';
|
|
5
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
6
|
+
class MessageSignerBase {
|
|
7
|
+
sha256(message) {
|
|
8
|
+
return crypto.sha256(Buffer.from(message));
|
|
9
|
+
}
|
|
10
|
+
tweakAndSignMessage(keypair, message, network) {
|
|
11
|
+
const tweaked = TweakedSigner.tweakSigner(keypair, {
|
|
12
|
+
network,
|
|
13
|
+
});
|
|
14
|
+
return this.signMessage(tweaked, message);
|
|
15
|
+
}
|
|
16
|
+
signMessage(keypair, message) {
|
|
17
|
+
if (typeof message === 'string') {
|
|
18
|
+
message = Buffer.from(message, 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
if (!keypair.privateKey) {
|
|
21
|
+
throw new Error('Private key not found in keypair.');
|
|
22
|
+
}
|
|
23
|
+
const hashedMessage = this.sha256(message);
|
|
24
|
+
return ecc.signSchnorr(hashedMessage, keypair.privateKey);
|
|
25
|
+
}
|
|
26
|
+
verifySignature(publicKey, message, signature) {
|
|
27
|
+
if (typeof message === 'string') {
|
|
28
|
+
message = Buffer.from(message, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
if (signature.length !== 64) {
|
|
31
|
+
throw new Error('Invalid signature length.');
|
|
32
|
+
}
|
|
33
|
+
const hashedMessage = this.sha256(message);
|
|
34
|
+
return ecc.verifySchnorr(hashedMessage, toXOnly(Buffer.from(publicKey)), signature);
|
|
35
|
+
}
|
|
36
|
+
tweakAndVerifySignature(publicKey, message, signature) {
|
|
37
|
+
const tweakedPublicKey = EcKeyPair.tweakPublicKey(Buffer.from(publicKey));
|
|
38
|
+
return this.verifySignature(tweakedPublicKey, message, signature);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export const MessageSigner = new MessageSignerBase();
|
package/build/opnet.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export * from './keypair/EcKeyPair.js';
|
|
|
13
13
|
export * from './keypair/Wallet.js';
|
|
14
14
|
export * from './keypair/interfaces/IWallet.js';
|
|
15
15
|
export * from './keypair/AddressVerificator.js';
|
|
16
|
+
export * from './keypair/MessageSigner.js';
|
|
16
17
|
export * from './metadata/ContractBaseMetadata.js';
|
|
18
|
+
export * from './network/ChainId.js';
|
|
17
19
|
export * from './signer/TweakedSigner.js';
|
|
18
20
|
export * from './transaction/TransactionFactory.js';
|
|
19
21
|
export * from './transaction/interfaces/ITransactionParameters.js';
|
|
@@ -26,7 +28,6 @@ export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
|
26
28
|
export * from './transaction/builders/DeploymentTransaction.js';
|
|
27
29
|
export * from './transaction/builders/CustomScriptTransaction.js';
|
|
28
30
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
29
|
-
export * from './network/ChainId.js';
|
|
30
31
|
export * from './utils/BitcoinUtils.js';
|
|
31
32
|
export * from './utxo/interfaces/IUTXO.js';
|
|
32
33
|
export * from './utxo/OPNetLimitedProvider.js';
|
package/build/opnet.js
CHANGED
|
@@ -13,7 +13,9 @@ export * from './keypair/EcKeyPair.js';
|
|
|
13
13
|
export * from './keypair/Wallet.js';
|
|
14
14
|
export * from './keypair/interfaces/IWallet.js';
|
|
15
15
|
export * from './keypair/AddressVerificator.js';
|
|
16
|
+
export * from './keypair/MessageSigner.js';
|
|
16
17
|
export * from './metadata/ContractBaseMetadata.js';
|
|
18
|
+
export * from './network/ChainId.js';
|
|
17
19
|
export * from './signer/TweakedSigner.js';
|
|
18
20
|
export * from './transaction/TransactionFactory.js';
|
|
19
21
|
export * from './transaction/interfaces/ITransactionParameters.js';
|
|
@@ -26,7 +28,6 @@ export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
|
26
28
|
export * from './transaction/builders/DeploymentTransaction.js';
|
|
27
29
|
export * from './transaction/builders/CustomScriptTransaction.js';
|
|
28
30
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
29
|
-
export * from './network/ChainId.js';
|
|
30
31
|
export * from './utils/BitcoinUtils.js';
|
|
31
32
|
export * from './utxo/interfaces/IUTXO.js';
|
|
32
33
|
export * from './utxo/OPNetLimitedProvider.js';
|
package/package.json
CHANGED
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.1.
|
|
1
|
+
export const version = '1.1.9';
|
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
|
|
3
3
|
import { address, initEccLib, Network, networks, payments, Signer } from '@btc-vision/bitcoin';
|
|
4
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
5
4
|
import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
|
|
6
5
|
import { IWallet } from './interfaces/IWallet.js';
|
|
7
6
|
import { CURVE, ProjectivePoint as Point } from '@noble/secp256k1';
|
|
8
7
|
import { taggedHash } from '@btc-vision/bitcoin/src/crypto.js';
|
|
8
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
9
9
|
|
|
10
10
|
initEccLib(ecc);
|
|
11
11
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ECPairInterface } from 'ecpair';
|
|
2
|
+
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
3
|
+
import { crypto, Network } from '@btc-vision/bitcoin';
|
|
4
|
+
import { TweakedSigner } from '../signer/TweakedSigner.js';
|
|
5
|
+
import { EcKeyPair } from './EcKeyPair.js';
|
|
6
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
7
|
+
|
|
8
|
+
class MessageSignerBase {
|
|
9
|
+
public sha256(message: Buffer | Uint8Array): Buffer {
|
|
10
|
+
return crypto.sha256(Buffer.from(message));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Tweak the keypair and sign a message.
|
|
15
|
+
* @param {ECPairInterface} keypair - The keypair to sign the message with. Must contain a private key.
|
|
16
|
+
* @param {Uint8Array | Buffer | string} message - The message to sign.
|
|
17
|
+
* @param {Network} network - The network to sign the message for.
|
|
18
|
+
* @returns The Schnorr signature.
|
|
19
|
+
*/
|
|
20
|
+
public tweakAndSignMessage(
|
|
21
|
+
keypair: ECPairInterface,
|
|
22
|
+
message: Uint8Array | Buffer | string,
|
|
23
|
+
network: Network,
|
|
24
|
+
): Uint8Array {
|
|
25
|
+
const tweaked = TweakedSigner.tweakSigner(keypair, {
|
|
26
|
+
network,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return this.signMessage(tweaked, message);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Signs a message using the provided keypair.
|
|
34
|
+
* @param {ECPairInterface} keypair - The keypair to sign the message with. Must contain a private key.
|
|
35
|
+
* @param {Uint8Array | Buffer | string} message - The message to sign.
|
|
36
|
+
* @returns The Schnorr signature.
|
|
37
|
+
* @throws Error if the private key is missing or invalid.
|
|
38
|
+
*/
|
|
39
|
+
public signMessage(
|
|
40
|
+
keypair: ECPairInterface,
|
|
41
|
+
message: Uint8Array | Buffer | string,
|
|
42
|
+
): Uint8Array {
|
|
43
|
+
if (typeof message === 'string') {
|
|
44
|
+
message = Buffer.from(message, 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!keypair.privateKey) {
|
|
48
|
+
throw new Error('Private key not found in keypair.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const hashedMessage = this.sha256(message);
|
|
52
|
+
return ecc.signSchnorr(hashedMessage, keypair.privateKey);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Verifies a Schnorr signature.
|
|
57
|
+
* @param {Uint8Array | Buffer} publicKey - The public key as a Uint8Array or Buffer.
|
|
58
|
+
* @param {Uint8Array | Buffer | string} message - The message to verify.
|
|
59
|
+
* @param {Uint8Array | Buffer} signature - The signature to verify.
|
|
60
|
+
* @returns True if the signature is valid, false otherwise.
|
|
61
|
+
* @throws Error if the signature length is invalid.
|
|
62
|
+
*/
|
|
63
|
+
public verifySignature(
|
|
64
|
+
publicKey: Uint8Array | Buffer,
|
|
65
|
+
message: Uint8Array | Buffer | string,
|
|
66
|
+
signature: Uint8Array | Buffer,
|
|
67
|
+
): boolean {
|
|
68
|
+
if (typeof message === 'string') {
|
|
69
|
+
message = Buffer.from(message, 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (signature.length !== 64) {
|
|
73
|
+
throw new Error('Invalid signature length.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const hashedMessage = this.sha256(message);
|
|
77
|
+
return ecc.verifySchnorr(hashedMessage, toXOnly(Buffer.from(publicKey)), signature);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tweak the public key and verify a signature.
|
|
82
|
+
* @param {Uint8Array | Buffer} publicKey - The public key as a Uint8Array or Buffer.
|
|
83
|
+
* @param {Uint8Array | Buffer | string} message - The message to verify.
|
|
84
|
+
* @param {Uint8Array | Buffer} signature - The signature to verify.
|
|
85
|
+
* @returns True if the signature is valid, false otherwise.
|
|
86
|
+
* @throws Error if the signature length is invalid.
|
|
87
|
+
*/
|
|
88
|
+
public tweakAndVerifySignature(
|
|
89
|
+
publicKey: Uint8Array | Buffer,
|
|
90
|
+
message: Uint8Array | Buffer | string,
|
|
91
|
+
signature: Uint8Array | Buffer,
|
|
92
|
+
): boolean {
|
|
93
|
+
const tweakedPublicKey = EcKeyPair.tweakPublicKey(Buffer.from(publicKey));
|
|
94
|
+
|
|
95
|
+
return this.verifySignature(tweakedPublicKey, message, signature);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const MessageSigner = new MessageSignerBase();
|
package/src/opnet.ts
CHANGED
|
@@ -21,10 +21,11 @@ export * from './keypair/EcKeyPair.js';
|
|
|
21
21
|
export * from './keypair/Wallet.js';
|
|
22
22
|
export * from './keypair/interfaces/IWallet.js';
|
|
23
23
|
export * from './keypair/AddressVerificator.js';
|
|
24
|
+
export * from './keypair/MessageSigner.js';
|
|
24
25
|
|
|
25
26
|
/** Metadata */
|
|
26
|
-
//export * from './metadata/contracts/wBTC.js';
|
|
27
27
|
export * from './metadata/ContractBaseMetadata.js';
|
|
28
|
+
export * from './network/ChainId.js';
|
|
28
29
|
|
|
29
30
|
/** Signer */
|
|
30
31
|
export * from './signer/TweakedSigner.js';
|
|
@@ -39,18 +40,11 @@ export * from './transaction/enums/TransactionType.js';
|
|
|
39
40
|
export * from './transaction/builders/InteractionTransaction.js';
|
|
40
41
|
export * from './transaction/builders/FundingTransaction.js';
|
|
41
42
|
export * from './transaction/builders/TransactionBuilder.js';
|
|
42
|
-
//export * from './transaction/builders/WrapTransaction.ts.disabled';
|
|
43
43
|
export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
44
44
|
export * from './transaction/builders/DeploymentTransaction.js';
|
|
45
|
-
//export * from './transaction/builders/UnwrapTransaction.ts.disabled';
|
|
46
45
|
export * from './transaction/builders/CustomScriptTransaction.js';
|
|
47
46
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
48
47
|
|
|
49
|
-
/** wBTC */
|
|
50
|
-
//export * from '../wbtc_disabled/WrappedGenerationParameters.js';
|
|
51
|
-
//export * from '../wbtc_disabled/Generate.js';
|
|
52
|
-
export * from './network/ChainId.js';
|
|
53
|
-
|
|
54
48
|
/** Utils */
|
|
55
49
|
export * from './utils/BitcoinUtils.js';
|
|
56
50
|
|
|
@@ -67,7 +61,6 @@ export * from './utxo/interfaces/BroadcastResponse.js';
|
|
|
67
61
|
export * from './transaction/psbt/PSBTTypes.js';
|
|
68
62
|
|
|
69
63
|
export * from './transaction/shared/P2TR_MS.js';
|
|
70
|
-
//export * from '../wbtc_disabled/UnwrapGeneration.ts.disabled';
|
|
71
64
|
|
|
72
65
|
/** Consensus */
|
|
73
66
|
export * from './consensus/ConsensusConfig.js';
|
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
import { Taptree } from '@btc-vision/bitcoin/src/types.js';
|
|
2
|
-
import { TransactionType } from '../enums/TransactionType.js';
|
|
3
|
-
import { IUnwrapParameters } from '../interfaces/ITransactionParameters.js';
|
|
4
|
-
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
5
|
-
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
|
-
import { wBTC } from '../../metadata/contracts/wBTC.js';
|
|
7
|
-
import { payments, Psbt, PsbtInputExtended, PsbtOutputExtended, Signer } from '@btc-vision/bitcoin';
|
|
8
|
-
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
9
|
-
import { IWBTCUTXODocument, PsbtTransaction, VaultUTXOs } from '../processor/PsbtTransaction.js';
|
|
10
|
-
import { currentConsensusConfig } from '../../consensus/ConsensusConfig.js';
|
|
11
|
-
import { ECPairInterface } from 'ecpair';
|
|
12
|
-
import { Selector } from '../../utils/types.js';
|
|
13
|
-
import { ABICoder } from '../../abi/ABICoder.js';
|
|
14
|
-
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
|
|
15
|
-
|
|
16
|
-
const abiCoder: ABICoder = new ABICoder();
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Unwrap transaction
|
|
20
|
-
* @class UnwrapSegwitTransaction
|
|
21
|
-
*/
|
|
22
|
-
export class UnwrapSegwitTransaction extends SharedInteractionTransaction<TransactionType.WBTC_UNWRAP> {
|
|
23
|
-
private static readonly UNWRAP_SELECTOR: Selector = Number(
|
|
24
|
-
'0x' + abiCoder.encodeSelector('burn'),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
public type: TransactionType.WBTC_UNWRAP = TransactionType.WBTC_UNWRAP;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The amount to wrap
|
|
31
|
-
* @private
|
|
32
|
-
*/
|
|
33
|
-
public readonly amount: bigint;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The compiled target script
|
|
37
|
-
* @protected
|
|
38
|
-
*/
|
|
39
|
-
protected readonly compiledTargetScript: Buffer;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The script tree
|
|
43
|
-
* @protected
|
|
44
|
-
*/
|
|
45
|
-
protected readonly scriptTree: Taptree;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The sighash types for the transaction
|
|
49
|
-
* @protected
|
|
50
|
-
*/
|
|
51
|
-
protected sighashTypes: number[] = []; //Transaction.SIGHASH_ALL, Transaction.SIGHASH_ANYONECANPAY
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Contract secret for the interaction
|
|
55
|
-
* @protected
|
|
56
|
-
*/
|
|
57
|
-
protected readonly contractSecret: Buffer;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The vault UTXOs
|
|
61
|
-
* @protected
|
|
62
|
-
*/
|
|
63
|
-
protected readonly vaultUTXOs: VaultUTXOs[];
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* The wBTC contract
|
|
67
|
-
* @private
|
|
68
|
-
*/
|
|
69
|
-
private readonly wbtc: wBTC;
|
|
70
|
-
|
|
71
|
-
private readonly calculatedSignHash: number = PsbtTransaction.calculateSignHash(
|
|
72
|
-
this.sighashTypes,
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
public constructor(parameters: IUnwrapParameters) {
|
|
76
|
-
if (parameters.amount < TransactionBuilder.MINIMUM_DUST) {
|
|
77
|
-
throw new Error('Amount is below dust limit');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
parameters.disableAutoRefund = true; // we have to disable auto refund for this transaction, so it does not create an unwanted output.
|
|
81
|
-
parameters.calldata = UnwrapSegwitTransaction.generateBurnCalldata(parameters.amount);
|
|
82
|
-
|
|
83
|
-
super(parameters);
|
|
84
|
-
|
|
85
|
-
this.wbtc = new wBTC(parameters.network, parameters.chainId);
|
|
86
|
-
this.to = this.wbtc.getAddress();
|
|
87
|
-
|
|
88
|
-
this.vaultUTXOs = parameters.unwrapUTXOs;
|
|
89
|
-
|
|
90
|
-
this.amount = parameters.amount;
|
|
91
|
-
this.contractSecret = this.generateSecret();
|
|
92
|
-
|
|
93
|
-
this.compiledTargetScript = this.calldataGenerator.compile(
|
|
94
|
-
this.calldata,
|
|
95
|
-
this.contractSecret,
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
this.scriptTree = this.getScriptTree();
|
|
99
|
-
this.internalInit();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Generate a valid wBTC calldata
|
|
104
|
-
* @param {bigint} amount - The amount to wrap
|
|
105
|
-
* @private
|
|
106
|
-
* @returns {Buffer} - The calldata
|
|
107
|
-
*/
|
|
108
|
-
public static generateBurnCalldata(amount: bigint): Buffer {
|
|
109
|
-
if (!amount) throw new Error('Amount is required');
|
|
110
|
-
|
|
111
|
-
const bufWriter: BinaryWriter = new BinaryWriter();
|
|
112
|
-
bufWriter.writeSelector(UnwrapSegwitTransaction.UNWRAP_SELECTOR);
|
|
113
|
-
bufWriter.writeU256(amount);
|
|
114
|
-
|
|
115
|
-
return Buffer.from(bufWriter.getBuffer());
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @description Signs the transaction
|
|
120
|
-
* @public
|
|
121
|
-
* @returns {Promise<Psbt>} - The signed transaction in hex format
|
|
122
|
-
* @throws {Error} - If something went wrong
|
|
123
|
-
*/
|
|
124
|
-
public async signPSBT(): Promise<Psbt> {
|
|
125
|
-
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
'Invalid contract address. The contract address must be a taproot address.',
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!this.vaultUTXOs.length) {
|
|
132
|
-
throw new Error('No vault UTXOs provided');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (this.signed) throw new Error('Transaction is already signed');
|
|
136
|
-
this.signed = true;
|
|
137
|
-
|
|
138
|
-
await this.buildTransaction();
|
|
139
|
-
|
|
140
|
-
this.ignoreSignatureError();
|
|
141
|
-
await this.mergeVaults(this.vaultUTXOs);
|
|
142
|
-
|
|
143
|
-
const builtTx = await this.internalBuildTransaction(this.transaction);
|
|
144
|
-
if (builtTx) {
|
|
145
|
-
return this.transaction;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
throw new Error('Could not sign transaction');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* @description Merge vault UTXOs into the transaction
|
|
153
|
-
* @param {VaultUTXOs[]} input The vault UTXOs
|
|
154
|
-
* @public
|
|
155
|
-
*/
|
|
156
|
-
public async mergeVaults(input: VaultUTXOs[]): Promise<void> {
|
|
157
|
-
const firstVault = input[0];
|
|
158
|
-
if (!firstVault) {
|
|
159
|
-
throw new Error('No vaults provided');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const total = this.getVaultTotalOutputAmount(input);
|
|
163
|
-
if (total < this.amount) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`Total vault amount (${total} sat) is less than the amount to unwrap (${this.amount} sat)`,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const outputLeftAmount = this.calculateOutputLeftAmountFromVaults(input);
|
|
170
|
-
if (
|
|
171
|
-
outputLeftAmount < currentConsensusConfig.VAULT_MINIMUM_AMOUNT &&
|
|
172
|
-
outputLeftAmount !== currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT
|
|
173
|
-
) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
`Output left amount is below minimum consolidation (${currentConsensusConfig.VAULT_MINIMUM_AMOUNT} sat) amount ${outputLeftAmount} for vault ${firstVault.vault}`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.addOutput({
|
|
180
|
-
address: firstVault.vault,
|
|
181
|
-
value: Number(outputLeftAmount),
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
this.addOutput({
|
|
185
|
-
address: this.from,
|
|
186
|
-
value: Number(this.amount),
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
for (const vault of input) {
|
|
190
|
-
await this.addVaultInputs(vault);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Builds the transaction.
|
|
196
|
-
* @param {Psbt} transaction - The transaction to build
|
|
197
|
-
* @param checkPartialSigs
|
|
198
|
-
* @protected
|
|
199
|
-
* @returns {Promise<boolean>}
|
|
200
|
-
* @throws {Error} - If something went wrong while building the transaction
|
|
201
|
-
*/
|
|
202
|
-
protected async internalBuildTransaction(
|
|
203
|
-
transaction: Psbt,
|
|
204
|
-
checkPartialSigs: boolean = false,
|
|
205
|
-
): Promise<boolean> {
|
|
206
|
-
if (transaction.data.inputs.length === 0) {
|
|
207
|
-
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
208
|
-
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
209
|
-
|
|
210
|
-
transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
211
|
-
transaction.addInputs(inputs, checkPartialSigs);
|
|
212
|
-
|
|
213
|
-
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
214
|
-
transaction.updateInput(i, this.updateInputs[i]);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
transaction.addOutputs(outputs);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
await this.signInputs(transaction);
|
|
222
|
-
|
|
223
|
-
if (this.finalized) {
|
|
224
|
-
this.transactionFee = BigInt(transaction.getFee());
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return true;
|
|
228
|
-
} catch (e) {
|
|
229
|
-
const err: Error = e as Error;
|
|
230
|
-
|
|
231
|
-
this.error(
|
|
232
|
-
`[internalBuildTransaction] Something went wrong while getting building the transaction: ${err.stack}`,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Generate a multi-signature redeem script
|
|
241
|
-
* @param {string[]} publicKeys The public keys
|
|
242
|
-
* @param {number} minimum The minimum number of signatures
|
|
243
|
-
* @protected
|
|
244
|
-
* @returns {{output: Buffer; redeem: Buffer}} The output and redeem script
|
|
245
|
-
*/
|
|
246
|
-
protected generateMultiSignRedeemScript(
|
|
247
|
-
publicKeys: string[],
|
|
248
|
-
minimum: number,
|
|
249
|
-
): { witnessUtxo: Buffer; redeemScript: Buffer; witnessScript: Buffer } {
|
|
250
|
-
const p2ms = payments.p2ms({
|
|
251
|
-
m: minimum,
|
|
252
|
-
pubkeys: publicKeys.map((key) => Buffer.from(key, 'base64')),
|
|
253
|
-
network: this.network,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
const p2wsh = payments.p2wsh({
|
|
257
|
-
redeem: p2ms,
|
|
258
|
-
network: this.network,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
const witnessUtxo = p2wsh.output;
|
|
262
|
-
const redeemScript = p2wsh.redeem?.output;
|
|
263
|
-
const witnessScript = p2ms.output;
|
|
264
|
-
|
|
265
|
-
if (!witnessUtxo || !redeemScript || !witnessScript) {
|
|
266
|
-
throw new Error('Failed to generate redeem script');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
witnessUtxo,
|
|
271
|
-
redeemScript,
|
|
272
|
-
witnessScript,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @description Add a vault UTXO to the transaction
|
|
278
|
-
* @private
|
|
279
|
-
*/
|
|
280
|
-
private addVaultUTXO(
|
|
281
|
-
utxo: IWBTCUTXODocument,
|
|
282
|
-
witness: {
|
|
283
|
-
witnessUtxo: Buffer;
|
|
284
|
-
redeemScript: Buffer;
|
|
285
|
-
witnessScript: Buffer;
|
|
286
|
-
},
|
|
287
|
-
): void {
|
|
288
|
-
const input: PsbtInputExtended = {
|
|
289
|
-
hash: utxo.hash,
|
|
290
|
-
index: utxo.outputIndex,
|
|
291
|
-
witnessUtxo: {
|
|
292
|
-
script: Buffer.from(utxo.output, 'base64'),
|
|
293
|
-
value: Number(utxo.value),
|
|
294
|
-
},
|
|
295
|
-
witnessScript: witness.witnessScript,
|
|
296
|
-
sequence: this.sequence,
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
if (this.calculatedSignHash) {
|
|
300
|
-
input.sighashType = this.calculatedSignHash;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
this.addInput(input);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* @description Add vault inputs to the transaction
|
|
308
|
-
* @param {VaultUTXOs} vault The vault UTXOs
|
|
309
|
-
* @param {Signer | ECPairInterface} [firstSigner] The first signer
|
|
310
|
-
* @private
|
|
311
|
-
*/
|
|
312
|
-
private async addVaultInputs(
|
|
313
|
-
vault: VaultUTXOs,
|
|
314
|
-
firstSigner: Signer | ECPairInterface = this.signer,
|
|
315
|
-
): Promise<void> {
|
|
316
|
-
const p2wshOutput = this.generateMultiSignRedeemScript(vault.publicKeys, vault.minimum);
|
|
317
|
-
for (const utxo of vault.utxos) {
|
|
318
|
-
const inputIndex = this.transaction.inputCount;
|
|
319
|
-
this.addVaultUTXO(utxo, p2wshOutput);
|
|
320
|
-
|
|
321
|
-
if (firstSigner) {
|
|
322
|
-
//this.log(
|
|
323
|
-
// `Signing input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
|
|
324
|
-
//);
|
|
325
|
-
|
|
326
|
-
// we don't care if we fail to sign the input
|
|
327
|
-
try {
|
|
328
|
-
await this.signInput(
|
|
329
|
-
this.transaction,
|
|
330
|
-
this.transaction.data.inputs[inputIndex],
|
|
331
|
-
inputIndex,
|
|
332
|
-
this.signer,
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
this.log(
|
|
336
|
-
`Signed input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
|
|
337
|
-
);
|
|
338
|
-
} catch (e) {
|
|
339
|
-
if (!this.ignoreSignatureErrors) {
|
|
340
|
-
this.warn(
|
|
341
|
-
`Failed to sign input ${inputIndex} with ${firstSigner.publicKey.toString('hex')} ${(e as Error).message}`,
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* @description Calculate the amount left to refund to the first vault.
|
|
351
|
-
* @param {VaultUTXOs[]} vaults The vaults
|
|
352
|
-
* @private
|
|
353
|
-
* @returns {bigint} The amount left
|
|
354
|
-
*/
|
|
355
|
-
private calculateOutputLeftAmountFromVaults(vaults: VaultUTXOs[]): bigint {
|
|
356
|
-
const total = this.getVaultTotalOutputAmount(vaults);
|
|
357
|
-
|
|
358
|
-
return total - this.amount;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
private getVaultTotalOutputAmount(vaults: VaultUTXOs[]): bigint {
|
|
362
|
-
let total = BigInt(0);
|
|
363
|
-
for (const vault of vaults) {
|
|
364
|
-
for (const utxo of vault.utxos) {
|
|
365
|
-
total += BigInt(utxo.value);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return total;
|
|
370
|
-
}
|
|
371
|
-
}
|