cashscript 0.8.0-next.3 → 0.8.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.
@@ -4,6 +4,7 @@ import { hash256, placeholder, scriptToBytecode, } from '@cashscript/utils';
4
4
  import { isSignableUtxo, } from './interfaces.js';
5
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) {
@@ -178,15 +179,15 @@ export class Transaction {
178
179
  throw Error('Attempted to build a transaction without outputs');
179
180
  }
180
181
  const allUtxos = await this.provider.getUtxos(this.address);
181
- const manualTokenInputs = this.inputs.filter((input) => input.token);
182
- // This will throw if the amount is not enough
183
- if (manualTokenInputs.length > 0) {
184
- selectAllTokenUtxos(manualTokenInputs, this.outputs);
182
+ const tokenInputs = this.inputs.length > 0
183
+ ? this.inputs.filter((input) => input.token)
184
+ : selectAllTokenUtxos(allUtxos, this.outputs);
185
+ // This throws if the manually selected inputs are not enough to cover the outputs
186
+ if (this.inputs.length > 0) {
187
+ selectAllTokenUtxos(this.inputs, this.outputs);
185
188
  }
186
- const automaticTokenInputs = selectAllTokenUtxos(allUtxos, this.outputs);
187
- const tokenInputs = manualTokenInputs.length > 0 ? manualTokenInputs : automaticTokenInputs;
188
189
  if (this.tokenChange) {
189
- const tokenChangeOutputs = createTokenChangeOutputs(tokenInputs, this.outputs, this.address);
190
+ const tokenChangeOutputs = createFungibleTokenChangeOutputs(tokenInputs, this.outputs, this.address);
190
191
  this.outputs.push(...tokenChangeOutputs);
191
192
  }
192
193
  // Construct list with all nfts in inputs
@@ -281,7 +282,7 @@ export class Transaction {
281
282
  // arguments and correctly sized placeholder preimage
282
283
  const placeholderScript = createInputScript(this.redeemScript, placeholderArgs, this.selector, placeholderPreimage);
283
284
  // Add one extra byte per input to over-estimate tx-in count
284
- const inputSize = getInputSize(placeholderScript) + 1;
285
+ const contractInputSize = getInputSize(placeholderScript) + 1;
285
286
  // Note that we use the addPrecision function to add "decimal points" to BigInt numbers
286
287
  // Calculate amount to send and base fee (excluding additional fees per UTXO)
287
288
  let amount = addPrecision(this.outputs.reduce((acc, output) => acc + output.amount, 0n));
@@ -290,8 +291,10 @@ export class Transaction {
290
291
  let satsAvailable = 0n;
291
292
  if (this.inputs.length > 0) {
292
293
  // If inputs are already defined, the user provided the UTXOs and we perform no further UTXO selection
293
- if (!this.hardcodedFee)
294
- fee += addPrecision(this.inputs.length * inputSize * this.feePerByte);
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
+ }
295
298
  satsAvailable = addPrecision(this.inputs.reduce((acc, input) => acc + input.satoshis, 0n));
296
299
  }
297
300
  else {
@@ -301,11 +304,11 @@ export class Transaction {
301
304
  // even if they report UTXOs in a different order
302
305
  bchUtxos.sort(utxoComparator).reverse();
303
306
  // Add all automatically added token inputs to the transaction
304
- for (const utxo of automaticTokenInputs) {
307
+ for (const utxo of tokenInputs) {
305
308
  this.inputs.push(utxo);
306
309
  satsAvailable += addPrecision(utxo.satoshis);
307
310
  if (!this.hardcodedFee)
308
- fee += addPrecision(inputSize * this.feePerByte);
311
+ fee += addPrecision(contractInputSize * this.feePerByte);
309
312
  }
310
313
  for (const utxo of bchUtxos) {
311
314
  if (satsAvailable > amount + fee)
@@ -313,7 +316,7 @@ export class Transaction {
313
316
  this.inputs.push(utxo);
314
317
  satsAvailable += addPrecision(utxo.satoshis);
315
318
  if (!this.hardcodedFee)
316
- fee += addPrecision(inputSize * this.feePerByte);
319
+ fee += addPrecision(contractInputSize * this.feePerByte);
317
320
  }
318
321
  }
319
322
  // Remove "decimal points" from BigInt numbers (rounding up for fee, down for others)
@@ -349,10 +352,9 @@ const calculateTotalTokenAmount = (outputs, tokenCategory) => (outputs
349
352
  .reduce((acc, output) => acc + output.token.amount, 0n));
350
353
  const selectTokenUtxos = (utxos, amountNeeded, tokenCategory) => {
351
354
  const genesisUtxo = getTokenGenesisUtxo(utxos, tokenCategory);
352
- if (genesisUtxo) {
355
+ if (genesisUtxo)
353
356
  return [genesisUtxo];
354
- }
355
- const tokenUtxos = utxos.filter((utxo) => utxo.token?.category === tokenCategory);
357
+ const tokenUtxos = utxos.filter((utxo) => utxo.token?.category === tokenCategory && utxo.token?.amount > 0n);
356
358
  // We sort the UTXOs mainly so there is consistent behaviour between network providers
357
359
  // even if they report UTXOs in a different order
358
360
  tokenUtxos.sort(utxoTokenComparator).reverse();
@@ -360,10 +362,10 @@ const selectTokenUtxos = (utxos, amountNeeded, tokenCategory) => {
360
362
  const selectedUtxos = [];
361
363
  // Add token UTXOs until we have enough to cover the amount needed (no fee calculation because it's a token)
362
364
  for (const utxo of tokenUtxos) {
363
- selectedUtxos.push(utxo);
364
- amountAvailable += utxo.token.amount;
365
365
  if (amountAvailable >= amountNeeded)
366
366
  break;
367
+ selectedUtxos.push(utxo);
368
+ amountAvailable += utxo.token.amount;
367
369
  }
368
370
  if (amountAvailable < amountNeeded) {
369
371
  throw new Error(`Insufficient funds for token ${tokenCategory}: available (${amountAvailable}) < needed (${amountNeeded}).`);
@@ -374,14 +376,17 @@ const selectAllTokenUtxos = (utxos, outputs) => {
374
376
  const tokenCategories = getTokenCategories(outputs);
375
377
  return tokenCategories.flatMap((tokenCategory) => selectTokenUtxos(utxos, calculateTotalTokenAmount(outputs, tokenCategory), tokenCategory));
376
378
  };
377
- const createTokenChangeOutputs = (utxos, outputs, address) => {
379
+ const createFungibleTokenChangeOutputs = (utxos, outputs, address) => {
378
380
  const tokenCategories = getTokenCategories(utxos);
379
- return tokenCategories.map((tokenCategory) => {
381
+ const changeOutputs = tokenCategories.map((tokenCategory) => {
380
382
  const required = calculateTotalTokenAmount(outputs, tokenCategory);
381
383
  const available = calculateTotalTokenAmount(utxos, tokenCategory);
382
384
  const change = available - required;
385
+ if (change === 0n)
386
+ return undefined;
383
387
  return { to: address, amount: BigInt(1000), token: { category: tokenCategory, amount: change } };
384
388
  });
389
+ return changeOutputs.filter((output) => output !== undefined);
385
390
  };
386
391
  // Note: the below is a very simple implementation of a "decimal point" system for BigInt numbers
387
392
  // It is safe to use for UTXO fee calculations due to its low numbers, but should not be used for other purposes
@@ -1,2 +1,3 @@
1
1
  export declare const VERSION_SIZE = 4;
2
2
  export declare const LOCKTIME_SIZE = 4;
3
+ export declare const P2PKH_INPUT_SIZE: number;
package/dist/constants.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export const VERSION_SIZE = 4;
2
2
  export const LOCKTIME_SIZE = 4;
3
+ export const P2PKH_INPUT_SIZE = 32 + 4 + 1 + 1 + 65 + 1 + 33 + 4;
3
4
  //# sourceMappingURL=constants.js.map
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@ import SignatureTemplate from './SignatureTemplate.js';
2
2
  export { SignatureTemplate };
3
3
  export { Contract, ContractFunction } from './Contract.js';
4
4
  export { Transaction } from './Transaction.js';
5
- export { Argument } from './Argument.js';
5
+ export { Argument, encodeArgument } from './Argument.js';
6
6
  export { Artifact, AbiFunction, AbiInput } from '@cashscript/utils';
7
7
  export * as utils from '@cashscript/utils';
8
- export { Utxo, Recipient, SignatureAlgorithm, HashType, Network, } from './interfaces.js';
8
+ export { Utxo, Recipient, SignatureAlgorithm, HashType, Network, isSignableUtxo, } from './interfaces.js';
9
9
  export * from './Errors.js';
10
10
  export { NetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
package/dist/index.js CHANGED
@@ -2,8 +2,9 @@ import SignatureTemplate from './SignatureTemplate.js';
2
2
  export { SignatureTemplate };
3
3
  export { Contract } from './Contract.js';
4
4
  export { Transaction } from './Transaction.js';
5
+ export { encodeArgument } from './Argument.js';
5
6
  export * as utils from '@cashscript/utils';
6
- export { SignatureAlgorithm, HashType, Network, } from './interfaces.js';
7
+ export { SignatureAlgorithm, HashType, Network, isSignableUtxo, } from './interfaces.js';
7
8
  export * from './Errors.js';
8
9
  export { BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
9
10
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cashscript",
3
- "version": "0.8.0-next.3",
3
+ "version": "0.8.0",
4
4
  "description": "Easily write and interact with Bitcoin Cash contracts",
5
5
  "keywords": [
6
6
  "bitcoin cash",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@bitauth/libauth": "^2.0.0-alpha.8",
47
- "@cashscript/utils": "^0.8.0-next.3",
47
+ "@cashscript/utils": "^0.8.0",
48
48
  "bip68": "^1.0.4",
49
49
  "bitcoin-rpc-promise-retry": "^1.3.0",
50
50
  "delay": "^5.0.0",
@@ -58,5 +58,5 @@
58
58
  "jest": "^29.4.1",
59
59
  "typescript": "^4.1.5"
60
60
  },
61
- "gitHead": "398ff2afab6d731f077002e6be021ed2f1996b4a"
61
+ "gitHead": "9864e23e5b7eb024cf4712a785a5a3fe09d6eee5"
62
62
  }