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.
- package/README.md +2 -6
- package/dist/{main/Argument.d.ts → Argument.d.ts} +1 -1
- package/dist/{module/Argument.js → Argument.js} +1 -1
- package/dist/{module/Contract.d.ts → Contract.d.ts} +8 -6
- package/dist/{module/Contract.js → Contract.js} +15 -25
- package/dist/{main/Errors.d.ts → Errors.d.ts} +4 -1
- package/dist/{module/Errors.js → Errors.js} +7 -3
- package/dist/{main/SignatureTemplate.d.ts → SignatureTemplate.d.ts} +2 -3
- package/dist/{module/SignatureTemplate.js → SignatureTemplate.js} +4 -5
- package/dist/{module/Transaction.d.ts → Transaction.d.ts} +7 -5
- package/dist/Transaction.js +352 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -0
- package/dist/{main/index.d.ts → index.d.ts} +1 -1
- package/dist/{module/index.js → index.js} +2 -3
- package/dist/{module/interfaces.d.ts → interfaces.d.ts} +33 -6
- package/dist/{module/interfaces.js → interfaces.js} +0 -2
- package/dist/{module/network → network}/BitcoinRpcNetworkProvider.d.ts +2 -3
- package/dist/network/BitcoinRpcNetworkProvider.js +29 -0
- package/dist/{module/network → network}/ElectrumNetworkProvider.js +53 -72
- package/dist/network/FullStackNetworkProvider.js +33 -0
- package/dist/{main/network → network}/index.d.ts +0 -1
- package/dist/{module/network → network}/index.js +0 -1
- package/dist/{main/utils.d.ts → utils.d.ts} +10 -6
- package/dist/{module/utils.js → utils.js} +90 -46
- package/package.json +18 -16
- package/dist/main/Argument.js +0 -60
- package/dist/main/Contract.d.ts +0 -23
- package/dist/main/Contract.js +0 -87
- package/dist/main/Errors.js +0 -84
- package/dist/main/SignatureTemplate.js +0 -45
- package/dist/main/Transaction.d.ts +0 -39
- package/dist/main/Transaction.js +0 -258
- package/dist/main/constants.d.ts +0 -5
- package/dist/main/constants.js +0 -9
- package/dist/main/index.js +0 -50
- package/dist/main/interfaces.d.ts +0 -43
- package/dist/main/interfaces.js +0 -32
- package/dist/main/network/BitboxNetworkProvider.d.ts +0 -26
- package/dist/main/network/BitboxNetworkProvider.js +0 -40
- package/dist/main/network/BitcoinRpcNetworkProvider.d.ts +0 -41
- package/dist/main/network/BitcoinRpcNetworkProvider.js +0 -49
- package/dist/main/network/ElectrumNetworkProvider.js +0 -162
- package/dist/main/network/FullStackNetworkProvider.js +0 -55
- package/dist/main/network/NetworkProvider.js +0 -3
- package/dist/main/network/index.js +0 -15
- package/dist/main/utils.js +0 -205
- package/dist/module/Argument.d.ts +0 -3
- package/dist/module/Errors.d.ts +0 -66
- package/dist/module/SignatureTemplate.d.ts +0 -15
- package/dist/module/Transaction.js +0 -251
- package/dist/module/constants.d.ts +0 -5
- package/dist/module/constants.js +0 -6
- package/dist/module/index.d.ts +0 -10
- package/dist/module/network/BitboxNetworkProvider.d.ts +0 -26
- package/dist/module/network/BitboxNetworkProvider.js +0 -37
- package/dist/module/network/BitcoinRpcNetworkProvider.js +0 -46
- package/dist/module/network/ElectrumNetworkProvider.d.ts +0 -19
- package/dist/module/network/FullStackNetworkProvider.d.ts +0 -40
- package/dist/module/network/FullStackNetworkProvider.js +0 -52
- package/dist/module/network/NetworkProvider.d.ts +0 -31
- package/dist/module/network/index.d.ts +0 -5
- package/dist/module/utils.d.ts +0 -26
- /package/dist/{main/network → network}/ElectrumNetworkProvider.d.ts +0 -0
- /package/dist/{main/network → network}/FullStackNetworkProvider.d.ts +0 -0
- /package/dist/{main/network → network}/NetworkProvider.d.ts +0 -0
- /package/dist/{module/network → network}/NetworkProvider.js +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Transaction } from '@bitauth/libauth';
|
|
1
|
+
import type { Transaction } from '@bitauth/libauth';
|
|
2
|
+
import type { NetworkProvider } from './network/index.js';
|
|
2
3
|
import type SignatureTemplate from './SignatureTemplate.js';
|
|
3
4
|
export interface Utxo {
|
|
4
5
|
txid: string;
|
|
5
6
|
vout: number;
|
|
6
|
-
satoshis:
|
|
7
|
+
satoshis: bigint;
|
|
8
|
+
token?: TokenDetails;
|
|
7
9
|
}
|
|
8
10
|
export interface SignableUtxo extends Utxo {
|
|
9
11
|
template: SignatureTemplate;
|
|
@@ -11,11 +13,34 @@ export interface SignableUtxo extends Utxo {
|
|
|
11
13
|
export declare function isSignableUtxo(utxo: Utxo): utxo is SignableUtxo;
|
|
12
14
|
export interface Recipient {
|
|
13
15
|
to: string;
|
|
14
|
-
amount:
|
|
16
|
+
amount: bigint;
|
|
17
|
+
token?: TokenDetails;
|
|
15
18
|
}
|
|
16
19
|
export interface Output {
|
|
17
20
|
to: string | Uint8Array;
|
|
18
|
-
amount:
|
|
21
|
+
amount: bigint;
|
|
22
|
+
token?: TokenDetails;
|
|
23
|
+
}
|
|
24
|
+
export interface TokenDetails {
|
|
25
|
+
amount: bigint;
|
|
26
|
+
category: string;
|
|
27
|
+
nft?: {
|
|
28
|
+
capability: 'none' | 'mutable' | 'minting';
|
|
29
|
+
commitment: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface LibauthOutput {
|
|
33
|
+
lockingBytecode: Uint8Array;
|
|
34
|
+
valueSatoshis: bigint;
|
|
35
|
+
token?: LibauthTokenDetails;
|
|
36
|
+
}
|
|
37
|
+
export interface LibauthTokenDetails {
|
|
38
|
+
amount: bigint;
|
|
39
|
+
category: Uint8Array;
|
|
40
|
+
nft?: {
|
|
41
|
+
capability: 'none' | 'mutable' | 'minting';
|
|
42
|
+
commitment: Uint8Array;
|
|
43
|
+
};
|
|
19
44
|
}
|
|
20
45
|
export declare enum SignatureAlgorithm {
|
|
21
46
|
ECDSA = 0,
|
|
@@ -32,8 +57,6 @@ export declare const Network: {
|
|
|
32
57
|
TESTNET3: "testnet3";
|
|
33
58
|
TESTNET4: "testnet4";
|
|
34
59
|
CHIPNET: "chipnet";
|
|
35
|
-
TESTNET: "testnet";
|
|
36
|
-
STAGING: "staging";
|
|
37
60
|
REGTEST: "regtest";
|
|
38
61
|
};
|
|
39
62
|
export declare type Network = (typeof Network)[keyof typeof Network];
|
|
@@ -41,3 +64,7 @@ export interface TransactionDetails extends Transaction {
|
|
|
41
64
|
txid: string;
|
|
42
65
|
hex: string;
|
|
43
66
|
}
|
|
67
|
+
export interface ContractOptions {
|
|
68
|
+
provider?: NetworkProvider;
|
|
69
|
+
addressType?: 'p2sh20' | 'p2sh32';
|
|
70
|
+
}
|
|
@@ -21,8 +21,6 @@ export const Network = {
|
|
|
21
21
|
TESTNET3: literal('testnet3'),
|
|
22
22
|
TESTNET4: literal('testnet4'),
|
|
23
23
|
CHIPNET: literal('chipnet'),
|
|
24
|
-
TESTNET: literal('testnet'),
|
|
25
|
-
STAGING: literal('staging'),
|
|
26
24
|
REGTEST: literal('regtest'),
|
|
27
25
|
};
|
|
28
26
|
//# sourceMappingURL=interfaces.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Utxo, Network } from '../interfaces.js';
|
|
2
2
|
import NetworkProvider from './NetworkProvider.js';
|
|
3
|
-
declare const RpcClientRetry: any;
|
|
4
3
|
export default class BitcoinRpcNetworkProvider implements NetworkProvider {
|
|
5
4
|
network: Network;
|
|
6
5
|
private rpcClient;
|
|
@@ -9,7 +8,7 @@ export default class BitcoinRpcNetworkProvider implements NetworkProvider {
|
|
|
9
8
|
getBlockHeight(): Promise<number>;
|
|
10
9
|
getRawTransaction(txid: string): Promise<string>;
|
|
11
10
|
sendRawTransaction(txHex: string): Promise<string>;
|
|
12
|
-
getClient():
|
|
11
|
+
getClient(): IRpcClientRetry;
|
|
13
12
|
}
|
|
14
13
|
interface ListUnspentItem {
|
|
15
14
|
txid: string;
|
|
@@ -24,7 +23,7 @@ interface ListUnspentItem {
|
|
|
24
23
|
solvable: boolean;
|
|
25
24
|
safe: boolean;
|
|
26
25
|
}
|
|
27
|
-
interface
|
|
26
|
+
interface IRpcClientRetry {
|
|
28
27
|
constructor(url: string, opts?: object): void;
|
|
29
28
|
listUnspent(minConf?: number, maxConf?: number, addresses?: string[], includeUnsafe?: boolean, queryOptions?: object): Promise<ListUnspentItem[]>;
|
|
30
29
|
getBlockCount(): Promise<number>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { default: RpcClientRetry } = await import('bitcoin-rpc-promise-retry');
|
|
2
|
+
export default class BitcoinRpcNetworkProvider {
|
|
3
|
+
constructor(network, url, opts) {
|
|
4
|
+
this.network = network;
|
|
5
|
+
this.rpcClient = new RpcClientRetry(url, opts);
|
|
6
|
+
}
|
|
7
|
+
async getUtxos(address) {
|
|
8
|
+
const result = await this.rpcClient.listUnspent(0, 9999999, [address]);
|
|
9
|
+
const utxos = result.map((utxo) => ({
|
|
10
|
+
txid: utxo.txid,
|
|
11
|
+
vout: utxo.vout,
|
|
12
|
+
satoshis: BigInt(utxo.amount * 1e8),
|
|
13
|
+
}));
|
|
14
|
+
return utxos;
|
|
15
|
+
}
|
|
16
|
+
async getBlockHeight() {
|
|
17
|
+
return this.rpcClient.getBlockCount();
|
|
18
|
+
}
|
|
19
|
+
async getRawTransaction(txid) {
|
|
20
|
+
return this.rpcClient.getRawTransaction(txid);
|
|
21
|
+
}
|
|
22
|
+
async sendRawTransaction(txHex) {
|
|
23
|
+
return this.rpcClient.sendRawTransaction(txHex);
|
|
24
|
+
}
|
|
25
|
+
getClient() {
|
|
26
|
+
return this.rpcClient;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=BitcoinRpcNetworkProvider.js.map
|
|
@@ -1,12 +1,3 @@
|
|
|
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 { sha256 } from '@cashscript/utils';
|
|
12
3
|
import { ElectrumCluster, ElectrumTransport, ClusterOrder, } from 'electrum-cash';
|
|
@@ -33,7 +24,7 @@ export default class ElectrumNetworkProvider {
|
|
|
33
24
|
this.electrum.addServer('bch.loping.net', 50004, ElectrumTransport.WSS.Scheme, false);
|
|
34
25
|
this.electrum.addServer('electrum.imaginary.cash', 50004, ElectrumTransport.WSS.Scheme, false);
|
|
35
26
|
}
|
|
36
|
-
else if (network === Network.
|
|
27
|
+
else if (network === Network.TESTNET3) {
|
|
37
28
|
// Initialise a 1-of-2 Electrum Cluster with 2 hardcoded servers
|
|
38
29
|
this.electrum = new ElectrumCluster('CashScript Application', '1.4.1', 1, 2, ClusterOrder.PRIORITY);
|
|
39
30
|
this.electrum.addServer('blackie.c3-soft.com', 60004, ElectrumTransport.WSS.Scheme, false);
|
|
@@ -41,7 +32,7 @@ export default class ElectrumNetworkProvider {
|
|
|
41
32
|
// this.electrum.addServer('bch.loping.net', 60004, ElectrumTransport.WSS.Scheme, false);
|
|
42
33
|
// this.electrum.addServer('testnet.imaginary.cash', 50004, ElectrumTransport.WSS.Scheme);
|
|
43
34
|
}
|
|
44
|
-
else if (network === Network.
|
|
35
|
+
else if (network === Network.TESTNET4) {
|
|
45
36
|
this.electrum = new ElectrumCluster('CashScript Application', '1.4.1', 1, 1, ClusterOrder.PRIORITY);
|
|
46
37
|
this.electrum.addServer('testnet4.imaginary.cash', 50004, ElectrumTransport.WSS.Scheme, false);
|
|
47
38
|
}
|
|
@@ -53,74 +44,64 @@ export default class ElectrumNetworkProvider {
|
|
|
53
44
|
throw new Error(`Tried to instantiate an ElectrumNetworkProvider for unsupported network ${network}`);
|
|
54
45
|
}
|
|
55
46
|
}
|
|
56
|
-
getUtxos(address) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
47
|
+
async getUtxos(address) {
|
|
48
|
+
const scripthash = addressToElectrumScriptHash(address);
|
|
49
|
+
const filteringOption = 'include_tokens';
|
|
50
|
+
const result = await this.performRequest('blockchain.scripthash.listunspent', scripthash, filteringOption);
|
|
51
|
+
const utxos = result.map((utxo) => ({
|
|
52
|
+
txid: utxo.tx_hash,
|
|
53
|
+
vout: utxo.tx_pos,
|
|
54
|
+
satoshis: BigInt(utxo.value),
|
|
55
|
+
token: utxo.token_data ? {
|
|
56
|
+
...utxo.token_data,
|
|
57
|
+
amount: BigInt(utxo.token_data.amount),
|
|
58
|
+
} : undefined,
|
|
59
|
+
}));
|
|
60
|
+
return utxos;
|
|
68
61
|
}
|
|
69
|
-
getBlockHeight() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return height;
|
|
73
|
-
});
|
|
62
|
+
async getBlockHeight() {
|
|
63
|
+
const { height } = await this.performRequest('blockchain.headers.subscribe');
|
|
64
|
+
return height;
|
|
74
65
|
}
|
|
75
|
-
getRawTransaction(txid) {
|
|
76
|
-
return
|
|
77
|
-
return yield this.performRequest('blockchain.transaction.get', txid);
|
|
78
|
-
});
|
|
66
|
+
async getRawTransaction(txid) {
|
|
67
|
+
return await this.performRequest('blockchain.transaction.get', txid);
|
|
79
68
|
}
|
|
80
|
-
sendRawTransaction(txHex) {
|
|
81
|
-
return
|
|
82
|
-
return yield this.performRequest('blockchain.transaction.broadcast', txHex);
|
|
83
|
-
});
|
|
69
|
+
async sendRawTransaction(txHex) {
|
|
70
|
+
return await this.performRequest('blockchain.transaction.broadcast', txHex);
|
|
84
71
|
}
|
|
85
|
-
connectCluster() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
});
|
|
72
|
+
async connectCluster() {
|
|
73
|
+
try {
|
|
74
|
+
return await this.electrum.startup();
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
94
79
|
}
|
|
95
|
-
disconnectCluster() {
|
|
96
|
-
return
|
|
97
|
-
return this.electrum.shutdown();
|
|
98
|
-
});
|
|
80
|
+
async disconnectCluster() {
|
|
81
|
+
return this.electrum.shutdown();
|
|
99
82
|
}
|
|
100
|
-
performRequest(name, ...parameters) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
yield this.disconnectCluster();
|
|
117
|
-
}
|
|
83
|
+
async performRequest(name, ...parameters) {
|
|
84
|
+
// Only connect the cluster when no concurrent requests are running
|
|
85
|
+
if (this.shouldConnect()) {
|
|
86
|
+
this.connectCluster();
|
|
87
|
+
}
|
|
88
|
+
this.concurrentRequests += 1;
|
|
89
|
+
await this.electrum.ready();
|
|
90
|
+
let result;
|
|
91
|
+
try {
|
|
92
|
+
result = await this.electrum.request(name, ...parameters);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
// Always disconnect the cluster, also if the request fails
|
|
96
|
+
// as long as no other concurrent requests are running
|
|
97
|
+
if (this.shouldDisconnect()) {
|
|
98
|
+
await this.disconnectCluster();
|
|
118
99
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
100
|
+
}
|
|
101
|
+
this.concurrentRequests -= 1;
|
|
102
|
+
if (result instanceof Error)
|
|
103
|
+
throw result;
|
|
104
|
+
return result;
|
|
124
105
|
}
|
|
125
106
|
shouldConnect() {
|
|
126
107
|
if (this.manualConnectionManagement)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class FullStackNetworkProvider {
|
|
2
|
+
/**
|
|
3
|
+
* @example
|
|
4
|
+
* const BCHJS = require("@psf/bch-js")
|
|
5
|
+
* let bchjs = new BCHJS({
|
|
6
|
+
* restURL: 'https://api.fullstack.cash/v3/',
|
|
7
|
+
* apiToken: 'eyJhbGciO...' // Your JWT token here.
|
|
8
|
+
* })
|
|
9
|
+
*/
|
|
10
|
+
constructor(network, bchjs) {
|
|
11
|
+
this.network = network;
|
|
12
|
+
this.bchjs = bchjs;
|
|
13
|
+
}
|
|
14
|
+
async getUtxos(address) {
|
|
15
|
+
const result = await this.bchjs.Electrumx.utxo(address);
|
|
16
|
+
const utxos = (result.utxos ?? []).map((utxo) => ({
|
|
17
|
+
txid: utxo.tx_hash,
|
|
18
|
+
vout: utxo.tx_pos,
|
|
19
|
+
satoshis: BigInt(utxo.value),
|
|
20
|
+
}));
|
|
21
|
+
return utxos;
|
|
22
|
+
}
|
|
23
|
+
async getBlockHeight() {
|
|
24
|
+
return this.bchjs.Blockchain.getBlockCount();
|
|
25
|
+
}
|
|
26
|
+
async getRawTransaction(txid) {
|
|
27
|
+
return this.bchjs.RawTransactions.getRawTransaction(txid);
|
|
28
|
+
}
|
|
29
|
+
async sendRawTransaction(txHex) {
|
|
30
|
+
return this.bchjs.RawTransactions.sendRawTransaction(txHex);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=FullStackNetworkProvider.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { default as NetworkProvider } from './NetworkProvider.js';
|
|
2
|
-
export { default as BitboxNetworkProvider } from './BitboxNetworkProvider.js';
|
|
3
2
|
export { default as BitcoinRpcNetworkProvider } from './BitcoinRpcNetworkProvider.js';
|
|
4
3
|
export { default as ElectrumNetworkProvider } from './ElectrumNetworkProvider.js';
|
|
5
4
|
export { default as FullStackNetworkProvider } from './FullStackNetworkProvider.js';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { default as BitboxNetworkProvider } from './BitboxNetworkProvider.js';
|
|
2
1
|
export { default as BitcoinRpcNetworkProvider } from './BitcoinRpcNetworkProvider.js';
|
|
3
2
|
export { default as ElectrumNetworkProvider } from './ElectrumNetworkProvider.js';
|
|
4
3
|
export { default as FullStackNetworkProvider } from './FullStackNetworkProvider.js';
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { Transaction } from '@bitauth/libauth';
|
|
2
2
|
import { Script } from '@cashscript/utils';
|
|
3
|
-
import { Utxo, Output, Recipient } from './interfaces.js';
|
|
3
|
+
import { Utxo, Output, Recipient, LibauthOutput } from './interfaces.js';
|
|
4
4
|
import { FailedTransactionError } from './Errors.js';
|
|
5
5
|
export declare function validateRecipient(recipient: Recipient): void;
|
|
6
|
+
export declare function calculateDust(recipient: Recipient): number;
|
|
7
|
+
export declare function getOutputSize(output: Output): number;
|
|
8
|
+
export declare function encodeOutput(output: Output): Uint8Array;
|
|
9
|
+
export declare function cashScriptOutputToLibauthOutput(output: Output): LibauthOutput;
|
|
10
|
+
export declare function libauthOutputToCashScriptOutput(output: LibauthOutput): Output;
|
|
6
11
|
export declare function getInputSize(inputScript: Uint8Array): number;
|
|
7
12
|
export declare function getPreimageSize(script: Uint8Array): number;
|
|
8
13
|
export declare function getTxSizeWithoutInputs(outputs: Output[]): number;
|
|
9
14
|
export declare function createInputScript(redeemScript: Script, encodedArgs: Uint8Array[], selector?: number, preimage?: Uint8Array): Uint8Array;
|
|
10
15
|
export declare function createOpReturnOutput(opReturnData: string[]): Output;
|
|
11
|
-
export declare function createSighashPreimage(transaction: Transaction,
|
|
12
|
-
satoshis: number;
|
|
13
|
-
}, inputIndex: number, coveredBytecode: Uint8Array, hashtype: number): Uint8Array;
|
|
16
|
+
export declare function createSighashPreimage(transaction: Transaction, inputs: Utxo[], inputIndex: number, coveredBytecode: Uint8Array, hashtype: number): Uint8Array;
|
|
14
17
|
export declare function buildError(reason: string, meepStr: string): FailedTransactionError;
|
|
15
18
|
export declare function meep(tx: any, utxos: Utxo[], script: Script): string;
|
|
16
|
-
export declare function scriptToAddress(script: Script, network: string): string;
|
|
17
|
-
export declare function scriptToLockingBytecode(script: Script): Uint8Array;
|
|
19
|
+
export declare function scriptToAddress(script: Script, network: string, addressType: 'p2sh20' | 'p2sh32', tokenSupport: boolean): string;
|
|
20
|
+
export declare function scriptToLockingBytecode(script: Script, addressType: 'p2sh20' | 'p2sh32'): Uint8Array;
|
|
21
|
+
export declare function utxoComparator(a: Utxo, b: Utxo): number;
|
|
18
22
|
/**
|
|
19
23
|
* Helper function to convert an address to a locking script
|
|
20
24
|
*
|
|
@@ -1,13 +1,67 @@
|
|
|
1
|
-
import { cashAddressToLockingBytecode,
|
|
2
|
-
import { encodeInt, hash160, Op, scriptToBytecode,
|
|
1
|
+
import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLockingBytecode, lockingBytecodeToCashAddress, binToHex, generateSigningSerializationBCH, utf8ToBin, hexToBin, flattenBinArray, LockingBytecodeType, encodeTransactionOutput, } from '@bitauth/libauth';
|
|
2
|
+
import { encodeInt, hash160, hash256, Op, scriptToBytecode, } from '@cashscript/utils';
|
|
3
3
|
import { Network, } from './interfaces.js';
|
|
4
|
-
import {
|
|
5
|
-
import { OutputSatoshisTooSmallError, Reason, FailedTransactionError, FailedRequireError, FailedTimeCheckError, FailedSigCheckError, } from './Errors.js';
|
|
4
|
+
import { VERSION_SIZE, LOCKTIME_SIZE } from './constants.js';
|
|
5
|
+
import { OutputSatoshisTooSmallError, TokensToNonTokenAddressError, Reason, FailedTransactionError, FailedRequireError, FailedTimeCheckError, FailedSigCheckError, } from './Errors.js';
|
|
6
6
|
// ////////// PARAMETER VALIDATION ////////////////////////////////////////////
|
|
7
7
|
export function validateRecipient(recipient) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const minimumAmount = calculateDust(recipient);
|
|
9
|
+
if (recipient.amount < minimumAmount) {
|
|
10
|
+
throw new OutputSatoshisTooSmallError(recipient.amount, BigInt(minimumAmount));
|
|
10
11
|
}
|
|
12
|
+
if (recipient.token) {
|
|
13
|
+
if (!isTokenAddress(recipient.to)) {
|
|
14
|
+
throw new TokensToNonTokenAddressError(recipient.to);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function calculateDust(recipient) {
|
|
19
|
+
const outputSize = getOutputSize(recipient);
|
|
20
|
+
// Formula used to calculate the minimum allowed output
|
|
21
|
+
const dustAmount = 444 + outputSize * 3;
|
|
22
|
+
return dustAmount;
|
|
23
|
+
}
|
|
24
|
+
export function getOutputSize(output) {
|
|
25
|
+
const encodedOutput = encodeOutput(output);
|
|
26
|
+
return encodedOutput.byteLength;
|
|
27
|
+
}
|
|
28
|
+
export function encodeOutput(output) {
|
|
29
|
+
return encodeTransactionOutput(cashScriptOutputToLibauthOutput(output));
|
|
30
|
+
}
|
|
31
|
+
export function cashScriptOutputToLibauthOutput(output) {
|
|
32
|
+
return {
|
|
33
|
+
lockingBytecode: typeof output.to === 'string' ? addressToLockScript(output.to) : output.to,
|
|
34
|
+
valueSatoshis: output.amount,
|
|
35
|
+
token: output.token && {
|
|
36
|
+
...output.token,
|
|
37
|
+
category: hexToBin(output.token.category),
|
|
38
|
+
nft: output.token.nft && {
|
|
39
|
+
...output.token.nft,
|
|
40
|
+
commitment: hexToBin(output.token.nft.commitment),
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function libauthOutputToCashScriptOutput(output) {
|
|
46
|
+
return {
|
|
47
|
+
to: output.lockingBytecode,
|
|
48
|
+
amount: output.valueSatoshis,
|
|
49
|
+
token: output.token && {
|
|
50
|
+
...output.token,
|
|
51
|
+
category: binToHex(output.token.category),
|
|
52
|
+
nft: output.token.nft && {
|
|
53
|
+
...output.token.nft,
|
|
54
|
+
commitment: binToHex(output.token.nft.commitment),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function isTokenAddress(address) {
|
|
60
|
+
const result = decodeCashAddress(address);
|
|
61
|
+
if (typeof result === 'string')
|
|
62
|
+
throw new Error(result);
|
|
63
|
+
const supportsTokens = (result.type === 'p2pkhWithTokens' || result.type === 'p2shWithTokens');
|
|
64
|
+
return supportsTokens;
|
|
11
65
|
}
|
|
12
66
|
// ////////// SIZE CALCULATIONS ///////////////////////////////////////////////
|
|
13
67
|
export function getInputSize(inputScript) {
|
|
@@ -36,15 +90,9 @@ export function getTxSizeWithoutInputs(outputs) {
|
|
|
36
90
|
// Script (?)*
|
|
37
91
|
// LockTime (4B)
|
|
38
92
|
let size = VERSION_SIZE + LOCKTIME_SIZE;
|
|
39
|
-
size += outputs.reduce((acc, output) =>
|
|
40
|
-
if (typeof output.to === 'string') {
|
|
41
|
-
return acc + P2PKH_OUTPUT_SIZE;
|
|
42
|
-
}
|
|
43
|
-
// Size of an OP_RETURN output = byteLength + 8 (amount) + 2 (scriptSize)
|
|
44
|
-
return acc + output.to.byteLength + 8 + 2;
|
|
45
|
-
}, 0);
|
|
93
|
+
size += outputs.reduce((acc, output) => acc + getOutputSize(output), 0);
|
|
46
94
|
// Add tx-out count (accounting for a potential change output)
|
|
47
|
-
size += encodeInt(outputs.length + 1).byteLength;
|
|
95
|
+
size += encodeInt(BigInt(outputs.length + 1)).byteLength;
|
|
48
96
|
return size;
|
|
49
97
|
}
|
|
50
98
|
// ////////// BUILD OBJECTS ///////////////////////////////////////////////////
|
|
@@ -54,7 +102,7 @@ export function createInputScript(redeemScript, encodedArgs, selector, preimage)
|
|
|
54
102
|
if (preimage !== undefined)
|
|
55
103
|
unlockScript.push(preimage);
|
|
56
104
|
if (selector !== undefined)
|
|
57
|
-
unlockScript.push(encodeInt(selector));
|
|
105
|
+
unlockScript.push(encodeInt(BigInt(selector)));
|
|
58
106
|
// Create input script and compile it to bytecode
|
|
59
107
|
const inputScript = [...unlockScript, scriptToBytecode(redeemScript)];
|
|
60
108
|
return scriptToBytecode(inputScript);
|
|
@@ -64,35 +112,25 @@ export function createOpReturnOutput(opReturnData) {
|
|
|
64
112
|
Op.OP_RETURN,
|
|
65
113
|
...opReturnData.map((output) => toBin(output)),
|
|
66
114
|
];
|
|
67
|
-
return { to: encodeNullDataScript(script), amount:
|
|
115
|
+
return { to: encodeNullDataScript(script), amount: 0n };
|
|
68
116
|
}
|
|
69
117
|
function toBin(output) {
|
|
70
118
|
const data = output.replace(/^0x/, '');
|
|
71
119
|
const encode = data === output ? utf8ToBin : hexToBin;
|
|
72
120
|
return encode(data);
|
|
73
121
|
}
|
|
74
|
-
export function createSighashPreimage(transaction,
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
coveredBytecode,
|
|
83
|
-
forkId: new Uint8Array([0, 0, 0]),
|
|
84
|
-
locktime: state.locktime,
|
|
85
|
-
outpointIndex: state.outpointIndex,
|
|
86
|
-
outpointTransactionHash: state.outpointTransactionHash,
|
|
87
|
-
outputValue: state.outputValue,
|
|
88
|
-
sequenceNumber: state.sequenceNumber,
|
|
89
|
-
sha256: { hash: sha256 },
|
|
90
|
-
signingSerializationType: new Uint8Array([hashtype]),
|
|
91
|
-
transactionOutpoints: state.transactionOutpoints,
|
|
92
|
-
transactionOutputs: state.transactionOutputs,
|
|
93
|
-
transactionSequenceNumbers: state.transactionSequenceNumbers,
|
|
94
|
-
version: 2,
|
|
122
|
+
export function createSighashPreimage(transaction, inputs, inputIndex, coveredBytecode, hashtype) {
|
|
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);
|
|
95
130
|
});
|
|
131
|
+
const context = { inputIndex, sourceOutputs, transaction };
|
|
132
|
+
const signingSerializationType = new Uint8Array([hashtype]);
|
|
133
|
+
const sighashPreimage = generateSigningSerializationBCH(context, { coveredBytecode, signingSerializationType });
|
|
96
134
|
return sighashPreimage;
|
|
97
135
|
}
|
|
98
136
|
export function buildError(reason, meepStr) {
|
|
@@ -121,21 +159,29 @@ function toRegExp(reasons) {
|
|
|
121
159
|
}
|
|
122
160
|
// ////////// MISC ////////////////////////////////////////////////////////////
|
|
123
161
|
export function meep(tx, utxos, script) {
|
|
124
|
-
const scriptPubkey = binToHex(scriptToLockingBytecode(script));
|
|
162
|
+
const scriptPubkey = binToHex(scriptToLockingBytecode(script, 'p2sh20'));
|
|
125
163
|
return `meep debug --tx=${tx} --idx=0 --amt=${utxos[0].satoshis} --pkscript=${scriptPubkey}`;
|
|
126
164
|
}
|
|
127
|
-
export function scriptToAddress(script, network) {
|
|
128
|
-
const lockingBytecode = scriptToLockingBytecode(script);
|
|
165
|
+
export function scriptToAddress(script, network, addressType, tokenSupport) {
|
|
166
|
+
const lockingBytecode = scriptToLockingBytecode(script, addressType);
|
|
129
167
|
const prefix = getNetworkPrefix(network);
|
|
130
|
-
const address = lockingBytecodeToCashAddress(lockingBytecode, prefix);
|
|
168
|
+
const address = lockingBytecodeToCashAddress(lockingBytecode, prefix, { tokenSupport });
|
|
131
169
|
return address;
|
|
132
170
|
}
|
|
133
|
-
export function scriptToLockingBytecode(script) {
|
|
134
|
-
const
|
|
135
|
-
const
|
|
171
|
+
export function scriptToLockingBytecode(script, addressType) {
|
|
172
|
+
const scriptBytecode = scriptToBytecode(script);
|
|
173
|
+
const scriptHash = (addressType === 'p2sh20') ? hash160(scriptBytecode) : hash256(scriptBytecode);
|
|
174
|
+
const addressContents = { payload: scriptHash, type: LockingBytecodeType[addressType] };
|
|
136
175
|
const lockingBytecode = addressContentsToLockingBytecode(addressContents);
|
|
137
176
|
return lockingBytecode;
|
|
138
177
|
}
|
|
178
|
+
export function utxoComparator(a, b) {
|
|
179
|
+
if (a.satoshis > b.satoshis)
|
|
180
|
+
return 1;
|
|
181
|
+
if (a.satoshis < b.satoshis)
|
|
182
|
+
return -1;
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
139
185
|
/**
|
|
140
186
|
* Helper function to convert an address to a locking script
|
|
141
187
|
*
|
|
@@ -153,9 +199,7 @@ export function getNetworkPrefix(network) {
|
|
|
153
199
|
switch (network) {
|
|
154
200
|
case Network.MAINNET:
|
|
155
201
|
return 'bitcoincash';
|
|
156
|
-
case Network.STAGING:
|
|
157
202
|
case Network.TESTNET4:
|
|
158
|
-
case Network.TESTNET:
|
|
159
203
|
case Network.TESTNET3:
|
|
160
204
|
case Network.CHIPNET:
|
|
161
205
|
return 'bchtest';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-next.1",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -21,39 +21,41 @@
|
|
|
21
21
|
"contributors": [
|
|
22
22
|
"Gabriel Cardona <gabriel@bitcoin.com>"
|
|
23
23
|
],
|
|
24
|
-
"main": "dist/
|
|
25
|
-
"
|
|
26
|
-
"
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"type": "module",
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"directories": {
|
|
29
29
|
"lib": "src",
|
|
30
30
|
"test": "test"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "
|
|
33
|
+
"build": "yarn clean && yarn compile",
|
|
34
|
+
"build:test": "yarn clean:test && yarn compile:test",
|
|
34
35
|
"clean": "rm -rf ./dist",
|
|
35
|
-
"
|
|
36
|
-
"compile
|
|
37
|
-
"compile:
|
|
36
|
+
"clean:test": "rm -rf ./dist-test",
|
|
37
|
+
"compile": "tsc -p tsconfig.build.json",
|
|
38
|
+
"compile:test": "tsc -p tsconfig.test.json",
|
|
38
39
|
"lint": "eslint . --ext .ts --ignore-path ../../.eslintignore",
|
|
39
|
-
"prepare": "
|
|
40
|
-
"
|
|
40
|
+
"prepare": "yarn build",
|
|
41
|
+
"pretest": "yarn build:test",
|
|
42
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
|
-
"@bitauth/libauth": "^
|
|
44
|
-
"@cashscript/utils": "^0.
|
|
45
|
+
"@bitauth/libauth": "^2.0.0-alpha.8",
|
|
46
|
+
"@cashscript/utils": "^0.8.0-next.1",
|
|
45
47
|
"bip68": "^1.0.4",
|
|
46
48
|
"bitcoin-rpc-promise-retry": "^1.3.0",
|
|
47
49
|
"delay": "^5.0.0",
|
|
48
50
|
"electrum-cash": "^2.0.10"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
53
|
+
"@jest/globals": "^29.4.1",
|
|
51
54
|
"@psf/bch-js": "^4.15.0",
|
|
52
|
-
"
|
|
55
|
+
"bip39": "^3.0.4",
|
|
53
56
|
"eslint": "^7.20.0",
|
|
54
|
-
"jest": "^
|
|
55
|
-
"ts-jest": "^26.5.1",
|
|
57
|
+
"jest": "^29.4.1",
|
|
56
58
|
"typescript": "^4.1.5"
|
|
57
59
|
},
|
|
58
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "77c621fb963cc8e8951faebae53267d3520b7fe9"
|
|
59
61
|
}
|