xpi-ts 0.2.0
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/LICENSE +21 -0
- package/README.md +516 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/lib/bitcore/address.d.ts +66 -0
- package/dist/lib/bitcore/address.d.ts.map +1 -0
- package/dist/lib/bitcore/address.js +407 -0
- package/dist/lib/bitcore/block/block.d.ts +57 -0
- package/dist/lib/bitcore/block/block.d.ts.map +1 -0
- package/dist/lib/bitcore/block/block.js +233 -0
- package/dist/lib/bitcore/block/blockheader.d.ts +82 -0
- package/dist/lib/bitcore/block/blockheader.d.ts.map +1 -0
- package/dist/lib/bitcore/block/blockheader.js +323 -0
- package/dist/lib/bitcore/block/index.d.ts +5 -0
- package/dist/lib/bitcore/block/index.d.ts.map +1 -0
- package/dist/lib/bitcore/block/index.js +2 -0
- package/dist/lib/bitcore/chunk.d.ts +22 -0
- package/dist/lib/bitcore/chunk.d.ts.map +1 -0
- package/dist/lib/bitcore/chunk.js +46 -0
- package/dist/lib/bitcore/crypto/bn.d.ts +53 -0
- package/dist/lib/bitcore/crypto/bn.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/bn.js +238 -0
- package/dist/lib/bitcore/crypto/ecdsa.d.ts +46 -0
- package/dist/lib/bitcore/crypto/ecdsa.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/ecdsa.js +247 -0
- package/dist/lib/bitcore/crypto/hash.d.ts +16 -0
- package/dist/lib/bitcore/crypto/hash.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/hash.js +87 -0
- package/dist/lib/bitcore/crypto/index.d.ts +9 -0
- package/dist/lib/bitcore/crypto/index.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/index.js +8 -0
- package/dist/lib/bitcore/crypto/musig2.d.ts +40 -0
- package/dist/lib/bitcore/crypto/musig2.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/musig2.js +236 -0
- package/dist/lib/bitcore/crypto/point.d.ts +20 -0
- package/dist/lib/bitcore/crypto/point.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/point.js +133 -0
- package/dist/lib/bitcore/crypto/random.d.ts +7 -0
- package/dist/lib/bitcore/crypto/random.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/random.js +30 -0
- package/dist/lib/bitcore/crypto/schnorr.d.ts +40 -0
- package/dist/lib/bitcore/crypto/schnorr.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/schnorr.js +185 -0
- package/dist/lib/bitcore/crypto/signature.d.ts +53 -0
- package/dist/lib/bitcore/crypto/signature.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/signature.js +300 -0
- package/dist/lib/bitcore/crypto/sigtype.d.ts +5 -0
- package/dist/lib/bitcore/crypto/sigtype.d.ts.map +1 -0
- package/dist/lib/bitcore/crypto/sigtype.js +18 -0
- package/dist/lib/bitcore/encoding/base58.d.ts +16 -0
- package/dist/lib/bitcore/encoding/base58.d.ts.map +1 -0
- package/dist/lib/bitcore/encoding/base58.js +55 -0
- package/dist/lib/bitcore/encoding/base58check.d.ts +9 -0
- package/dist/lib/bitcore/encoding/base58check.d.ts.map +1 -0
- package/dist/lib/bitcore/encoding/base58check.js +82 -0
- package/dist/lib/bitcore/encoding/bufferreader.d.ts +34 -0
- package/dist/lib/bitcore/encoding/bufferreader.d.ts.map +1 -0
- package/dist/lib/bitcore/encoding/bufferreader.js +198 -0
- package/dist/lib/bitcore/encoding/bufferwriter.d.ts +36 -0
- package/dist/lib/bitcore/encoding/bufferwriter.d.ts.map +1 -0
- package/dist/lib/bitcore/encoding/bufferwriter.js +189 -0
- package/dist/lib/bitcore/encoding/varint.d.ts +20 -0
- package/dist/lib/bitcore/encoding/varint.d.ts.map +1 -0
- package/dist/lib/bitcore/encoding/varint.js +61 -0
- package/dist/lib/bitcore/errors.d.ts +28 -0
- package/dist/lib/bitcore/errors.d.ts.map +1 -0
- package/dist/lib/bitcore/errors.js +325 -0
- package/dist/lib/bitcore/hdprivatekey.d.ts +78 -0
- package/dist/lib/bitcore/hdprivatekey.d.ts.map +1 -0
- package/dist/lib/bitcore/hdprivatekey.js +381 -0
- package/dist/lib/bitcore/hdpublickey.d.ts +98 -0
- package/dist/lib/bitcore/hdpublickey.d.ts.map +1 -0
- package/dist/lib/bitcore/hdpublickey.js +416 -0
- package/dist/lib/bitcore/index.d.ts +60 -0
- package/dist/lib/bitcore/index.d.ts.map +1 -0
- package/dist/lib/bitcore/index.js +44 -0
- package/dist/lib/bitcore/message.d.ts +23 -0
- package/dist/lib/bitcore/message.d.ts.map +1 -0
- package/dist/lib/bitcore/message.js +112 -0
- package/dist/lib/bitcore/mnemonic/errors.d.ts +7 -0
- package/dist/lib/bitcore/mnemonic/errors.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/errors.js +20 -0
- package/dist/lib/bitcore/mnemonic/index.d.ts +5 -0
- package/dist/lib/bitcore/mnemonic/index.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/index.js +4 -0
- package/dist/lib/bitcore/mnemonic/mnemonic.d.ts +23 -0
- package/dist/lib/bitcore/mnemonic/mnemonic.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/mnemonic.js +164 -0
- package/dist/lib/bitcore/mnemonic/pbkdf2.d.ts +2 -0
- package/dist/lib/bitcore/mnemonic/pbkdf2.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/pbkdf2.js +25 -0
- package/dist/lib/bitcore/mnemonic/words/english.d.ts +2 -0
- package/dist/lib/bitcore/mnemonic/words/english.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/words/english.js +2050 -0
- package/dist/lib/bitcore/mnemonic/words/index.d.ts +4 -0
- package/dist/lib/bitcore/mnemonic/words/index.d.ts.map +1 -0
- package/dist/lib/bitcore/mnemonic/words/index.js +4 -0
- package/dist/lib/bitcore/musig2/index.d.ts +3 -0
- package/dist/lib/bitcore/musig2/index.d.ts.map +1 -0
- package/dist/lib/bitcore/musig2/index.js +2 -0
- package/dist/lib/bitcore/musig2/session.d.ts +79 -0
- package/dist/lib/bitcore/musig2/session.d.ts.map +1 -0
- package/dist/lib/bitcore/musig2/session.js +346 -0
- package/dist/lib/bitcore/musig2/signer.d.ts +61 -0
- package/dist/lib/bitcore/musig2/signer.d.ts.map +1 -0
- package/dist/lib/bitcore/musig2/signer.js +146 -0
- package/dist/lib/bitcore/networks.d.ts +53 -0
- package/dist/lib/bitcore/networks.d.ts.map +1 -0
- package/dist/lib/bitcore/networks.js +150 -0
- package/dist/lib/bitcore/opcode.d.ts +250 -0
- package/dist/lib/bitcore/opcode.d.ts.map +1 -0
- package/dist/lib/bitcore/opcode.js +270 -0
- package/dist/lib/bitcore/privatekey.d.ts +56 -0
- package/dist/lib/bitcore/privatekey.d.ts.map +1 -0
- package/dist/lib/bitcore/privatekey.js +237 -0
- package/dist/lib/bitcore/publickey.d.ts +59 -0
- package/dist/lib/bitcore/publickey.d.ts.map +1 -0
- package/dist/lib/bitcore/publickey.js +263 -0
- package/dist/lib/bitcore/script/interpreter.d.ts +98 -0
- package/dist/lib/bitcore/script/interpreter.d.ts.map +1 -0
- package/dist/lib/bitcore/script/interpreter.js +1704 -0
- package/dist/lib/bitcore/script.d.ts +111 -0
- package/dist/lib/bitcore/script.d.ts.map +1 -0
- package/dist/lib/bitcore/script.js +1112 -0
- package/dist/lib/bitcore/taproot/musig2.d.ts +29 -0
- package/dist/lib/bitcore/taproot/musig2.d.ts.map +1 -0
- package/dist/lib/bitcore/taproot/musig2.js +104 -0
- package/dist/lib/bitcore/taproot/nft.d.ts +164 -0
- package/dist/lib/bitcore/taproot/nft.d.ts.map +1 -0
- package/dist/lib/bitcore/taproot/nft.js +407 -0
- package/dist/lib/bitcore/taproot.d.ts +65 -0
- package/dist/lib/bitcore/taproot.d.ts.map +1 -0
- package/dist/lib/bitcore/taproot.js +288 -0
- package/dist/lib/bitcore/transaction/index.d.ts +12 -0
- package/dist/lib/bitcore/transaction/index.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/index.js +6 -0
- package/dist/lib/bitcore/transaction/input.d.ts +202 -0
- package/dist/lib/bitcore/transaction/input.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/input.js +911 -0
- package/dist/lib/bitcore/transaction/output.d.ts +48 -0
- package/dist/lib/bitcore/transaction/output.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/output.js +231 -0
- package/dist/lib/bitcore/transaction/sighash.d.ts +32 -0
- package/dist/lib/bitcore/transaction/sighash.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/sighash.js +335 -0
- package/dist/lib/bitcore/transaction/signature.d.ts +36 -0
- package/dist/lib/bitcore/transaction/signature.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/signature.js +130 -0
- package/dist/lib/bitcore/transaction/transaction.d.ts +164 -0
- package/dist/lib/bitcore/transaction/transaction.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/transaction.js +1016 -0
- package/dist/lib/bitcore/transaction/unspentoutput.d.ts +58 -0
- package/dist/lib/bitcore/transaction/unspentoutput.d.ts.map +1 -0
- package/dist/lib/bitcore/transaction/unspentoutput.js +167 -0
- package/dist/lib/bitcore/unit.d.ts +44 -0
- package/dist/lib/bitcore/unit.d.ts.map +1 -0
- package/dist/lib/bitcore/unit.js +106 -0
- package/dist/lib/bitcore/uri.d.ts +29 -0
- package/dist/lib/bitcore/uri.d.ts.map +1 -0
- package/dist/lib/bitcore/uri.js +163 -0
- package/dist/lib/bitcore/util/base32.d.ts +5 -0
- package/dist/lib/bitcore/util/base32.d.ts.map +1 -0
- package/dist/lib/bitcore/util/base32.js +58 -0
- package/dist/lib/bitcore/util/buffer.d.ts +18 -0
- package/dist/lib/bitcore/util/buffer.d.ts.map +1 -0
- package/dist/lib/bitcore/util/buffer.js +76 -0
- package/dist/lib/bitcore/util/convertBits.d.ts +2 -0
- package/dist/lib/bitcore/util/convertBits.d.ts.map +1 -0
- package/dist/lib/bitcore/util/convertBits.js +26 -0
- package/dist/lib/bitcore/util/js.d.ts +9 -0
- package/dist/lib/bitcore/util/js.d.ts.map +1 -0
- package/dist/lib/bitcore/util/js.js +45 -0
- package/dist/lib/bitcore/util/preconditions.d.ts +6 -0
- package/dist/lib/bitcore/util/preconditions.d.ts.map +1 -0
- package/dist/lib/bitcore/util/preconditions.js +31 -0
- package/dist/lib/bitcore/util.d.ts +14 -0
- package/dist/lib/bitcore/util.d.ts.map +1 -0
- package/dist/lib/bitcore/util.js +13 -0
- package/dist/lib/bitcore/xaddress.d.ts +45 -0
- package/dist/lib/bitcore/xaddress.d.ts.map +1 -0
- package/dist/lib/bitcore/xaddress.js +279 -0
- package/dist/lib/rank/api.d.ts +75 -0
- package/dist/lib/rank/api.d.ts.map +1 -0
- package/dist/lib/rank/api.js +4 -0
- package/dist/lib/rank/index.d.ts +127 -0
- package/dist/lib/rank/index.d.ts.map +1 -0
- package/dist/lib/rank/index.js +421 -0
- package/dist/lib/rank/opcode.d.ts +23 -0
- package/dist/lib/rank/opcode.d.ts.map +1 -0
- package/dist/lib/rank/opcode.js +23 -0
- package/dist/lib/rank/script.d.ts +2 -0
- package/dist/lib/rank/script.d.ts.map +1 -0
- package/dist/lib/rank/script.js +7 -0
- package/dist/lib/rank/transaction.d.ts +3 -0
- package/dist/lib/rank/transaction.d.ts.map +1 -0
- package/dist/lib/rank/transaction.js +12 -0
- package/dist/lib/rpc.d.ts +136 -0
- package/dist/lib/rpc.d.ts.map +1 -0
- package/dist/lib/rpc.js +62 -0
- package/dist/utils/constants.d.ts +18 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +20 -0
- package/dist/utils/env.d.ts +3 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +8 -0
- package/dist/utils/string.d.ts +11 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +47 -0
- package/dist/utils/types.d.ts +2 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +1 -0
- package/dist/utils/wallet.d.ts +12 -0
- package/dist/utils/wallet.d.ts.map +1 -0
- package/dist/utils/wallet.js +28 -0
- package/package.json +91 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { Hash } from '../crypto/hash.js';
|
|
2
|
+
import { Script } from '../script.js';
|
|
3
|
+
import { buildScriptPathTaproot, buildKeyPathTaproot, extractTaprootCommitment, extractTaprootState, isPayToTaproot, } from '../taproot.js';
|
|
4
|
+
import { Transaction } from '../transaction/transaction.js';
|
|
5
|
+
import { Output } from '../transaction/output.js';
|
|
6
|
+
import { TaprootInput } from '../transaction/input.js';
|
|
7
|
+
import { UnspentOutput, } from '../transaction/unspentoutput.js';
|
|
8
|
+
import { Signature } from '../crypto/signature.js';
|
|
9
|
+
export class NFT {
|
|
10
|
+
_script;
|
|
11
|
+
_address;
|
|
12
|
+
_metadataHash;
|
|
13
|
+
_metadata;
|
|
14
|
+
_satoshis;
|
|
15
|
+
_txid;
|
|
16
|
+
_outputIndex;
|
|
17
|
+
_commitment;
|
|
18
|
+
_merkleRoot;
|
|
19
|
+
_leaves;
|
|
20
|
+
_collectionHash;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this._metadata = config.metadata;
|
|
23
|
+
this._satoshis = config.satoshis || 1000;
|
|
24
|
+
this._txid = config.txid;
|
|
25
|
+
this._outputIndex = config.outputIndex;
|
|
26
|
+
this._collectionHash = config.collectionHash;
|
|
27
|
+
if (config.collectionHash) {
|
|
28
|
+
this._metadataHash = NFTUtil.hashCollectionNFT(config.collectionHash, config.metadata);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
this._metadataHash = NFTUtil.hashMetadata(config.metadata);
|
|
32
|
+
}
|
|
33
|
+
if (config.scriptTree) {
|
|
34
|
+
const result = buildScriptPathTaproot(config.ownerKey, config.scriptTree, this._metadataHash);
|
|
35
|
+
this._script = result.script;
|
|
36
|
+
this._commitment = result.commitment;
|
|
37
|
+
this._merkleRoot = result.merkleRoot;
|
|
38
|
+
this._leaves = result.leaves;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this._script = buildKeyPathTaproot(config.ownerKey, this._metadataHash);
|
|
42
|
+
}
|
|
43
|
+
const address = this._script.toAddress(config.network);
|
|
44
|
+
if (!address) {
|
|
45
|
+
throw new Error('Failed to create address from script');
|
|
46
|
+
}
|
|
47
|
+
this._address = address;
|
|
48
|
+
}
|
|
49
|
+
static fromScript(script, metadata, satoshis, txid, outputIndex) {
|
|
50
|
+
if (!isPayToTaproot(script)) {
|
|
51
|
+
throw new Error('Script is not a valid Pay-To-Taproot script');
|
|
52
|
+
}
|
|
53
|
+
const metadataHash = extractTaprootState(script);
|
|
54
|
+
if (!metadataHash) {
|
|
55
|
+
throw new Error('Script does not have state parameter');
|
|
56
|
+
}
|
|
57
|
+
const computedHash = NFTUtil.hashMetadata(metadata);
|
|
58
|
+
if (!computedHash.equals(metadataHash)) {
|
|
59
|
+
throw new Error('Metadata does not match on-chain hash');
|
|
60
|
+
}
|
|
61
|
+
const address = script.toAddress();
|
|
62
|
+
if (!address) {
|
|
63
|
+
throw new Error('Failed to create address from script');
|
|
64
|
+
}
|
|
65
|
+
const nft = Object.create(NFT.prototype);
|
|
66
|
+
nft._script = script;
|
|
67
|
+
nft._address = address;
|
|
68
|
+
nft._metadataHash = metadataHash;
|
|
69
|
+
nft._metadata = metadata;
|
|
70
|
+
nft._satoshis = satoshis;
|
|
71
|
+
nft._txid = txid;
|
|
72
|
+
nft._outputIndex = outputIndex;
|
|
73
|
+
return nft;
|
|
74
|
+
}
|
|
75
|
+
static fromUTXO(utxo, metadata) {
|
|
76
|
+
if (utxo instanceof UnspentOutput) {
|
|
77
|
+
return NFT.fromScript(utxo.script, metadata, utxo.satoshis, utxo.txId, utxo.outputIndex);
|
|
78
|
+
}
|
|
79
|
+
const scriptData = 'script' in utxo ? utxo.script : utxo.scriptPubKey;
|
|
80
|
+
if (!scriptData) {
|
|
81
|
+
throw new Error('UTXO must have script or scriptPubKey');
|
|
82
|
+
}
|
|
83
|
+
const script = scriptData instanceof Script ? scriptData : new Script(scriptData);
|
|
84
|
+
const txid = 'txId' in utxo && utxo.txId ? utxo.txId : utxo.txid;
|
|
85
|
+
const outputIndex = 'outputIndex' in utxo
|
|
86
|
+
? utxo.outputIndex
|
|
87
|
+
: utxo.vout;
|
|
88
|
+
const satoshis = 'satoshis' in utxo && typeof utxo.satoshis === 'number'
|
|
89
|
+
? utxo.satoshis
|
|
90
|
+
: 'amount' in utxo && utxo.amount
|
|
91
|
+
? utxo.amount * 1000000
|
|
92
|
+
: 1000;
|
|
93
|
+
return NFT.fromScript(script, metadata, satoshis, txid, outputIndex);
|
|
94
|
+
}
|
|
95
|
+
get script() {
|
|
96
|
+
return this._script;
|
|
97
|
+
}
|
|
98
|
+
get address() {
|
|
99
|
+
return this._address;
|
|
100
|
+
}
|
|
101
|
+
get metadataHash() {
|
|
102
|
+
return this._metadataHash;
|
|
103
|
+
}
|
|
104
|
+
get metadata() {
|
|
105
|
+
return this._metadata;
|
|
106
|
+
}
|
|
107
|
+
get satoshis() {
|
|
108
|
+
return this._satoshis;
|
|
109
|
+
}
|
|
110
|
+
get txid() {
|
|
111
|
+
return this._txid;
|
|
112
|
+
}
|
|
113
|
+
get outputIndex() {
|
|
114
|
+
return this._outputIndex;
|
|
115
|
+
}
|
|
116
|
+
get commitment() {
|
|
117
|
+
return this._commitment;
|
|
118
|
+
}
|
|
119
|
+
get merkleRoot() {
|
|
120
|
+
return this._merkleRoot;
|
|
121
|
+
}
|
|
122
|
+
get leaves() {
|
|
123
|
+
return this._leaves;
|
|
124
|
+
}
|
|
125
|
+
get collectionHash() {
|
|
126
|
+
return this._collectionHash;
|
|
127
|
+
}
|
|
128
|
+
hasScriptTree() {
|
|
129
|
+
return this._leaves !== undefined && this._leaves.length > 0;
|
|
130
|
+
}
|
|
131
|
+
isCollectionNFT() {
|
|
132
|
+
return this._collectionHash !== undefined;
|
|
133
|
+
}
|
|
134
|
+
verifyMetadata() {
|
|
135
|
+
return NFTUtil.verifyMetadata(this._metadata, this._metadataHash);
|
|
136
|
+
}
|
|
137
|
+
transfer(newOwnerKey, currentOwnerKey, fee) {
|
|
138
|
+
if (!this._txid || this._outputIndex === undefined) {
|
|
139
|
+
throw new Error('Cannot transfer NFT without UTXO information (txid and outputIndex)');
|
|
140
|
+
}
|
|
141
|
+
return NFTUtil.transferNFT({
|
|
142
|
+
currentOwnerKey,
|
|
143
|
+
newOwnerKey,
|
|
144
|
+
nftUtxo: {
|
|
145
|
+
txid: this._txid,
|
|
146
|
+
outputIndex: this._outputIndex,
|
|
147
|
+
script: this._script,
|
|
148
|
+
satoshis: this._satoshis,
|
|
149
|
+
},
|
|
150
|
+
metadataHash: this._metadataHash,
|
|
151
|
+
fee,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
updateUTXO(txid, outputIndex) {
|
|
155
|
+
this._txid = txid;
|
|
156
|
+
this._outputIndex = outputIndex;
|
|
157
|
+
}
|
|
158
|
+
getInfo() {
|
|
159
|
+
const commitment = this._commitment || extractTaprootCommitment(this._script);
|
|
160
|
+
return {
|
|
161
|
+
commitment,
|
|
162
|
+
metadataHash: this._metadataHash,
|
|
163
|
+
address: this._address,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
toOutput() {
|
|
167
|
+
return new Output({
|
|
168
|
+
script: this._script,
|
|
169
|
+
satoshis: this._satoshis,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
toUnspentOutput() {
|
|
173
|
+
if (!this._txid || this._outputIndex === undefined) {
|
|
174
|
+
throw new Error('Cannot create UnspentOutput without UTXO information (txid and outputIndex)');
|
|
175
|
+
}
|
|
176
|
+
return new UnspentOutput({
|
|
177
|
+
txid: this._txid,
|
|
178
|
+
outputIndex: this._outputIndex,
|
|
179
|
+
script: this._script,
|
|
180
|
+
satoshis: this._satoshis,
|
|
181
|
+
address: this._address,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
getUtxo() {
|
|
185
|
+
if (!this._txid || this._outputIndex === undefined) {
|
|
186
|
+
throw new Error('Cannot get UTXO without transaction information (txid and outputIndex)');
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
txid: this._txid,
|
|
190
|
+
outputIndex: this._outputIndex,
|
|
191
|
+
script: this._script,
|
|
192
|
+
satoshis: this._satoshis,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
toJSON() {
|
|
196
|
+
return {
|
|
197
|
+
script: this._script.toBuffer().toString('hex'),
|
|
198
|
+
address: this._address.toString(),
|
|
199
|
+
metadataHash: this._metadataHash.toString('hex'),
|
|
200
|
+
metadata: this._metadata,
|
|
201
|
+
satoshis: this._satoshis,
|
|
202
|
+
txid: this._txid,
|
|
203
|
+
outputIndex: this._outputIndex,
|
|
204
|
+
commitment: this._commitment?.toString(),
|
|
205
|
+
merkleRoot: this._merkleRoot?.toString('hex'),
|
|
206
|
+
collectionHash: this._collectionHash?.toString('hex'),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
toObject() {
|
|
210
|
+
return {
|
|
211
|
+
script: this._script,
|
|
212
|
+
address: this._address,
|
|
213
|
+
metadataHash: this._metadataHash,
|
|
214
|
+
metadata: this._metadata,
|
|
215
|
+
satoshis: this._satoshis,
|
|
216
|
+
txid: this._txid,
|
|
217
|
+
outputIndex: this._outputIndex,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
toString() {
|
|
221
|
+
return `NFT(${this._metadata.name}, ${this._address.toString()})`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
export class NFTUtil {
|
|
225
|
+
static hashMetadata(metadata) {
|
|
226
|
+
const metadataJSON = JSON.stringify(metadata);
|
|
227
|
+
return Hash.sha256(Buffer.from(metadataJSON, 'utf8'));
|
|
228
|
+
}
|
|
229
|
+
static hashCollection(collectionInfo) {
|
|
230
|
+
const collectionJSON = JSON.stringify(collectionInfo);
|
|
231
|
+
return Hash.sha256(Buffer.from(collectionJSON, 'utf8'));
|
|
232
|
+
}
|
|
233
|
+
static hashCollectionNFT(collectionHash, nftMetadata) {
|
|
234
|
+
const combinedData = {
|
|
235
|
+
collection: collectionHash.toString('hex'),
|
|
236
|
+
nft: nftMetadata,
|
|
237
|
+
};
|
|
238
|
+
const combinedJSON = JSON.stringify(combinedData);
|
|
239
|
+
return Hash.sha256(Buffer.from(combinedJSON, 'utf8'));
|
|
240
|
+
}
|
|
241
|
+
static verifyMetadata(metadata, hash) {
|
|
242
|
+
const computedHash = NFTUtil.hashMetadata(metadata);
|
|
243
|
+
return computedHash.equals(hash);
|
|
244
|
+
}
|
|
245
|
+
static verifyCollectionNFT(collectionHash, nftMetadata, hash) {
|
|
246
|
+
const computedHash = NFTUtil.hashCollectionNFT(collectionHash, nftMetadata);
|
|
247
|
+
return computedHash.equals(hash);
|
|
248
|
+
}
|
|
249
|
+
static extractMetadataHash(script) {
|
|
250
|
+
if (!isPayToTaproot(script)) {
|
|
251
|
+
throw new Error('Script is not a valid Pay-To-Taproot script');
|
|
252
|
+
}
|
|
253
|
+
return extractTaprootState(script);
|
|
254
|
+
}
|
|
255
|
+
static createKeyPathNFT(ownerKey, metadata, satoshis = 1000, network) {
|
|
256
|
+
const metadataHash = NFTUtil.hashMetadata(metadata);
|
|
257
|
+
const script = buildKeyPathTaproot(ownerKey, metadataHash);
|
|
258
|
+
const address = script.toAddress(network);
|
|
259
|
+
if (!address) {
|
|
260
|
+
throw new Error('Failed to create address from script');
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
script,
|
|
264
|
+
address,
|
|
265
|
+
metadataHash,
|
|
266
|
+
metadata,
|
|
267
|
+
satoshis,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
static createScriptPathNFT(ownerKey, metadata, scriptTree, satoshis = 1000, network) {
|
|
271
|
+
const metadataHash = NFTUtil.hashMetadata(metadata);
|
|
272
|
+
const { script, commitment, merkleRoot, leaves } = buildScriptPathTaproot(ownerKey, scriptTree, metadataHash);
|
|
273
|
+
const address = script.toAddress(network);
|
|
274
|
+
if (!address) {
|
|
275
|
+
throw new Error('Failed to create address from script');
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
script,
|
|
279
|
+
address,
|
|
280
|
+
metadataHash,
|
|
281
|
+
metadata,
|
|
282
|
+
satoshis,
|
|
283
|
+
commitment,
|
|
284
|
+
merkleRoot,
|
|
285
|
+
leaves,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
static createCollectionNFT(ownerKey, collectionHash, nftMetadata, satoshis = 1000, network) {
|
|
289
|
+
const metadataHash = NFTUtil.hashCollectionNFT(collectionHash, nftMetadata);
|
|
290
|
+
const script = buildKeyPathTaproot(ownerKey, metadataHash);
|
|
291
|
+
const address = script.toAddress(network);
|
|
292
|
+
if (!address) {
|
|
293
|
+
throw new Error('Failed to create address from script');
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
script,
|
|
297
|
+
address,
|
|
298
|
+
metadataHash,
|
|
299
|
+
metadata: nftMetadata,
|
|
300
|
+
satoshis,
|
|
301
|
+
collectionHash,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
static mintNFT(config) {
|
|
305
|
+
const nft = NFTUtil.createKeyPathNFT(config.ownerKey.publicKey, config.metadata, config.satoshis || 1000, config.network);
|
|
306
|
+
const tx = new Transaction();
|
|
307
|
+
tx.addOutput(new Output({
|
|
308
|
+
script: nft.script,
|
|
309
|
+
satoshis: nft.satoshis,
|
|
310
|
+
}));
|
|
311
|
+
return tx;
|
|
312
|
+
}
|
|
313
|
+
static mintBatch(ownerKey, nftMetadataList, satoshisPerNFT = 1000, network) {
|
|
314
|
+
const tx = new Transaction();
|
|
315
|
+
for (const metadata of nftMetadataList) {
|
|
316
|
+
const nft = NFTUtil.createKeyPathNFT(ownerKey.publicKey, metadata, satoshisPerNFT, network);
|
|
317
|
+
tx.addOutput(new Output({
|
|
318
|
+
script: nft.script,
|
|
319
|
+
satoshis: nft.satoshis,
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
return tx;
|
|
323
|
+
}
|
|
324
|
+
static mintCollection(ownerKey, collectionInfo, nftMetadataList, satoshisPerNFT = 1000, network) {
|
|
325
|
+
const collectionHash = NFTUtil.hashCollection(collectionInfo);
|
|
326
|
+
const tx = new Transaction();
|
|
327
|
+
for (const nftMetadata of nftMetadataList) {
|
|
328
|
+
const nft = NFTUtil.createCollectionNFT(ownerKey.publicKey, collectionHash, nftMetadata, satoshisPerNFT, network);
|
|
329
|
+
tx.addOutput(new Output({
|
|
330
|
+
script: nft.script,
|
|
331
|
+
satoshis: nft.satoshis,
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
return tx;
|
|
335
|
+
}
|
|
336
|
+
static transferNFT(config) {
|
|
337
|
+
const { currentOwnerKey, newOwnerKey, nftUtxo, metadataHash, fee } = config;
|
|
338
|
+
const inputState = NFTUtil.extractMetadataHash(nftUtxo.script);
|
|
339
|
+
if (!inputState || !inputState.equals(metadataHash)) {
|
|
340
|
+
throw new Error('Input script metadata hash does not match');
|
|
341
|
+
}
|
|
342
|
+
const newNFTScript = buildKeyPathTaproot(newOwnerKey, metadataHash);
|
|
343
|
+
const outputSatoshis = fee ? nftUtxo.satoshis - fee : nftUtxo.satoshis;
|
|
344
|
+
if (outputSatoshis < 546) {
|
|
345
|
+
throw new Error('Output value below dust limit (546 satoshis)');
|
|
346
|
+
}
|
|
347
|
+
const tx = new Transaction();
|
|
348
|
+
tx.addInput(new TaprootInput({
|
|
349
|
+
prevTxId: Buffer.from(nftUtxo.txid, 'hex'),
|
|
350
|
+
outputIndex: nftUtxo.outputIndex,
|
|
351
|
+
output: new Output({
|
|
352
|
+
script: nftUtxo.script,
|
|
353
|
+
satoshis: nftUtxo.satoshis,
|
|
354
|
+
}),
|
|
355
|
+
script: new Script(),
|
|
356
|
+
}));
|
|
357
|
+
tx.addOutput(new Output({
|
|
358
|
+
script: newNFTScript,
|
|
359
|
+
satoshis: outputSatoshis,
|
|
360
|
+
}));
|
|
361
|
+
tx.sign(currentOwnerKey, Signature.SIGHASH_ALL | Signature.SIGHASH_LOTUS, 'schnorr');
|
|
362
|
+
return tx;
|
|
363
|
+
}
|
|
364
|
+
static validateTransfer(inputScript, outputScript) {
|
|
365
|
+
const inputState = NFTUtil.extractMetadataHash(inputScript);
|
|
366
|
+
const outputState = NFTUtil.extractMetadataHash(outputScript);
|
|
367
|
+
if (!inputState || !outputState) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
return inputState.equals(outputState);
|
|
371
|
+
}
|
|
372
|
+
static verifyProvenance(transfers) {
|
|
373
|
+
if (transfers.length === 0) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const originalHash = transfers[0].metadataHash;
|
|
377
|
+
for (const transfer of transfers) {
|
|
378
|
+
if (transfer.metadataHash !== originalHash) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
static isNFT(script) {
|
|
385
|
+
if (!isPayToTaproot(script)) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
const state = extractTaprootState(script);
|
|
389
|
+
return state !== null;
|
|
390
|
+
}
|
|
391
|
+
static getNFTInfo(script) {
|
|
392
|
+
if (!NFTUtil.isNFT(script)) {
|
|
393
|
+
throw new Error('Script is not an NFT');
|
|
394
|
+
}
|
|
395
|
+
const commitment = extractTaprootCommitment(script);
|
|
396
|
+
const metadataHash = extractTaprootState(script);
|
|
397
|
+
const address = script.toAddress();
|
|
398
|
+
if (!address) {
|
|
399
|
+
throw new Error('Failed to create address from NFT script');
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
commitment,
|
|
403
|
+
metadataHash,
|
|
404
|
+
address,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PublicKey } from './publickey.js';
|
|
2
|
+
import { PrivateKey } from './privatekey.js';
|
|
3
|
+
import { Script } from './script.js';
|
|
4
|
+
export interface TapLeafNode {
|
|
5
|
+
script: Script | Buffer;
|
|
6
|
+
leafVersion?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface TapBranchNode {
|
|
9
|
+
left: TapNode;
|
|
10
|
+
right: TapNode;
|
|
11
|
+
}
|
|
12
|
+
export type TapNode = TapLeafNode | TapBranchNode;
|
|
13
|
+
export interface TapLeaf {
|
|
14
|
+
script: Script;
|
|
15
|
+
leafVersion: number;
|
|
16
|
+
leafHash: Buffer;
|
|
17
|
+
merklePath: Buffer[];
|
|
18
|
+
}
|
|
19
|
+
export interface TapTreeBuildResult {
|
|
20
|
+
merkleRoot: Buffer;
|
|
21
|
+
leaves: TapLeaf[];
|
|
22
|
+
}
|
|
23
|
+
export declare const TAPROOT_LEAF_MASK = 254;
|
|
24
|
+
export declare const TAPROOT_LEAF_TAPSCRIPT = 192;
|
|
25
|
+
export declare const TAPROOT_CONTROL_BASE_SIZE = 33;
|
|
26
|
+
export declare const TAPROOT_CONTROL_NODE_SIZE = 32;
|
|
27
|
+
export declare const TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
|
|
28
|
+
export declare const TAPROOT_CONTROL_MAX_SIZE: number;
|
|
29
|
+
export declare const TAPROOT_SCRIPTTYPE = 81;
|
|
30
|
+
export declare const TAPROOT_INTRO_SIZE = 3;
|
|
31
|
+
export declare const TAPROOT_SIZE_WITHOUT_STATE: number;
|
|
32
|
+
export declare const TAPROOT_SIZE_WITH_STATE: number;
|
|
33
|
+
export declare const TAPROOT_SIGHASH_TYPE: number;
|
|
34
|
+
export declare const TAPROOT_ANNEX_TAG = 80;
|
|
35
|
+
export declare function taggedHash(tag: string, data: Buffer): Buffer;
|
|
36
|
+
export declare function calculateTapTweak(internalPubKey: PublicKey, merkleRoot?: Buffer): Buffer;
|
|
37
|
+
export declare function calculateTapLeaf(script: Script | Buffer, leafVersion?: number): Buffer;
|
|
38
|
+
export declare function calculateTapBranch(left: Buffer, right: Buffer): Buffer;
|
|
39
|
+
export declare function tweakPublicKey(internalPubKey: PublicKey, merkleRoot?: Buffer): PublicKey;
|
|
40
|
+
export declare function tweakPrivateKey(internalPrivKey: PrivateKey, merkleRoot?: Buffer): PrivateKey;
|
|
41
|
+
export declare function isTapLeafNode(node: TapNode): node is TapLeafNode;
|
|
42
|
+
export declare function isTapBranchNode(node: TapNode): node is TapBranchNode;
|
|
43
|
+
export declare function buildTapTree(tree: TapNode): TapTreeBuildResult;
|
|
44
|
+
export declare function createControlBlock(internalPubKey: PublicKey, leafIndex: number, tree: TapNode): Buffer;
|
|
45
|
+
export declare function verifyTaprootCommitment(commitmentPubKey: PublicKey, internalPubKey: PublicKey, merkleRoot: Buffer): boolean;
|
|
46
|
+
export declare function isPayToTaproot(script: Script): boolean;
|
|
47
|
+
export declare function extractTaprootCommitment(script: Script): PublicKey;
|
|
48
|
+
export declare function extractTaprootState(script: Script): Buffer | null;
|
|
49
|
+
export declare function buildPayToTaproot(commitment: PublicKey, state?: Buffer): Script;
|
|
50
|
+
export declare function buildKeyPathTaproot(internalPubKey: PublicKey, state?: Buffer): Script;
|
|
51
|
+
export declare function buildScriptPathTaproot(internalPubKey: PublicKey, tree: TapNode, state?: Buffer): {
|
|
52
|
+
script: Script;
|
|
53
|
+
commitment: PublicKey;
|
|
54
|
+
merkleRoot: Buffer;
|
|
55
|
+
leaves: TapLeaf[];
|
|
56
|
+
};
|
|
57
|
+
export declare function verifyTaprootScriptPath(internalPubKey: Buffer, script: Script, commitmentPubKey: Buffer, leafVersion: number, merklePath: Buffer[], parity: number): boolean;
|
|
58
|
+
export interface TaprootVerifyResult {
|
|
59
|
+
success: boolean;
|
|
60
|
+
error?: string;
|
|
61
|
+
scriptToExecute?: Script;
|
|
62
|
+
stack?: Buffer[];
|
|
63
|
+
}
|
|
64
|
+
export declare function verifyTaprootSpend(scriptPubkey: Script, stack: Buffer[], flags: number): TaprootVerifyResult;
|
|
65
|
+
//# sourceMappingURL=taproot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taproot.d.ts","sourceRoot":"","sources":["../../../lib/bitcore/taproot.ts"],"names":[],"mappings":"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"}
|