cashscript 0.7.6 → 0.8.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +2 -6
  2. package/dist/{main/Argument.d.ts → Argument.d.ts} +1 -1
  3. package/dist/{module/Argument.js → Argument.js} +1 -1
  4. package/dist/{module/Contract.d.ts → Contract.d.ts} +8 -6
  5. package/dist/{module/Contract.js → Contract.js} +15 -25
  6. package/dist/{main/Errors.d.ts → Errors.d.ts} +4 -1
  7. package/dist/{module/Errors.js → Errors.js} +7 -3
  8. package/dist/{main/SignatureTemplate.d.ts → SignatureTemplate.d.ts} +2 -3
  9. package/dist/{module/SignatureTemplate.js → SignatureTemplate.js} +4 -5
  10. package/dist/{module/Transaction.d.ts → Transaction.d.ts} +7 -5
  11. package/dist/Transaction.js +352 -0
  12. package/dist/constants.d.ts +2 -0
  13. package/dist/constants.js +3 -0
  14. package/dist/{main/index.d.ts → index.d.ts} +1 -1
  15. package/dist/{module/index.js → index.js} +2 -3
  16. package/dist/{module/interfaces.d.ts → interfaces.d.ts} +33 -6
  17. package/dist/{module/interfaces.js → interfaces.js} +0 -2
  18. package/dist/{module/network → network}/BitcoinRpcNetworkProvider.d.ts +2 -3
  19. package/dist/network/BitcoinRpcNetworkProvider.js +29 -0
  20. package/dist/{module/network → network}/ElectrumNetworkProvider.js +53 -72
  21. package/dist/network/FullStackNetworkProvider.js +33 -0
  22. package/dist/{main/network → network}/index.d.ts +0 -1
  23. package/dist/{module/network → network}/index.js +0 -1
  24. package/dist/{main/utils.d.ts → utils.d.ts} +10 -6
  25. package/dist/{module/utils.js → utils.js} +90 -46
  26. package/package.json +18 -16
  27. package/dist/main/Argument.js +0 -60
  28. package/dist/main/Contract.d.ts +0 -23
  29. package/dist/main/Contract.js +0 -87
  30. package/dist/main/Errors.js +0 -84
  31. package/dist/main/SignatureTemplate.js +0 -45
  32. package/dist/main/Transaction.d.ts +0 -39
  33. package/dist/main/Transaction.js +0 -258
  34. package/dist/main/constants.d.ts +0 -5
  35. package/dist/main/constants.js +0 -9
  36. package/dist/main/index.js +0 -50
  37. package/dist/main/interfaces.d.ts +0 -43
  38. package/dist/main/interfaces.js +0 -32
  39. package/dist/main/network/BitboxNetworkProvider.d.ts +0 -26
  40. package/dist/main/network/BitboxNetworkProvider.js +0 -40
  41. package/dist/main/network/BitcoinRpcNetworkProvider.d.ts +0 -41
  42. package/dist/main/network/BitcoinRpcNetworkProvider.js +0 -49
  43. package/dist/main/network/ElectrumNetworkProvider.js +0 -162
  44. package/dist/main/network/FullStackNetworkProvider.js +0 -55
  45. package/dist/main/network/NetworkProvider.js +0 -3
  46. package/dist/main/network/index.js +0 -15
  47. package/dist/main/utils.js +0 -205
  48. package/dist/module/Argument.d.ts +0 -3
  49. package/dist/module/Errors.d.ts +0 -66
  50. package/dist/module/SignatureTemplate.d.ts +0 -15
  51. package/dist/module/Transaction.js +0 -251
  52. package/dist/module/constants.d.ts +0 -5
  53. package/dist/module/constants.js +0 -6
  54. package/dist/module/index.d.ts +0 -10
  55. package/dist/module/network/BitboxNetworkProvider.d.ts +0 -26
  56. package/dist/module/network/BitboxNetworkProvider.js +0 -37
  57. package/dist/module/network/BitcoinRpcNetworkProvider.js +0 -46
  58. package/dist/module/network/ElectrumNetworkProvider.d.ts +0 -19
  59. package/dist/module/network/FullStackNetworkProvider.d.ts +0 -40
  60. package/dist/module/network/FullStackNetworkProvider.js +0 -52
  61. package/dist/module/network/NetworkProvider.d.ts +0 -31
  62. package/dist/module/network/index.d.ts +0 -5
  63. package/dist/module/utils.d.ts +0 -26
  64. /package/dist/{main/network → network}/ElectrumNetworkProvider.d.ts +0 -0
  65. /package/dist/{main/network → network}/FullStackNetworkProvider.d.ts +0 -0
  66. /package/dist/{main/network → network}/NetworkProvider.d.ts +0 -0
  67. /package/dist/{module/network → network}/NetworkProvider.js +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CashScript
2
2
 
3
- [![Build Status](https://travis-ci.org/Bitcoin-com/cashscript.svg)](https://travis-ci.org/Bitcoin-com/cashscript)
3
+ ![Build Status](https://github.com/CashScript/cashscript/actions/workflows/github-actions.yml/badge.svg)
4
4
  [![Coverage Status](https://img.shields.io/codecov/c/github/Bitcoin-com/cashscript.svg)](https://codecov.io/gh/Bitcoin-com/cashscript/)
5
5
  [![NPM Version](https://img.shields.io/npm/v/cashscript.svg)](https://www.npmjs.com/package/cashscript)
6
6
  [![NPM Monthly Downloads](https://img.shields.io/npm/dm/cashscript.svg)](https://www.npmjs.com/package/cashscript)
@@ -26,16 +26,12 @@ npm install cashscript
26
26
  import { Contract, ... } from 'cashscript';
27
27
  ```
28
28
 
29
- ```js
30
- const { Contract, ... } = require('cashscript');
31
- ```
32
-
33
29
  Using the CashScript SDK, you can import contract artifact files, create new instances of these contracts, and interact with these instances:
34
30
 
35
31
  ```ts
36
32
  ...
37
33
  // Import the P2PKH artifact
38
- const P2PKH = require('./p2pkh-artifact.json');
34
+ import P2PKH from './p2pkh-artifact.json' assert { type: 'json' };
39
35
 
40
36
  // Instantiate a network provider for CashScript's network operations
41
37
  const provider = new ElectrumNetworkProvider('mainnet');
@@ -1,3 +1,3 @@
1
1
  import SignatureTemplate from './SignatureTemplate.js';
2
- export declare type Argument = number | bigint | boolean | string | Uint8Array | SignatureTemplate;
2
+ export declare type Argument = bigint | boolean | string | Uint8Array | SignatureTemplate;
3
3
  export declare function encodeArgument(argument: Argument, typeStr: string): Uint8Array | SignatureTemplate;
@@ -11,7 +11,7 @@ export function encodeArgument(argument, typeStr) {
11
11
  return encodeBool(argument);
12
12
  }
13
13
  if (type === PrimitiveType.INT) {
14
- if (typeof argument !== 'number' && typeof argument !== 'bigint') {
14
+ if (typeof argument !== 'bigint') {
15
15
  throw new TypeError(typeof argument, type);
16
16
  }
17
17
  return encodeInt(argument);
@@ -1,23 +1,25 @@
1
1
  import { Artifact } from '@cashscript/utils';
2
2
  import { Transaction } from './Transaction.js';
3
3
  import { Argument } from './Argument.js';
4
- import { Utxo } from './interfaces.js';
5
- import NetworkProvider from './network/NetworkProvider.js';
4
+ import { ContractOptions, Utxo } from './interfaces.js';
6
5
  export declare class Contract {
7
6
  private artifact;
8
- private provider;
7
+ private options?;
9
8
  name: string;
10
9
  address: string;
10
+ tokenAddress: string;
11
+ bytecode: string;
11
12
  bytesize: number;
12
13
  opcount: number;
13
14
  functions: {
14
15
  [name: string]: ContractFunction;
15
16
  };
16
17
  private redeemScript;
17
- constructor(artifact: Artifact, constructorArgs: Argument[], provider?: NetworkProvider);
18
- getBalance(): Promise<number>;
18
+ private provider;
19
+ private addressType;
20
+ constructor(artifact: Artifact, constructorArgs: Argument[], options?: ContractOptions | undefined);
21
+ getBalance(): Promise<bigint>;
19
22
  getUtxos(): Promise<Utxo[]>;
20
- getRedeemScriptHex(): string;
21
23
  private createFunction;
22
24
  }
23
25
  export declare type ContractFunction = (...args: Argument[]) => Transaction;
@@ -1,23 +1,18 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { binToHex } from '@bitauth/libauth';
11
2
  import { asmToScript, calculateBytesize, countOpcodes, generateRedeemScript, scriptToBytecode, } from '@cashscript/utils';
12
3
  import { Transaction } from './Transaction.js';
13
4
  import { encodeArgument } from './Argument.js';
14
- import { scriptToAddress, } from './utils.js';
5
+ import { scriptToAddress } from './utils.js';
15
6
  import SignatureTemplate from './SignatureTemplate.js';
16
7
  import { ElectrumNetworkProvider } from './network/index.js';
17
8
  export class Contract {
18
- constructor(artifact, constructorArgs, provider = new ElectrumNetworkProvider()) {
9
+ constructor(artifact, constructorArgs, options) {
19
10
  this.artifact = artifact;
20
- this.provider = provider;
11
+ this.options = options;
12
+ const defaultProvider = new ElectrumNetworkProvider();
13
+ const defaultAddressType = 'p2sh32';
14
+ this.provider = this.options?.provider ?? defaultProvider;
15
+ this.addressType = this.options?.addressType ?? defaultAddressType;
21
16
  const expectedProperties = ['abi', 'bytecode', 'constructorInputs', 'contractName'];
22
17
  if (!expectedProperties.every((property) => property in artifact)) {
23
18
  throw new Error('Invalid or incomplete artifact provided');
@@ -47,23 +42,18 @@ export class Contract {
47
42
  });
48
43
  }
49
44
  this.name = artifact.contractName;
50
- this.address = scriptToAddress(this.redeemScript, this.provider.network);
45
+ this.address = scriptToAddress(this.redeemScript, this.provider.network, this.addressType, false);
46
+ this.tokenAddress = scriptToAddress(this.redeemScript, this.provider.network, this.addressType, true);
47
+ this.bytecode = binToHex(scriptToBytecode(this.redeemScript));
51
48
  this.bytesize = calculateBytesize(this.redeemScript);
52
49
  this.opcount = countOpcodes(this.redeemScript);
53
50
  }
54
- getBalance() {
55
- return __awaiter(this, void 0, void 0, function* () {
56
- const utxos = yield this.getUtxos();
57
- return utxos.reduce((acc, utxo) => acc + utxo.satoshis, 0);
58
- });
51
+ async getBalance() {
52
+ const utxos = await this.getUtxos();
53
+ return utxos.reduce((acc, utxo) => acc + utxo.satoshis, 0n);
59
54
  }
60
- getUtxos() {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- return this.provider.getUtxos(this.address);
63
- });
64
- }
65
- getRedeemScriptHex() {
66
- return binToHex(scriptToBytecode(this.redeemScript));
55
+ async getUtxos() {
56
+ return this.provider.getUtxos(this.address);
67
57
  }
68
58
  createFunction(abiFunction, selector) {
69
59
  return (...args) => {
@@ -3,7 +3,10 @@ export declare class TypeError extends Error {
3
3
  constructor(actual: string, expected: Type);
4
4
  }
5
5
  export declare class OutputSatoshisTooSmallError extends Error {
6
- constructor(satoshis: number);
6
+ constructor(satoshis: bigint, minimumAmount: bigint);
7
+ }
8
+ export declare class TokensToNonTokenAddressError extends Error {
9
+ constructor(address: string);
7
10
  }
8
11
  export declare class FailedTransactionError extends Error {
9
12
  reason: string;
@@ -1,12 +1,16 @@
1
- import { DUST_LIMIT } from './constants.js';
2
1
  export class TypeError extends Error {
3
2
  constructor(actual, expected) {
4
3
  super(`Found type '${actual}' where type '${expected.toString()}' was expected`);
5
4
  }
6
5
  }
7
6
  export class OutputSatoshisTooSmallError extends Error {
8
- constructor(satoshis) {
9
- super(`Tried to add an output with ${satoshis} satoshis, which is less than the DUST limit (${DUST_LIMIT})`);
7
+ constructor(satoshis, minimumAmount) {
8
+ super(`Tried to add an output with ${satoshis} satoshis, which is less than the required minimum for this output-type (${minimumAmount})`);
9
+ }
10
+ }
11
+ export class TokensToNonTokenAddressError extends Error {
12
+ constructor(address) {
13
+ super(`Tried to send tokens to an address without token support, ${address}.`);
10
14
  }
11
15
  }
12
16
  export class FailedTransactionError extends Error {
@@ -1,13 +1,12 @@
1
- import { Secp256k1 } from '@bitauth/libauth';
2
1
  import { HashType, SignatureAlgorithm } from './interfaces.js';
3
2
  export default class SignatureTemplate {
4
3
  private hashtype;
5
4
  private signatureAlgorithm;
6
5
  private privateKey;
7
6
  constructor(signer: Keypair | Uint8Array | string, hashtype?: HashType, signatureAlgorithm?: SignatureAlgorithm);
8
- generateSignature(payload: Uint8Array, secp256k1: Secp256k1, bchForkId?: boolean): Uint8Array;
7
+ generateSignature(payload: Uint8Array, bchForkId?: boolean): Uint8Array;
9
8
  getHashType(bchForkId?: boolean): number;
10
- getPublicKey(secp256k1: Secp256k1): Uint8Array;
9
+ getPublicKey(): Uint8Array;
11
10
  }
12
11
  interface Keypair {
13
12
  toWIF(): string;
@@ -1,5 +1,4 @@
1
- import { decodePrivateKeyWif, SigningSerializationFlag } from '@bitauth/libauth';
2
- import { sha256 } from '@cashscript/utils';
1
+ import { decodePrivateKeyWif, secp256k1, SigningSerializationFlag } from '@bitauth/libauth';
3
2
  import { HashType, SignatureAlgorithm } from './interfaces.js';
4
3
  export default class SignatureTemplate {
5
4
  constructor(signer, hashtype = HashType.SIGHASH_ALL, signatureAlgorithm = SignatureAlgorithm.SCHNORR) {
@@ -16,7 +15,7 @@ export default class SignatureTemplate {
16
15
  this.privateKey = signer;
17
16
  }
18
17
  }
19
- generateSignature(payload, secp256k1, bchForkId) {
18
+ generateSignature(payload, bchForkId) {
20
19
  const signature = this.signatureAlgorithm === SignatureAlgorithm.SCHNORR
21
20
  ? secp256k1.signMessageHashSchnorr(this.privateKey, payload)
22
21
  : secp256k1.signMessageHashDER(this.privateKey, payload);
@@ -25,7 +24,7 @@ export default class SignatureTemplate {
25
24
  getHashType(bchForkId = true) {
26
25
  return bchForkId ? (this.hashtype | SigningSerializationFlag.forkId) : this.hashtype;
27
26
  }
28
- getPublicKey(secp256k1) {
27
+ getPublicKey() {
29
28
  return secp256k1.derivePublicKeyCompressed(this.privateKey);
30
29
  }
31
30
  }
@@ -33,7 +32,7 @@ function isKeypair(obj) {
33
32
  return typeof obj.toWIF === 'function';
34
33
  }
35
34
  function decodeWif(wif) {
36
- const result = decodePrivateKeyWif({ hash: sha256 }, wif);
35
+ const result = decodePrivateKeyWif(wif);
37
36
  if (typeof result === 'string') {
38
37
  throw new Error(result);
39
38
  }
@@ -1,5 +1,5 @@
1
1
  import { AbiFunction, Script } from '@cashscript/utils';
2
- import { Utxo, Recipient, TransactionDetails } from './interfaces.js';
2
+ import { Utxo, Recipient, TokenDetails, TransactionDetails } from './interfaces.js';
3
3
  import NetworkProvider from './network/NetworkProvider.js';
4
4
  import SignatureTemplate from './SignatureTemplate.js';
5
5
  export declare class Transaction {
@@ -13,23 +13,25 @@ export declare class Transaction {
13
13
  private outputs;
14
14
  private sequence;
15
15
  private locktime;
16
- private hardcodedFee;
17
16
  private feePerByte;
17
+ private hardcodedFee;
18
18
  private minChange;
19
+ private tokenChange;
19
20
  constructor(address: string, provider: NetworkProvider, redeemScript: Script, abiFunction: AbiFunction, args: (Uint8Array | SignatureTemplate)[], selector?: number | undefined);
20
21
  from(input: Utxo): this;
21
22
  from(inputs: Utxo[]): this;
22
23
  experimentalFromP2PKH(input: Utxo, template: SignatureTemplate): this;
23
24
  experimentalFromP2PKH(inputs: Utxo[], template: SignatureTemplate): this;
24
- to(to: string, amount: number): this;
25
+ to(to: string, amount: bigint, token?: TokenDetails): this;
25
26
  to(outputs: Recipient[]): this;
26
27
  withOpReturn(chunks: string[]): this;
27
28
  withAge(age: number): this;
28
29
  withTime(time: number): this;
29
- withHardcodedFee(hardcodedFee: number): this;
30
+ withHardcodedFee(hardcodedFee: bigint): this;
30
31
  withFeePerByte(feePerByte: number): this;
31
- withMinChange(minChange: number): this;
32
+ withMinChange(minChange: bigint): this;
32
33
  withoutChange(): this;
34
+ withoutTokenChange(): this;
33
35
  build(): Promise<string>;
34
36
  send(): Promise<TransactionDetails>;
35
37
  send(raw: true): Promise<string>;
@@ -0,0 +1,352 @@
1
+ import { hexToBin, binToHex, encodeTransaction, addressContentsToLockingBytecode, decodeTransaction, LockingBytecodeType, } from '@bitauth/libauth';
2
+ import delay from 'delay';
3
+ import { hash160, hash256, placeholder, scriptToBytecode, } from '@cashscript/utils';
4
+ import { isSignableUtxo, } from './interfaces.js';
5
+ import { meep, createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, buildError, createSighashPreimage, validateRecipient, utxoComparator, cashScriptOutputToLibauthOutput, calculateDust, getOutputSize, } from './utils.js';
6
+ import SignatureTemplate from './SignatureTemplate.js';
7
+ const bip68 = await import('bip68');
8
+ export class Transaction {
9
+ constructor(address, provider, redeemScript, abiFunction, args, selector) {
10
+ this.address = address;
11
+ this.provider = provider;
12
+ this.redeemScript = redeemScript;
13
+ this.abiFunction = abiFunction;
14
+ this.args = args;
15
+ this.selector = selector;
16
+ this.inputs = [];
17
+ this.outputs = [];
18
+ this.sequence = 0xfffffffe;
19
+ this.feePerByte = 1.0;
20
+ this.minChange = 0n;
21
+ this.tokenChange = true;
22
+ }
23
+ from(inputOrInputs) {
24
+ if (!Array.isArray(inputOrInputs)) {
25
+ inputOrInputs = [inputOrInputs];
26
+ }
27
+ this.inputs = this.inputs.concat(inputOrInputs);
28
+ return this;
29
+ }
30
+ experimentalFromP2PKH(inputOrInputs, template) {
31
+ if (!Array.isArray(inputOrInputs)) {
32
+ inputOrInputs = [inputOrInputs];
33
+ }
34
+ inputOrInputs = inputOrInputs.map((input) => ({ ...input, template }));
35
+ this.inputs = this.inputs.concat(inputOrInputs);
36
+ return this;
37
+ }
38
+ to(toOrOutputs, amount, token) {
39
+ if (typeof toOrOutputs === 'string' && typeof amount === 'bigint') {
40
+ const recipient = { to: toOrOutputs, amount, token };
41
+ return this.to([recipient]);
42
+ }
43
+ if (Array.isArray(toOrOutputs) && amount === undefined) {
44
+ toOrOutputs.forEach(validateRecipient);
45
+ this.outputs = this.outputs.concat(toOrOutputs);
46
+ return this;
47
+ }
48
+ throw new Error('Incorrect arguments passed to function \'to\'');
49
+ }
50
+ withOpReturn(chunks) {
51
+ this.outputs.push(createOpReturnOutput(chunks));
52
+ return this;
53
+ }
54
+ withAge(age) {
55
+ this.sequence = bip68.encode({ blocks: age });
56
+ return this;
57
+ }
58
+ withTime(time) {
59
+ this.locktime = time;
60
+ return this;
61
+ }
62
+ withHardcodedFee(hardcodedFee) {
63
+ this.hardcodedFee = hardcodedFee;
64
+ return this;
65
+ }
66
+ withFeePerByte(feePerByte) {
67
+ this.feePerByte = feePerByte;
68
+ return this;
69
+ }
70
+ withMinChange(minChange) {
71
+ this.minChange = minChange;
72
+ return this;
73
+ }
74
+ withoutChange() {
75
+ return this.withMinChange(BigInt(Number.MAX_VALUE));
76
+ }
77
+ withoutTokenChange() {
78
+ this.tokenChange = false;
79
+ return this;
80
+ }
81
+ async build() {
82
+ this.locktime = this.locktime ?? await this.provider.getBlockHeight();
83
+ await this.setInputsAndOutputs();
84
+ const bytecode = scriptToBytecode(this.redeemScript);
85
+ const inputs = this.inputs.map((utxo) => ({
86
+ outpointIndex: utxo.vout,
87
+ outpointTransactionHash: hexToBin(utxo.txid),
88
+ sequenceNumber: this.sequence,
89
+ unlockingBytecode: new Uint8Array(),
90
+ }));
91
+ const outputs = this.outputs.map(cashScriptOutputToLibauthOutput);
92
+ const transaction = {
93
+ inputs,
94
+ locktime: this.locktime,
95
+ outputs,
96
+ version: 2,
97
+ };
98
+ const inputScripts = [];
99
+ this.inputs.forEach((utxo, i) => {
100
+ // UTXO's with signature templates are signed using P2PKH
101
+ if (isSignableUtxo(utxo)) {
102
+ const pubkey = utxo.template.getPublicKey();
103
+ const pubkeyHash = hash160(pubkey);
104
+ const addressContents = { payload: pubkeyHash, type: LockingBytecodeType.p2pkh };
105
+ const prevOutScript = addressContentsToLockingBytecode(addressContents);
106
+ const hashtype = utxo.template.getHashType();
107
+ const preimage = createSighashPreimage(transaction, this.inputs, i, prevOutScript, hashtype);
108
+ const sighash = hash256(preimage);
109
+ const signature = utxo.template.generateSignature(sighash);
110
+ const inputScript = scriptToBytecode([signature, pubkey]);
111
+ inputScripts.push(inputScript);
112
+ return;
113
+ }
114
+ let covenantHashType = -1;
115
+ const completeArgs = this.args.map((arg) => {
116
+ if (!(arg instanceof SignatureTemplate))
117
+ return arg;
118
+ // First signature is used for sighash preimage (maybe not the best way)
119
+ if (covenantHashType < 0)
120
+ covenantHashType = arg.getHashType();
121
+ const preimage = createSighashPreimage(transaction, this.inputs, i, bytecode, arg.getHashType());
122
+ const sighash = hash256(preimage);
123
+ return arg.generateSignature(sighash);
124
+ });
125
+ const preimage = this.abiFunction.covenant
126
+ ? createSighashPreimage(transaction, this.inputs, i, bytecode, covenantHashType)
127
+ : undefined;
128
+ const inputScript = createInputScript(this.redeemScript, completeArgs, this.selector, preimage);
129
+ inputScripts.push(inputScript);
130
+ });
131
+ inputScripts.forEach((script, i) => {
132
+ transaction.inputs[i].unlockingBytecode = script;
133
+ });
134
+ return binToHex(encodeTransaction(transaction));
135
+ }
136
+ async send(raw) {
137
+ const tx = await this.build();
138
+ try {
139
+ const txid = await this.provider.sendRawTransaction(tx);
140
+ return raw ? await this.getTxDetails(txid, raw) : await this.getTxDetails(txid);
141
+ }
142
+ catch (e) {
143
+ const reason = e.error ?? e.message;
144
+ throw buildError(reason, meep(tx, this.inputs, this.redeemScript));
145
+ }
146
+ }
147
+ async getTxDetails(txid, raw) {
148
+ for (let retries = 0; retries < 1200; retries += 1) {
149
+ await delay(500);
150
+ try {
151
+ const hex = await this.provider.getRawTransaction(txid);
152
+ if (raw)
153
+ return hex;
154
+ const libauthTransaction = decodeTransaction(hexToBin(hex));
155
+ return { ...libauthTransaction, txid, hex };
156
+ }
157
+ catch (ignored) {
158
+ // ignored
159
+ }
160
+ }
161
+ // Should not happen
162
+ throw new Error('Could not retrieve transaction details for over 10 minutes');
163
+ }
164
+ async meep() {
165
+ const tx = await this.build();
166
+ return meep(tx, this.inputs, this.redeemScript);
167
+ }
168
+ async setInputsAndOutputs() {
169
+ if (this.outputs.length === 0) {
170
+ throw Error('Attempted to build a transaction without outputs');
171
+ }
172
+ // Construct object with total output of fungible tokens by tokenId
173
+ const netBalanceTokens = {};
174
+ // Construct list with all nfts in inputs
175
+ const listNftsInputs = [];
176
+ // If inputs are manually selected, add their tokens to balance
177
+ for (const input of this.inputs) {
178
+ if (!input.token)
179
+ continue;
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
+ }
187
+ if (input.token.nft) {
188
+ listNftsInputs.push({ ...input.token.nft, category: input.token.category });
189
+ }
190
+ }
191
+ // Construct list with all nfts in outputs
192
+ let listNftsOutputs = [];
193
+ // Subtract all token outputs from the token balances
194
+ for (const output of this.outputs) {
195
+ if (!output.token)
196
+ continue;
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
+ }
204
+ if (output.token.nft) {
205
+ listNftsOutputs.push({ ...output.token.nft, category: output.token.category });
206
+ }
207
+ }
208
+ // If inputs are manually provided, check token balances
209
+ 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
+ // Compare nfts in- and outputs, check if inputs have nfts corresponding to outputs
226
+ // Keep list of nfts in inputs without matching output
227
+ // First check immutable nfts, then mutable & minting nfts together
228
+ // this is so the mutable nft in input does not get match to an output nft corresponding to an immutable nft in the inputs
229
+ let unusedNfts = listNftsInputs;
230
+ for (const nftInput of listNftsInputs) {
231
+ if (nftInput.capability === 'none') {
232
+ for (let i = 0; i < listNftsOutputs.length; i++) {
233
+ // Deep equality check token objects
234
+ if (JSON.stringify(listNftsOutputs[i]) === JSON.stringify(nftInput)) {
235
+ listNftsOutputs.splice(i, 1);
236
+ unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
237
+ break;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ for (const nftInput of listNftsInputs) {
243
+ if (nftInput.capability === 'minting') {
244
+ const newListNftsOutputs = listNftsOutputs.filter((nftOutput) => nftOutput.category !== nftInput.category);
245
+ if (newListNftsOutputs !== listNftsOutputs) {
246
+ unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
247
+ listNftsOutputs = newListNftsOutputs;
248
+ }
249
+ }
250
+ if (nftInput.capability === 'mutable') {
251
+ for (let i = 0; i < listNftsOutputs.length; i++) {
252
+ if (listNftsOutputs[i].category === nftInput.category) {
253
+ listNftsOutputs.splice(i, 1);
254
+ unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
255
+ break;
256
+ }
257
+ }
258
+ }
259
+ }
260
+ if (listNftsOutputs.length !== 0) {
261
+ throw new Error('Nfts in outputs don\'t have corresponding nfts in inputs!');
262
+ }
263
+ if (this.tokenChange) {
264
+ for (const unusedNft of unusedNfts) {
265
+ const tokenDetails = {
266
+ category: unusedNft.category,
267
+ amount: BigInt(0),
268
+ nft: {
269
+ capability: unusedNft.capability,
270
+ commitment: unusedNft.commitment,
271
+ },
272
+ };
273
+ const nftChangeOutput = { to: this.address, amount: BigInt(1000), token: tokenDetails };
274
+ this.outputs.push(nftChangeOutput);
275
+ }
276
+ }
277
+ }
278
+ // Replace all SignatureTemplate with 65-length placeholder Uint8Arrays
279
+ const placeholderArgs = this.args.map((arg) => (arg instanceof SignatureTemplate ? placeholder(65) : arg));
280
+ // Create a placeholder preimage of the correct size
281
+ const placeholderPreimage = this.abiFunction.covenant
282
+ ? placeholder(getPreimageSize(scriptToBytecode(this.redeemScript)))
283
+ : undefined;
284
+ // Create a placeholder input script for size calculation using the placeholder
285
+ // arguments and correctly sized placeholder preimage
286
+ const placeholderScript = createInputScript(this.redeemScript, placeholderArgs, this.selector, placeholderPreimage);
287
+ // Add one extra byte per input to over-estimate tx-in count
288
+ const inputSize = getInputSize(placeholderScript) + 1;
289
+ // Note that we use the addPrecision function to add "decimal points" to BigInt numbers
290
+ // Calculate amount to send and base fee (excluding additional fees per UTXO)
291
+ let amount = addPrecision(this.outputs.reduce((acc, output) => acc + output.amount, 0n));
292
+ let fee = addPrecision(this.hardcodedFee ?? getTxSizeWithoutInputs(this.outputs) * this.feePerByte);
293
+ // Select and gather UTXOs and calculate fees and available funds
294
+ let satsAvailable = 0n;
295
+ if (this.inputs.length > 0) {
296
+ // If inputs are already defined, the user provided the UTXOs and we perform no further UTXO selection
297
+ if (!this.hardcodedFee)
298
+ fee += addPrecision(this.inputs.length * inputSize * this.feePerByte);
299
+ satsAvailable = addPrecision(this.inputs.reduce((acc, input) => acc + input.satoshis, 0n));
300
+ }
301
+ else {
302
+ // If inputs are not defined yet, we retrieve the contract's UTXOs and perform selection
303
+ const utxos = await this.provider.getUtxos(this.address);
304
+ // We sort the UTXOs mainly so there is consistent behaviour between network providers
305
+ // even if they report UTXOs in a different order
306
+ utxos.sort(utxoComparator).reverse();
307
+ for (const utxo of utxos) {
308
+ this.inputs.push(utxo);
309
+ satsAvailable += addPrecision(utxo.satoshis);
310
+ if (!this.hardcodedFee)
311
+ fee += addPrecision(inputSize * this.feePerByte);
312
+ if (satsAvailable > amount + fee)
313
+ break;
314
+ }
315
+ }
316
+ // Remove "decimal points" from BigInt numbers (rounding up for fee, down for others)
317
+ satsAvailable = removePrecisionFloor(satsAvailable);
318
+ amount = removePrecisionFloor(amount);
319
+ fee = removePrecisionCeil(fee);
320
+ // Calculate change and check available funds
321
+ let change = satsAvailable - amount - fee;
322
+ if (change < 0) {
323
+ throw new Error(`Insufficient funds: available (${satsAvailable}) < needed (${amount + fee}).`);
324
+ }
325
+ // Account for the fee of adding a change output
326
+ if (!this.hardcodedFee) {
327
+ const changeOutputSize = getOutputSize({ to: this.address, amount: 0n });
328
+ change -= BigInt(changeOutputSize * this.feePerByte);
329
+ }
330
+ // Add a change output if applicable
331
+ const changeOutput = { to: this.address, amount: change };
332
+ if (change >= this.minChange && change >= calculateDust(changeOutput)) {
333
+ this.outputs.push(changeOutput);
334
+ }
335
+ }
336
+ }
337
+ // Note: the below is a very simple implementation of a "decimal point" system for BigInt numbers
338
+ // It is safe to use for UTXO fee calculations due to its low numbers, but should not be used for other purposes
339
+ // Also note that multiplication and division between two "decimal" bigints is not supported
340
+ // High precision may not work with some 'number' inputs, so we set the default to 6 "decimal places"
341
+ const addPrecision = (amount, precision = 6) => {
342
+ if (typeof amount === 'number') {
343
+ return BigInt(Math.ceil(amount * 10 ** precision));
344
+ }
345
+ return amount * BigInt(10 ** precision);
346
+ };
347
+ const removePrecisionFloor = (amount, precision = 6) => (amount / (10n ** BigInt(precision)));
348
+ const removePrecisionCeil = (amount, precision = 6) => {
349
+ const multiplier = 10n ** BigInt(precision);
350
+ return (amount + multiplier - 1n) / multiplier;
351
+ };
352
+ //# sourceMappingURL=Transaction.js.map
@@ -0,0 +1,2 @@
1
+ export declare const VERSION_SIZE = 4;
2
+ export declare const LOCKTIME_SIZE = 4;
@@ -0,0 +1,3 @@
1
+ export const VERSION_SIZE = 4;
2
+ export const LOCKTIME_SIZE = 4;
3
+ //# sourceMappingURL=constants.js.map
@@ -7,4 +7,4 @@ export { Artifact, AbiFunction, AbiInput } from '@cashscript/utils';
7
7
  export * as utils from '@cashscript/utils';
8
8
  export { Utxo, Recipient, SignatureAlgorithm, HashType, Network, } from './interfaces.js';
9
9
  export * from './Errors.js';
10
- export { NetworkProvider, BitboxNetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
10
+ export { NetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
@@ -2,9 +2,8 @@ import SignatureTemplate from './SignatureTemplate.js';
2
2
  export { SignatureTemplate };
3
3
  export { Contract } from './Contract.js';
4
4
  export { Transaction } from './Transaction.js';
5
- import * as utils_1 from '@cashscript/utils';
6
- export { utils_1 as utils };
5
+ export * as utils from '@cashscript/utils';
7
6
  export { SignatureAlgorithm, HashType, Network, } from './interfaces.js';
8
7
  export * from './Errors.js';
9
- export { BitboxNetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
8
+ export { BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, } from './network/index.js';
10
9
  //# sourceMappingURL=index.js.map