cashscript 0.8.0-next.2 → 0.8.0-next.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/dist/SignatureTemplate.js +1 -1
- package/dist/Transaction.d.ts +2 -2
- package/dist/Transaction.js +112 -58
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/interfaces.d.ts +6 -0
- package/dist/interfaces.js +1 -0
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +18 -9
- package/package.json +4 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { decodePrivateKeyWif, secp256k1, SigningSerializationFlag } from '@bitauth/libauth';
|
|
2
2
|
import { HashType, SignatureAlgorithm } from './interfaces.js';
|
|
3
3
|
export default class SignatureTemplate {
|
|
4
|
-
constructor(signer, hashtype = HashType.SIGHASH_ALL, signatureAlgorithm = SignatureAlgorithm.SCHNORR) {
|
|
4
|
+
constructor(signer, hashtype = HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS, signatureAlgorithm = SignatureAlgorithm.SCHNORR) {
|
|
5
5
|
this.hashtype = hashtype;
|
|
6
6
|
this.signatureAlgorithm = signatureAlgorithm;
|
|
7
7
|
if (isKeypair(signer)) {
|
package/dist/Transaction.d.ts
CHANGED
|
@@ -20,8 +20,8 @@ export declare class Transaction {
|
|
|
20
20
|
constructor(address: string, provider: NetworkProvider, redeemScript: Script, abiFunction: AbiFunction, args: (Uint8Array | SignatureTemplate)[], selector?: number | undefined);
|
|
21
21
|
from(input: Utxo): this;
|
|
22
22
|
from(inputs: Utxo[]): this;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
fromP2PKH(input: Utxo, template: SignatureTemplate): this;
|
|
24
|
+
fromP2PKH(inputs: Utxo[], template: SignatureTemplate): this;
|
|
25
25
|
to(to: string, amount: bigint, token?: TokenDetails): this;
|
|
26
26
|
to(outputs: Recipient[]): this;
|
|
27
27
|
withOpReturn(chunks: string[]): this;
|
package/dist/Transaction.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { hexToBin, binToHex, encodeTransaction,
|
|
1
|
+
import { hexToBin, binToHex, encodeTransaction, decodeTransaction, } from '@bitauth/libauth';
|
|
2
2
|
import delay from 'delay';
|
|
3
|
-
import {
|
|
3
|
+
import { hash256, placeholder, scriptToBytecode, } from '@cashscript/utils';
|
|
4
4
|
import { isSignableUtxo, } from './interfaces.js';
|
|
5
|
-
import { meep, createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, buildError, createSighashPreimage, validateRecipient, utxoComparator, cashScriptOutputToLibauthOutput, calculateDust, getOutputSize, } from './utils.js';
|
|
5
|
+
import { meep, createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, buildError, createSighashPreimage, validateRecipient, utxoComparator, cashScriptOutputToLibauthOutput, calculateDust, getOutputSize, addressToLockScript, publicKeyToP2PKHLockingBytecode, utxoTokenComparator, } from './utils.js';
|
|
6
6
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
7
|
+
import { P2PKH_INPUT_SIZE } from './constants.js';
|
|
7
8
|
const bip68 = await import('bip68');
|
|
8
9
|
export class Transaction {
|
|
9
10
|
constructor(address, provider, redeemScript, abiFunction, args, selector) {
|
|
@@ -27,7 +28,7 @@ export class Transaction {
|
|
|
27
28
|
this.inputs = this.inputs.concat(inputOrInputs);
|
|
28
29
|
return this;
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
fromP2PKH(inputOrInputs, template) {
|
|
31
32
|
if (!Array.isArray(inputOrInputs)) {
|
|
32
33
|
inputOrInputs = [inputOrInputs];
|
|
33
34
|
}
|
|
@@ -82,12 +83,22 @@ export class Transaction {
|
|
|
82
83
|
this.locktime = this.locktime ?? await this.provider.getBlockHeight();
|
|
83
84
|
await this.setInputsAndOutputs();
|
|
84
85
|
const bytecode = scriptToBytecode(this.redeemScript);
|
|
86
|
+
const lockingBytecode = addressToLockScript(this.address);
|
|
85
87
|
const inputs = this.inputs.map((utxo) => ({
|
|
86
88
|
outpointIndex: utxo.vout,
|
|
87
89
|
outpointTransactionHash: hexToBin(utxo.txid),
|
|
88
90
|
sequenceNumber: this.sequence,
|
|
89
91
|
unlockingBytecode: new Uint8Array(),
|
|
90
92
|
}));
|
|
93
|
+
// Generate source outputs from inputs (for signing with SIGHASH_UTXOS)
|
|
94
|
+
const sourceOutputs = this.inputs.map((input) => {
|
|
95
|
+
const sourceOutput = {
|
|
96
|
+
amount: input.satoshis,
|
|
97
|
+
to: isSignableUtxo(input) ? publicKeyToP2PKHLockingBytecode(input.template.getPublicKey()) : lockingBytecode,
|
|
98
|
+
token: input.token,
|
|
99
|
+
};
|
|
100
|
+
return cashScriptOutputToLibauthOutput(sourceOutput);
|
|
101
|
+
});
|
|
91
102
|
const outputs = this.outputs.map(cashScriptOutputToLibauthOutput);
|
|
92
103
|
const transaction = {
|
|
93
104
|
inputs,
|
|
@@ -100,11 +111,9 @@ export class Transaction {
|
|
|
100
111
|
// UTXO's with signature templates are signed using P2PKH
|
|
101
112
|
if (isSignableUtxo(utxo)) {
|
|
102
113
|
const pubkey = utxo.template.getPublicKey();
|
|
103
|
-
const
|
|
104
|
-
const addressContents = { payload: pubkeyHash, type: LockingBytecodeType.p2pkh };
|
|
105
|
-
const prevOutScript = addressContentsToLockingBytecode(addressContents);
|
|
114
|
+
const prevOutScript = publicKeyToP2PKHLockingBytecode(pubkey);
|
|
106
115
|
const hashtype = utxo.template.getHashType();
|
|
107
|
-
const preimage = createSighashPreimage(transaction,
|
|
116
|
+
const preimage = createSighashPreimage(transaction, sourceOutputs, i, prevOutScript, hashtype);
|
|
108
117
|
const sighash = hash256(preimage);
|
|
109
118
|
const signature = utxo.template.generateSignature(sighash);
|
|
110
119
|
const inputScript = scriptToBytecode([signature, pubkey]);
|
|
@@ -118,12 +127,12 @@ export class Transaction {
|
|
|
118
127
|
// First signature is used for sighash preimage (maybe not the best way)
|
|
119
128
|
if (covenantHashType < 0)
|
|
120
129
|
covenantHashType = arg.getHashType();
|
|
121
|
-
const preimage = createSighashPreimage(transaction,
|
|
130
|
+
const preimage = createSighashPreimage(transaction, sourceOutputs, i, bytecode, arg.getHashType());
|
|
122
131
|
const sighash = hash256(preimage);
|
|
123
132
|
return arg.generateSignature(sighash);
|
|
124
133
|
});
|
|
125
134
|
const preimage = this.abiFunction.covenant
|
|
126
|
-
? createSighashPreimage(transaction,
|
|
135
|
+
? createSighashPreimage(transaction, sourceOutputs, i, bytecode, covenantHashType)
|
|
127
136
|
: undefined;
|
|
128
137
|
const inputScript = createInputScript(this.redeemScript, completeArgs, this.selector, preimage);
|
|
129
138
|
inputScripts.push(inputScript);
|
|
@@ -169,67 +178,48 @@ export class Transaction {
|
|
|
169
178
|
if (this.outputs.length === 0) {
|
|
170
179
|
throw Error('Attempted to build a transaction without outputs');
|
|
171
180
|
}
|
|
172
|
-
|
|
173
|
-
const
|
|
181
|
+
const allUtxos = await this.provider.getUtxos(this.address);
|
|
182
|
+
const manualTokenInputs = this.inputs.filter((input) => input.token);
|
|
183
|
+
// This will throw if the amount is not enough
|
|
184
|
+
if (manualTokenInputs.length > 0) {
|
|
185
|
+
selectAllTokenUtxos(manualTokenInputs, this.outputs);
|
|
186
|
+
}
|
|
187
|
+
const automaticTokenInputs = selectAllTokenUtxos(allUtxos, this.outputs);
|
|
188
|
+
const tokenInputs = manualTokenInputs.length > 0 ? manualTokenInputs : automaticTokenInputs;
|
|
189
|
+
if (this.tokenChange) {
|
|
190
|
+
const tokenChangeOutputs = createFungibleTokenChangeOutputs(tokenInputs, this.outputs, this.address);
|
|
191
|
+
this.outputs.push(...tokenChangeOutputs);
|
|
192
|
+
}
|
|
174
193
|
// Construct list with all nfts in inputs
|
|
175
194
|
const listNftsInputs = [];
|
|
176
195
|
// If inputs are manually selected, add their tokens to balance
|
|
177
|
-
|
|
196
|
+
this.inputs.forEach((input) => {
|
|
178
197
|
if (!input.token)
|
|
179
|
-
|
|
180
|
-
const tokenCategory = input.token.category;
|
|
181
|
-
if (!netBalanceTokens[tokenCategory]) {
|
|
182
|
-
netBalanceTokens[tokenCategory] = input.token.amount;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
netBalanceTokens[tokenCategory] += input.token.amount;
|
|
186
|
-
}
|
|
198
|
+
return;
|
|
187
199
|
if (input.token.nft) {
|
|
188
200
|
listNftsInputs.push({ ...input.token.nft, category: input.token.category });
|
|
189
201
|
}
|
|
190
|
-
}
|
|
202
|
+
});
|
|
191
203
|
// Construct list with all nfts in outputs
|
|
192
204
|
let listNftsOutputs = [];
|
|
193
205
|
// Subtract all token outputs from the token balances
|
|
194
|
-
|
|
206
|
+
this.outputs.forEach((output) => {
|
|
195
207
|
if (!output.token)
|
|
196
|
-
|
|
197
|
-
const tokenCategory = output.token.category;
|
|
198
|
-
if (!netBalanceTokens[tokenCategory]) {
|
|
199
|
-
netBalanceTokens[tokenCategory] = -output.token.amount;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
netBalanceTokens[tokenCategory] -= output.token.amount;
|
|
203
|
-
}
|
|
208
|
+
return;
|
|
204
209
|
if (output.token.nft) {
|
|
205
210
|
listNftsOutputs.push({ ...output.token.nft, category: output.token.category });
|
|
206
211
|
}
|
|
207
|
-
}
|
|
212
|
+
});
|
|
208
213
|
// If inputs are manually provided, check token balances
|
|
209
214
|
if (this.inputs.length > 0) {
|
|
210
|
-
for (const [category, balance] of Object.entries(netBalanceTokens)) {
|
|
211
|
-
// Add token change outputs if applicable
|
|
212
|
-
if (this.tokenChange && balance > 0) {
|
|
213
|
-
const tokenDetails = {
|
|
214
|
-
category,
|
|
215
|
-
amount: balance,
|
|
216
|
-
};
|
|
217
|
-
const tokenChangeOutput = { to: this.address, amount: BigInt(1000), token: tokenDetails };
|
|
218
|
-
this.outputs.push(tokenChangeOutput);
|
|
219
|
-
}
|
|
220
|
-
// Throw error when token balance is insufficient
|
|
221
|
-
if (balance < 0) {
|
|
222
|
-
throw new Error(`Insufficient token balance for token with category ${category}.`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
215
|
// Compare nfts in- and outputs, check if inputs have nfts corresponding to outputs
|
|
226
216
|
// Keep list of nfts in inputs without matching output
|
|
227
217
|
// First check immutable nfts, then mutable & minting nfts together
|
|
228
|
-
//
|
|
218
|
+
// This is so an immutible input gets matched first and is removed from the list of unused nfts
|
|
229
219
|
let unusedNfts = listNftsInputs;
|
|
230
220
|
for (const nftInput of listNftsInputs) {
|
|
231
221
|
if (nftInput.capability === 'none') {
|
|
232
|
-
for (let i = 0; i < listNftsOutputs.length; i
|
|
222
|
+
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
233
223
|
// Deep equality check token objects
|
|
234
224
|
if (JSON.stringify(listNftsOutputs[i]) === JSON.stringify(nftInput)) {
|
|
235
225
|
listNftsOutputs.splice(i, 1);
|
|
@@ -241,6 +231,7 @@ export class Transaction {
|
|
|
241
231
|
}
|
|
242
232
|
for (const nftInput of listNftsInputs) {
|
|
243
233
|
if (nftInput.capability === 'minting') {
|
|
234
|
+
// eslint-disable-next-line max-len
|
|
244
235
|
const newListNftsOutputs = listNftsOutputs.filter((nftOutput) => nftOutput.category !== nftInput.category);
|
|
245
236
|
if (newListNftsOutputs !== listNftsOutputs) {
|
|
246
237
|
unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
|
|
@@ -248,7 +239,7 @@ export class Transaction {
|
|
|
248
239
|
}
|
|
249
240
|
}
|
|
250
241
|
if (nftInput.capability === 'mutable') {
|
|
251
|
-
for (let i = 0; i < listNftsOutputs.length; i
|
|
242
|
+
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
252
243
|
if (listNftsOutputs[i].category === nftInput.category) {
|
|
253
244
|
listNftsOutputs.splice(i, 1);
|
|
254
245
|
unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
|
|
@@ -257,8 +248,14 @@ export class Transaction {
|
|
|
257
248
|
}
|
|
258
249
|
}
|
|
259
250
|
}
|
|
251
|
+
for (const nftOutput of listNftsOutputs) {
|
|
252
|
+
const genesisUtxo = getTokenGenesisUtxo(this.inputs, nftOutput.category);
|
|
253
|
+
if (genesisUtxo) {
|
|
254
|
+
listNftsOutputs = listNftsOutputs.filter((nft) => nft !== nftOutput);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
260
257
|
if (listNftsOutputs.length !== 0) {
|
|
261
|
-
throw new Error(
|
|
258
|
+
throw new Error(`NFT output with token category ${listNftsOutputs[0].category} does not have corresponding input`);
|
|
262
259
|
}
|
|
263
260
|
if (this.tokenChange) {
|
|
264
261
|
for (const unusedNft of unusedNfts) {
|
|
@@ -285,7 +282,7 @@ export class Transaction {
|
|
|
285
282
|
// arguments and correctly sized placeholder preimage
|
|
286
283
|
const placeholderScript = createInputScript(this.redeemScript, placeholderArgs, this.selector, placeholderPreimage);
|
|
287
284
|
// Add one extra byte per input to over-estimate tx-in count
|
|
288
|
-
const
|
|
285
|
+
const contractInputSize = getInputSize(placeholderScript) + 1;
|
|
289
286
|
// Note that we use the addPrecision function to add "decimal points" to BigInt numbers
|
|
290
287
|
// Calculate amount to send and base fee (excluding additional fees per UTXO)
|
|
291
288
|
let amount = addPrecision(this.outputs.reduce((acc, output) => acc + output.amount, 0n));
|
|
@@ -294,23 +291,32 @@ export class Transaction {
|
|
|
294
291
|
let satsAvailable = 0n;
|
|
295
292
|
if (this.inputs.length > 0) {
|
|
296
293
|
// If inputs are already defined, the user provided the UTXOs and we perform no further UTXO selection
|
|
297
|
-
if (!this.hardcodedFee)
|
|
298
|
-
|
|
294
|
+
if (!this.hardcodedFee) {
|
|
295
|
+
const totalInputSize = this.inputs.reduce((acc, input) => acc + (isSignableUtxo(input) ? P2PKH_INPUT_SIZE : contractInputSize), 0);
|
|
296
|
+
fee += addPrecision(totalInputSize * this.feePerByte);
|
|
297
|
+
}
|
|
299
298
|
satsAvailable = addPrecision(this.inputs.reduce((acc, input) => acc + input.satoshis, 0n));
|
|
300
299
|
}
|
|
301
300
|
else {
|
|
302
301
|
// If inputs are not defined yet, we retrieve the contract's UTXOs and perform selection
|
|
303
|
-
const
|
|
302
|
+
const bchUtxos = allUtxos.filter((utxo) => !utxo.token);
|
|
304
303
|
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
305
304
|
// even if they report UTXOs in a different order
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
bchUtxos.sort(utxoComparator).reverse();
|
|
306
|
+
// Add all automatically added token inputs to the transaction
|
|
307
|
+
for (const utxo of automaticTokenInputs) {
|
|
308
308
|
this.inputs.push(utxo);
|
|
309
309
|
satsAvailable += addPrecision(utxo.satoshis);
|
|
310
310
|
if (!this.hardcodedFee)
|
|
311
|
-
fee += addPrecision(
|
|
311
|
+
fee += addPrecision(contractInputSize * this.feePerByte);
|
|
312
|
+
}
|
|
313
|
+
for (const utxo of bchUtxos) {
|
|
312
314
|
if (satsAvailable > amount + fee)
|
|
313
315
|
break;
|
|
316
|
+
this.inputs.push(utxo);
|
|
317
|
+
satsAvailable += addPrecision(utxo.satoshis);
|
|
318
|
+
if (!this.hardcodedFee)
|
|
319
|
+
fee += addPrecision(contractInputSize * this.feePerByte);
|
|
314
320
|
}
|
|
315
321
|
}
|
|
316
322
|
// Remove "decimal points" from BigInt numbers (rounding up for fee, down for others)
|
|
@@ -334,6 +340,54 @@ export class Transaction {
|
|
|
334
340
|
}
|
|
335
341
|
}
|
|
336
342
|
}
|
|
343
|
+
const getTokenGenesisUtxo = (utxos, tokenCategory) => {
|
|
344
|
+
const creationUtxo = utxos.find((utxo) => utxo.vout === 0 && utxo.txid === tokenCategory);
|
|
345
|
+
return creationUtxo;
|
|
346
|
+
};
|
|
347
|
+
const getTokenCategories = (outputs) => (outputs
|
|
348
|
+
.filter((output) => output.token)
|
|
349
|
+
.map((output) => output.token.category));
|
|
350
|
+
const calculateTotalTokenAmount = (outputs, tokenCategory) => (outputs
|
|
351
|
+
.filter((output) => output.token?.category === tokenCategory)
|
|
352
|
+
.reduce((acc, output) => acc + output.token.amount, 0n));
|
|
353
|
+
const selectTokenUtxos = (utxos, amountNeeded, tokenCategory) => {
|
|
354
|
+
const genesisUtxo = getTokenGenesisUtxo(utxos, tokenCategory);
|
|
355
|
+
if (genesisUtxo)
|
|
356
|
+
return [genesisUtxo];
|
|
357
|
+
const tokenUtxos = utxos.filter((utxo) => utxo.token?.category === tokenCategory && utxo.token?.amount > 0n);
|
|
358
|
+
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
359
|
+
// even if they report UTXOs in a different order
|
|
360
|
+
tokenUtxos.sort(utxoTokenComparator).reverse();
|
|
361
|
+
let amountAvailable = 0n;
|
|
362
|
+
const selectedUtxos = [];
|
|
363
|
+
// Add token UTXOs until we have enough to cover the amount needed (no fee calculation because it's a token)
|
|
364
|
+
for (const utxo of tokenUtxos) {
|
|
365
|
+
if (amountAvailable >= amountNeeded)
|
|
366
|
+
break;
|
|
367
|
+
selectedUtxos.push(utxo);
|
|
368
|
+
amountAvailable += utxo.token.amount;
|
|
369
|
+
}
|
|
370
|
+
if (amountAvailable < amountNeeded) {
|
|
371
|
+
throw new Error(`Insufficient funds for token ${tokenCategory}: available (${amountAvailable}) < needed (${amountNeeded}).`);
|
|
372
|
+
}
|
|
373
|
+
return selectedUtxos;
|
|
374
|
+
};
|
|
375
|
+
const selectAllTokenUtxos = (utxos, outputs) => {
|
|
376
|
+
const tokenCategories = getTokenCategories(outputs);
|
|
377
|
+
return tokenCategories.flatMap((tokenCategory) => selectTokenUtxos(utxos, calculateTotalTokenAmount(outputs, tokenCategory), tokenCategory));
|
|
378
|
+
};
|
|
379
|
+
const createFungibleTokenChangeOutputs = (utxos, outputs, address) => {
|
|
380
|
+
const tokenCategories = getTokenCategories(utxos);
|
|
381
|
+
const changeOutputs = tokenCategories.map((tokenCategory) => {
|
|
382
|
+
const required = calculateTotalTokenAmount(outputs, tokenCategory);
|
|
383
|
+
const available = calculateTotalTokenAmount(utxos, tokenCategory);
|
|
384
|
+
const change = available - required;
|
|
385
|
+
if (change === 0n)
|
|
386
|
+
return undefined;
|
|
387
|
+
return { to: address, amount: BigInt(1000), token: { category: tokenCategory, amount: change } };
|
|
388
|
+
});
|
|
389
|
+
return changeOutputs.filter((output) => output !== undefined);
|
|
390
|
+
};
|
|
337
391
|
// Note: the below is a very simple implementation of a "decimal point" system for BigInt numbers
|
|
338
392
|
// It is safe to use for UTXO fee calculations due to its low numbers, but should not be used for other purposes
|
|
339
393
|
// Also note that multiplication and division between two "decimal" bigints is not supported
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/dist/interfaces.d.ts
CHANGED
|
@@ -29,6 +29,11 @@ export interface TokenDetails {
|
|
|
29
29
|
commitment: string;
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
export interface NftObject {
|
|
33
|
+
category: string;
|
|
34
|
+
capability: 'none' | 'mutable' | 'minting';
|
|
35
|
+
commitment: string;
|
|
36
|
+
}
|
|
32
37
|
export interface LibauthOutput {
|
|
33
38
|
lockingBytecode: Uint8Array;
|
|
34
39
|
valueSatoshis: bigint;
|
|
@@ -50,6 +55,7 @@ export declare enum HashType {
|
|
|
50
55
|
SIGHASH_ALL = 1,
|
|
51
56
|
SIGHASH_NONE = 2,
|
|
52
57
|
SIGHASH_SINGLE = 3,
|
|
58
|
+
SIGHASH_UTXOS = 32,
|
|
53
59
|
SIGHASH_ANYONECANPAY = 128
|
|
54
60
|
}
|
|
55
61
|
export declare const Network: {
|
package/dist/interfaces.js
CHANGED
|
@@ -11,6 +11,7 @@ export var HashType;
|
|
|
11
11
|
HashType[HashType["SIGHASH_ALL"] = 1] = "SIGHASH_ALL";
|
|
12
12
|
HashType[HashType["SIGHASH_NONE"] = 2] = "SIGHASH_NONE";
|
|
13
13
|
HashType[HashType["SIGHASH_SINGLE"] = 3] = "SIGHASH_SINGLE";
|
|
14
|
+
HashType[HashType["SIGHASH_UTXOS"] = 32] = "SIGHASH_UTXOS";
|
|
14
15
|
HashType[HashType["SIGHASH_ANYONECANPAY"] = 128] = "SIGHASH_ANYONECANPAY";
|
|
15
16
|
})(HashType || (HashType = {}));
|
|
16
17
|
// Weird setup to allow both Enum parameters, as well as literal strings
|
package/dist/utils.d.ts
CHANGED
|
@@ -13,12 +13,14 @@ export declare function getPreimageSize(script: Uint8Array): number;
|
|
|
13
13
|
export declare function getTxSizeWithoutInputs(outputs: Output[]): number;
|
|
14
14
|
export declare function createInputScript(redeemScript: Script, encodedArgs: Uint8Array[], selector?: number, preimage?: Uint8Array): Uint8Array;
|
|
15
15
|
export declare function createOpReturnOutput(opReturnData: string[]): Output;
|
|
16
|
-
export declare function createSighashPreimage(transaction: Transaction,
|
|
16
|
+
export declare function createSighashPreimage(transaction: Transaction, sourceOutputs: LibauthOutput[], inputIndex: number, coveredBytecode: Uint8Array, hashtype: number): Uint8Array;
|
|
17
17
|
export declare function buildError(reason: string, meepStr: string): FailedTransactionError;
|
|
18
18
|
export declare function meep(tx: any, utxos: Utxo[], script: Script): string;
|
|
19
19
|
export declare function scriptToAddress(script: Script, network: string, addressType: 'p2sh20' | 'p2sh32', tokenSupport: boolean): string;
|
|
20
20
|
export declare function scriptToLockingBytecode(script: Script, addressType: 'p2sh20' | 'p2sh32'): Uint8Array;
|
|
21
|
+
export declare function publicKeyToP2PKHLockingBytecode(publicKey: Uint8Array): Uint8Array;
|
|
21
22
|
export declare function utxoComparator(a: Utxo, b: Utxo): number;
|
|
23
|
+
export declare function utxoTokenComparator(a: Utxo, b: Utxo): number;
|
|
22
24
|
/**
|
|
23
25
|
* Helper function to convert an address to a locking script
|
|
24
26
|
*
|
package/dist/utils.js
CHANGED
|
@@ -119,15 +119,7 @@ function toBin(output) {
|
|
|
119
119
|
const encode = data === output ? utf8ToBin : hexToBin;
|
|
120
120
|
return encode(data);
|
|
121
121
|
}
|
|
122
|
-
export function createSighashPreimage(transaction,
|
|
123
|
-
const sourceOutputs = inputs.map((input) => {
|
|
124
|
-
const sourceOutput = {
|
|
125
|
-
amount: input.satoshis,
|
|
126
|
-
to: Uint8Array.of(),
|
|
127
|
-
token: input.token,
|
|
128
|
-
};
|
|
129
|
-
return cashScriptOutputToLibauthOutput(sourceOutput);
|
|
130
|
-
});
|
|
122
|
+
export function createSighashPreimage(transaction, sourceOutputs, inputIndex, coveredBytecode, hashtype) {
|
|
131
123
|
const context = { inputIndex, sourceOutputs, transaction };
|
|
132
124
|
const signingSerializationType = new Uint8Array([hashtype]);
|
|
133
125
|
const sighashPreimage = generateSigningSerializationBCH(context, { coveredBytecode, signingSerializationType });
|
|
@@ -175,6 +167,12 @@ export function scriptToLockingBytecode(script, addressType) {
|
|
|
175
167
|
const lockingBytecode = addressContentsToLockingBytecode(addressContents);
|
|
176
168
|
return lockingBytecode;
|
|
177
169
|
}
|
|
170
|
+
export function publicKeyToP2PKHLockingBytecode(publicKey) {
|
|
171
|
+
const pubkeyHash = hash160(publicKey);
|
|
172
|
+
const addressContents = { payload: pubkeyHash, type: LockingBytecodeType.p2pkh };
|
|
173
|
+
const lockingBytecode = addressContentsToLockingBytecode(addressContents);
|
|
174
|
+
return lockingBytecode;
|
|
175
|
+
}
|
|
178
176
|
export function utxoComparator(a, b) {
|
|
179
177
|
if (a.satoshis > b.satoshis)
|
|
180
178
|
return 1;
|
|
@@ -182,6 +180,17 @@ export function utxoComparator(a, b) {
|
|
|
182
180
|
return -1;
|
|
183
181
|
return 0;
|
|
184
182
|
}
|
|
183
|
+
export function utxoTokenComparator(a, b) {
|
|
184
|
+
if (!a.token || !b.token)
|
|
185
|
+
throw new Error('UTXO does not have token data');
|
|
186
|
+
if (!a.token.category !== !b.token.category)
|
|
187
|
+
throw new Error('UTXO token categories do not match');
|
|
188
|
+
if (a.token.amount > b.token.amount)
|
|
189
|
+
return 1;
|
|
190
|
+
if (a.token.amount < b.token.amount)
|
|
191
|
+
return -1;
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
185
194
|
/**
|
|
186
195
|
* Helper function to convert an address to a locking script
|
|
187
196
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.8.0-next.
|
|
3
|
+
"version": "0.8.0-next.4",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -38,12 +38,13 @@
|
|
|
38
38
|
"compile:test": "tsc -p tsconfig.test.json",
|
|
39
39
|
"lint": "eslint . --ext .ts --ignore-path ../../.eslintignore",
|
|
40
40
|
"prepare": "yarn build",
|
|
41
|
+
"prepublishOnly": "yarn test && yarn lint",
|
|
41
42
|
"pretest": "yarn build:test",
|
|
42
43
|
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@bitauth/libauth": "^2.0.0-alpha.8",
|
|
46
|
-
"@cashscript/utils": "^0.8.0-next.
|
|
47
|
+
"@cashscript/utils": "^0.8.0-next.4",
|
|
47
48
|
"bip68": "^1.0.4",
|
|
48
49
|
"bitcoin-rpc-promise-retry": "^1.3.0",
|
|
49
50
|
"delay": "^5.0.0",
|
|
@@ -57,5 +58,5 @@
|
|
|
57
58
|
"jest": "^29.4.1",
|
|
58
59
|
"typescript": "^4.1.5"
|
|
59
60
|
},
|
|
60
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "f4c3f9c21cca98465e5cad919e64b4c6292e9a1f"
|
|
61
62
|
}
|