cashscript 0.11.2 → 0.11.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.
@@ -1,6 +1,6 @@
1
1
  import { binToHex, decodeTransaction, decodeTransactionUnsafe, encodeTransaction, hexToBin, } from '@bitauth/libauth';
2
2
  import delay from 'delay';
3
- import { isUnlockableUtxo, isStandardUnlockableUtxo, } from './interfaces.js';
3
+ import { isUnlockableUtxo, isStandardUnlockableUtxo, isP2PKHUnlocker, } from './interfaces.js';
4
4
  import { cashScriptOutputToLibauthOutput, createOpReturnOutput, generateLibauthSourceOutputs, validateInput, validateOutput, } from './utils.js';
5
5
  import { FailedTransactionError } from './Errors.js';
6
6
  import { getBitauthUri } from './LibauthTemplate.js';
@@ -90,6 +90,10 @@ export class TransactionBuilder {
90
90
  return binToHex(encodeTransaction(transaction));
91
91
  }
92
92
  debug() {
93
+ // do not debug a pure P2PKH-spend transaction
94
+ if (this.inputs.every((input) => isP2PKHUnlocker(input.unlocker))) {
95
+ return {};
96
+ }
93
97
  if (this.inputs.some((input) => !isStandardUnlockableUtxo(input))) {
94
98
  throw new Error('Cannot debug a transaction with custom unlocker');
95
99
  }
@@ -360,7 +360,7 @@ export const getLibauthTemplates = (txn) => {
360
360
  export const debugLibauthTemplate = (template, transaction) => {
361
361
  const allArtifacts = transaction.inputs
362
362
  .map(input => 'contract' in input.unlocker ? input.unlocker.contract : undefined)
363
- .filter((contract) => !!contract)
363
+ .filter((contract) => Boolean(contract))
364
364
  .map(contract => contract.artifact);
365
365
  return debugTemplate(template, allArtifacts);
366
366
  };
@@ -14,7 +14,7 @@ export default class ElectrumNetworkProvider {
14
14
  if ('electrum' in options)
15
15
  return options.electrum;
16
16
  const server = 'hostname' in options ? options.hostname : this.getServerForNetwork(network);
17
- return new ElectrumClient('CashScript Application', '1.4.1', server);
17
+ return new ElectrumClient('CashScript Application', '1.4.1', server, { disableBrowserVisibilityHandling: true });
18
18
  }
19
19
  // Get Electrum server based on network
20
20
  getServerForNetwork(network) {
@@ -1,16 +1,21 @@
1
1
  import { Utxo, Network } from '../interfaces.js';
2
2
  import NetworkProvider from './NetworkProvider.js';
3
+ interface MockNetworkProviderOptions {
4
+ updateUtxoSet: boolean;
5
+ }
3
6
  export default class MockNetworkProvider implements NetworkProvider {
4
- private utxoMap;
7
+ private utxoSet;
5
8
  private transactionMap;
6
9
  network: Network;
7
10
  blockHeight: number;
8
- constructor();
11
+ options: MockNetworkProviderOptions;
12
+ constructor(options?: Partial<MockNetworkProviderOptions>);
9
13
  getUtxos(address: string): Promise<Utxo[]>;
10
14
  setBlockHeight(newBlockHeight: number): void;
11
15
  getBlockHeight(): Promise<number>;
12
16
  getRawTransaction(txid: string): Promise<string>;
13
17
  sendRawTransaction(txHex: string): Promise<string>;
14
- addUtxo(address: string, utxo: Utxo): void;
18
+ addUtxo(addressOrLockingBytecode: string, utxo: Utxo): void;
15
19
  reset(): void;
16
20
  }
21
+ export {};
@@ -1,17 +1,21 @@
1
- import { binToHex, hexToBin } from '@bitauth/libauth';
1
+ import { binToHex, decodeTransactionUnsafe, hexToBin, isHex } from '@bitauth/libauth';
2
2
  import { sha256 } from '@cashscript/utils';
3
3
  import { Network } from '../interfaces.js';
4
- import { addressToLockScript, randomUtxo } from '../utils.js';
4
+ import { addressToLockScript, libauthTokenDetailsToCashScriptTokenDetails, randomUtxo } from '../utils.js';
5
5
  // redeclare the addresses from vars.ts instead of importing them
6
6
  const aliceAddress = 'bchtest:qpgjmwev3spwlwkgmyjrr2s2cvlkkzlewq62mzgjnp';
7
7
  const bobAddress = 'bchtest:qz6q5gqnxdldkr07xpls5474mmzmlesd6qnux4skuc';
8
8
  const carolAddress = 'bchtest:qqsr7nqwe6rq5crj63gy5gdqchpnwmguusmr7tfmsj';
9
+ // We are setting the default updateUtxoSet to 'false' so that it doesn't break the current behaviour
10
+ // TODO: in a future breaking release we want to set this to 'true' by default
9
11
  export default class MockNetworkProvider {
10
- constructor() {
11
- this.utxoMap = {};
12
+ constructor(options) {
13
+ // we use lockingBytecode hex as the key for utxoMap to make cash addresses and token addresses interchangeable
14
+ this.utxoSet = [];
12
15
  this.transactionMap = {};
13
16
  this.network = Network.MOCKNET;
14
17
  this.blockHeight = 133700;
18
+ this.options = { updateUtxoSet: false, ...options };
15
19
  for (let i = 0; i < 3; i += 1) {
16
20
  this.addUtxo(aliceAddress, randomUtxo());
17
21
  this.addUtxo(bobAddress, randomUtxo());
@@ -19,8 +23,8 @@ export default class MockNetworkProvider {
19
23
  }
20
24
  }
21
25
  async getUtxos(address) {
22
- const lockingBytecode = binToHex(addressToLockScript(address));
23
- return this.utxoMap[lockingBytecode] ?? [];
26
+ const addressLockingBytecode = binToHex(addressToLockScript(address));
27
+ return this.utxoSet.filter(([lockingBytecode]) => lockingBytecode === addressLockingBytecode).map(([, utxo]) => utxo);
24
28
  }
25
29
  setBlockHeight(newBlockHeight) {
26
30
  this.blockHeight = newBlockHeight;
@@ -34,18 +38,42 @@ export default class MockNetworkProvider {
34
38
  async sendRawTransaction(txHex) {
35
39
  const transactionBin = hexToBin(txHex);
36
40
  const txid = binToHex(sha256(sha256(transactionBin)).reverse());
41
+ if (this.options.updateUtxoSet && this.transactionMap[txid]) {
42
+ throw new Error(`Transaction with txid ${txid} was already submitted`);
43
+ }
37
44
  this.transactionMap[txid] = txHex;
45
+ // If updateUtxoSet is false, we don't need to update the utxo set, and just return the txid
46
+ if (!this.options.updateUtxoSet)
47
+ return txid;
48
+ const decodedTransaction = decodeTransactionUnsafe(transactionBin);
49
+ decodedTransaction.inputs.forEach((input) => {
50
+ const utxoIndex = this.utxoSet.findIndex(([, utxo]) => utxo.txid === binToHex(input.outpointTransactionHash) && utxo.vout === input.outpointIndex);
51
+ // TODO: we should check what error a BCHN node throws, so we can throw the same error here
52
+ if (utxoIndex === -1) {
53
+ throw new Error(`UTXO not found for input ${input.outpointIndex} of transaction ${txid}`);
54
+ }
55
+ this.utxoSet.splice(utxoIndex, 1);
56
+ });
57
+ decodedTransaction.outputs.forEach((output, vout) => {
58
+ this.addUtxo(binToHex(output.lockingBytecode), {
59
+ txid,
60
+ vout,
61
+ satoshis: output.valueSatoshis,
62
+ token: output.token && libauthTokenDetailsToCashScriptTokenDetails(output.token),
63
+ });
64
+ });
38
65
  return txid;
39
66
  }
40
- addUtxo(address, utxo) {
41
- const lockingBytecode = binToHex(addressToLockScript(address));
42
- if (!this.utxoMap[lockingBytecode]) {
43
- this.utxoMap[lockingBytecode] = [];
44
- }
45
- this.utxoMap[lockingBytecode].push(utxo);
67
+ // Note: the user can technically add the same UTXO multiple times (txid + vout), to the same or different addresses
68
+ // but we don't check for this in the sendRawTransaction method. We might want to prevent duplicates from being added
69
+ // in the first place.
70
+ addUtxo(addressOrLockingBytecode, utxo) {
71
+ const lockingBytecode = isHex(addressOrLockingBytecode) ?
72
+ addressOrLockingBytecode : binToHex(addressToLockScript(addressOrLockingBytecode));
73
+ this.utxoSet.push([lockingBytecode, utxo]);
46
74
  }
47
75
  reset() {
48
- this.utxoMap = {};
76
+ this.utxoSet = [];
49
77
  this.transactionMap = {};
50
78
  }
51
79
  }
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Transaction } from '@bitauth/libauth';
2
2
  import { Script } from '@cashscript/utils';
3
- import { Utxo, Output, LibauthOutput, TokenDetails, AddressType, UnlockableUtxo } from './interfaces.js';
3
+ import { Utxo, Output, LibauthOutput, TokenDetails, AddressType, UnlockableUtxo, LibauthTokenDetails } from './interfaces.js';
4
4
  export declare function validateInput(utxo: Utxo): void;
5
5
  export declare function validateOutput(output: Output): void;
6
6
  export declare function calculateDust(output: Output): number;
@@ -8,6 +8,7 @@ export declare function getOutputSize(output: Output): number;
8
8
  export declare function encodeOutput(output: Output): Uint8Array;
9
9
  export declare function cashScriptOutputToLibauthOutput(output: Output): LibauthOutput;
10
10
  export declare function libauthOutputToCashScriptOutput(output: LibauthOutput): Output;
11
+ export declare function libauthTokenDetailsToCashScriptTokenDetails(token: LibauthTokenDetails): TokenDetails;
11
12
  export declare function generateLibauthSourceOutputs(inputs: UnlockableUtxo[]): LibauthOutput[];
12
13
  export declare function getInputSize(inputScript: Uint8Array): number;
13
14
  export declare function getTxSizeWithoutInputs(outputs: Output[]): number;
package/dist/utils.js CHANGED
@@ -64,13 +64,16 @@ export function libauthOutputToCashScriptOutput(output) {
64
64
  return {
65
65
  to: output.lockingBytecode,
66
66
  amount: output.valueSatoshis,
67
- token: output.token && {
68
- ...output.token,
69
- category: binToHex(output.token.category),
70
- nft: output.token.nft && {
71
- ...output.token.nft,
72
- commitment: binToHex(output.token.nft.commitment),
73
- },
67
+ token: output.token && libauthTokenDetailsToCashScriptTokenDetails(output.token),
68
+ };
69
+ }
70
+ export function libauthTokenDetailsToCashScriptTokenDetails(token) {
71
+ return {
72
+ ...token,
73
+ category: binToHex(token.category),
74
+ nft: token.nft && {
75
+ ...token.nft,
76
+ commitment: binToHex(token.nft.commitment),
74
77
  },
75
78
  };
76
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cashscript",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "Easily write and interact with Bitcoin Cash contracts",
5
5
  "keywords": [
6
6
  "bitcoin cash",
@@ -46,13 +46,14 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@bitauth/libauth": "^3.1.0-next.2",
49
- "@cashscript/utils": "^0.11.2",
50
- "@electrum-cash/network": "^4.1.1",
49
+ "@cashscript/utils": "^0.11.4",
50
+ "@electrum-cash/network": "^4.1.3",
51
51
  "@mr-zwets/bchn-api-wrapper": "^1.0.1",
52
+ "@types/node": "^22.17.0",
52
53
  "delay": "^6.0.0",
53
54
  "fast-deep-equal": "^3.1.3",
54
55
  "pako": "^2.1.0",
55
- "semver": "^7.6.3"
56
+ "semver": "^7.7.2"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@jest/globals": "^29.7.0",
@@ -61,7 +62,7 @@
61
62
  "@types/semver": "^7.5.8",
62
63
  "eslint": "^8.54.0",
63
64
  "jest": "^29.7.0",
64
- "typescript": "^5.7.3"
65
+ "typescript": "^5.9.2"
65
66
  },
66
- "gitHead": "81e7dc182ef71909af721d62751ef403bcfcc41e"
67
+ "gitHead": "d878d068b91ddc5a4ca1807376520ae451e4a0ed"
67
68
  }