cashscript 0.13.0-next.4 → 0.13.0-next.6
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/TransactionBuilder.d.ts +5 -3
- package/dist/TransactionBuilder.js +39 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interfaces.d.ts +4 -0
- package/dist/libauth-template/LibauthTemplate.d.ts +2 -2
- package/dist/libauth-template/LibauthTemplate.js +1 -2
- package/dist/libauth-template/utils.js +1 -1
- package/dist/network/MockNetworkProvider.d.ts +2 -2
- package/dist/network/MockNetworkProvider.js +1 -1
- package/dist/transaction-utils.d.ts +7 -0
- package/dist/transaction-utils.js +36 -0
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Unlocker, Output, TransactionDetails, UnlockableUtxo, Utxo, InputOptions, VmResourceUsage } from './interfaces.js';
|
|
1
|
+
import { WalletTemplate } from '@bitauth/libauth';
|
|
2
|
+
import { Unlocker, Output, TransactionDetails, UnlockableUtxo, Utxo, InputOptions, VmResourceUsage, BchChangeOutputOptions } from './interfaces.js';
|
|
3
3
|
import { NetworkProvider } from './network/index.js';
|
|
4
4
|
import { DebugResults } from './debugging.js';
|
|
5
5
|
import { WcTransactionOptions } from './walletconnect-utils.js';
|
|
@@ -23,10 +23,12 @@ export declare class TransactionBuilder {
|
|
|
23
23
|
addOutput(output: Output): this;
|
|
24
24
|
addOutputs(outputs: Output[]): this;
|
|
25
25
|
addOpReturnOutput(chunks: string[]): this;
|
|
26
|
+
addBchChangeOutputIfNeeded(changeOutputOptions: BchChangeOutputOptions): this;
|
|
27
|
+
getTransactionSize(): bigint;
|
|
26
28
|
setLocktime(locktime: number): this;
|
|
27
29
|
private checkMaxFee;
|
|
28
30
|
private checkFungibleTokenBurn;
|
|
29
|
-
buildLibauthTransaction
|
|
31
|
+
private buildLibauthTransaction;
|
|
30
32
|
build(): string;
|
|
31
33
|
debug(): DebugResults;
|
|
32
34
|
getVmResourceUsage(verbose?: boolean): Array<VmResourceUsage>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { binToHex, decodeTransaction, decodeTransactionUnsafe, encodeTransaction, hexToBin, } from '@bitauth/libauth';
|
|
2
2
|
import { isUnlockableUtxo, isStandardUnlockableUtxo, isContractUnlocker, } from './interfaces.js';
|
|
3
|
-
import { cashScriptOutputToLibauthOutput, createOpReturnOutput, delay, generateLibauthSourceOutputs, validateInput, validateOutput, } from './utils.js';
|
|
3
|
+
import { calculateDust, cashScriptOutputToLibauthOutput, createOpReturnOutput, delay, generateLibauthSourceOutputs, getOutputSize, validateInput, validateOutput, } from './utils.js';
|
|
4
4
|
import { FailedTransactionError } from './Errors.js';
|
|
5
5
|
import { debugLibauthTemplate, getLibauthTemplate, getBitauthUri } from './libauth-template/LibauthTemplate.js';
|
|
6
6
|
import { getWcContractInfo } from './walletconnect-utils.js';
|
|
@@ -46,6 +46,35 @@ export class TransactionBuilder {
|
|
|
46
46
|
this.outputs.push(createOpReturnOutput(chunks));
|
|
47
47
|
return this;
|
|
48
48
|
}
|
|
49
|
+
addBchChangeOutputIfNeeded(changeOutputOptions) {
|
|
50
|
+
const totalBchInputAmount = this.inputs.reduce((total, input) => total + input.satoshis, 0n);
|
|
51
|
+
const totalBchOutputAmount = this.outputs.reduce((total, output) => total + output.amount, 0n);
|
|
52
|
+
const tentativeSurplus = totalBchInputAmount - totalBchOutputAmount;
|
|
53
|
+
const tentativeTransactionSize = this.getTransactionSize();
|
|
54
|
+
const tentativeFee = BigInt(Math.ceil(changeOutputOptions.feeRate * Number(tentativeTransactionSize)));
|
|
55
|
+
const tentativeChangeAmount = tentativeSurplus - tentativeFee;
|
|
56
|
+
if (tentativeChangeAmount < 0n) {
|
|
57
|
+
throw new Error(`Transaction does not have enough funds to cover the transaction fee rate of ${changeOutputOptions.feeRate} even without adding a change output. Current surplus: ${tentativeSurplus}, current fee: ${tentativeFee}, current change amount: ${tentativeChangeAmount}`);
|
|
58
|
+
}
|
|
59
|
+
const changeOutputSize = getOutputSize({
|
|
60
|
+
to: changeOutputOptions.to,
|
|
61
|
+
amount: tentativeSurplus,
|
|
62
|
+
});
|
|
63
|
+
const transactionSize = tentativeTransactionSize + BigInt(changeOutputSize);
|
|
64
|
+
const calculatedFee = BigInt(Math.ceil(changeOutputOptions.feeRate * Number(transactionSize)));
|
|
65
|
+
const changeAmount = tentativeSurplus - calculatedFee;
|
|
66
|
+
const changeOutput = { to: changeOutputOptions.to, amount: changeAmount };
|
|
67
|
+
const changeOutputDust = calculateDust(changeOutput);
|
|
68
|
+
if (changeAmount < changeOutputDust) {
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
this.outputs.push(changeOutput);
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
getTransactionSize() {
|
|
75
|
+
const transaction = this.buildLibauthTransaction(true);
|
|
76
|
+
return BigInt(encodeTransaction(transaction).byteLength);
|
|
77
|
+
}
|
|
49
78
|
setLocktime(locktime) {
|
|
50
79
|
this.locktime = locktime;
|
|
51
80
|
return this;
|
|
@@ -87,8 +116,10 @@ export class TransactionBuilder {
|
|
|
87
116
|
}
|
|
88
117
|
}
|
|
89
118
|
}
|
|
90
|
-
buildLibauthTransaction() {
|
|
91
|
-
|
|
119
|
+
buildLibauthTransaction(skipChecks = false) {
|
|
120
|
+
if (!skipChecks) {
|
|
121
|
+
this.checkFungibleTokenBurn();
|
|
122
|
+
}
|
|
92
123
|
const inputs = this.inputs.map((utxo) => ({
|
|
93
124
|
outpointIndex: utxo.vout,
|
|
94
125
|
outpointTransactionHash: hexToBin(utxo.txid),
|
|
@@ -108,7 +139,9 @@ export class TransactionBuilder {
|
|
|
108
139
|
inputScripts.forEach((script, i) => {
|
|
109
140
|
transaction.inputs[i].unlockingBytecode = script;
|
|
110
141
|
});
|
|
111
|
-
|
|
142
|
+
if (!skipChecks) {
|
|
143
|
+
this.checkMaxFee(transaction);
|
|
144
|
+
}
|
|
112
145
|
return transaction;
|
|
113
146
|
}
|
|
114
147
|
build() {
|
|
@@ -163,7 +196,8 @@ export class TransactionBuilder {
|
|
|
163
196
|
return getBitauthUri(this.getLibauthTemplate());
|
|
164
197
|
}
|
|
165
198
|
getLibauthTemplate() {
|
|
166
|
-
|
|
199
|
+
const libauthTransaction = this.buildLibauthTransaction();
|
|
200
|
+
return getLibauthTemplate(this, libauthTransaction);
|
|
167
201
|
}
|
|
168
202
|
async send(raw) {
|
|
169
203
|
const tx = this.build();
|
package/dist/index.d.ts
CHANGED
|
@@ -9,3 +9,4 @@ export * from './Errors.js';
|
|
|
9
9
|
export { type NetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, MockNetworkProvider, } from './network/index.js';
|
|
10
10
|
export { randomUtxo, randomToken, randomNFT } from './utils.js';
|
|
11
11
|
export * from './walletconnect-utils.js';
|
|
12
|
+
export { gatherBchUtxos, gatherFungibleTokenUtxos, type GatherUtxosResult } from './transaction-utils.js';
|
package/dist/index.js
CHANGED
|
@@ -8,4 +8,5 @@ export * from './Errors.js';
|
|
|
8
8
|
export { BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, MockNetworkProvider, } from './network/index.js';
|
|
9
9
|
export { randomUtxo, randomToken, randomNFT } from './utils.js';
|
|
10
10
|
export * from './walletconnect-utils.js';
|
|
11
|
+
export { gatherBchUtxos, gatherFungibleTokenUtxos } from './transaction-utils.js';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WalletTemplate } from '@bitauth/libauth';
|
|
1
|
+
import { TransactionBch, WalletTemplate } from '@bitauth/libauth';
|
|
2
2
|
import { DebugResults } from '../debugging.js';
|
|
3
3
|
import { TransactionBuilder } from '../TransactionBuilder.js';
|
|
4
|
-
export declare const getLibauthTemplate: (transactionBuilder: TransactionBuilder) => WalletTemplate;
|
|
4
|
+
export declare const getLibauthTemplate: (transactionBuilder: TransactionBuilder, libauthTransaction: TransactionBch) => WalletTemplate;
|
|
5
5
|
export declare const debugLibauthTemplate: (template: WalletTemplate, transaction: TransactionBuilder) => DebugResults;
|
|
6
6
|
export declare const getBitauthUri: (template: WalletTemplate) => string;
|
|
@@ -8,11 +8,10 @@ import { zlibSync } from 'fflate';
|
|
|
8
8
|
import MockNetworkProvider from '../network/MockNetworkProvider.js';
|
|
9
9
|
import { addHexPrefixExceptEmpty, DEFAULT_VM_TARGET, formatBytecodeForDebugging, formatParametersForDebugging, getLockScriptName, getSignatureAndPubkeyFromP2PKHInput, getUnlockScriptName, serialiseTokenDetails } from './utils.js';
|
|
10
10
|
// TODO: Add / improve descriptions throughout the template generation
|
|
11
|
-
export const getLibauthTemplate = (transactionBuilder) => {
|
|
11
|
+
export const getLibauthTemplate = (transactionBuilder, libauthTransaction) => {
|
|
12
12
|
if (transactionBuilder.inputs.some((input) => !isStandardUnlockableUtxo(input))) {
|
|
13
13
|
throw new Error('Cannot use debugging functionality with a transaction that contains custom unlockers');
|
|
14
14
|
}
|
|
15
|
-
const libauthTransaction = transactionBuilder.buildLibauthTransaction();
|
|
16
15
|
const vmTarget = transactionBuilder.provider instanceof MockNetworkProvider
|
|
17
16
|
? transactionBuilder.provider.vmTarget
|
|
18
17
|
: DEFAULT_VM_TARGET;
|
|
@@ -64,7 +64,7 @@ export const formatBytecodeForDebugging = (artifact) => {
|
|
|
64
64
|
.map((asmElement) => (isHex(asmElement) ? `<0x${asmElement}>` : asmElement))
|
|
65
65
|
.join('\n');
|
|
66
66
|
}
|
|
67
|
-
return formatBitAuthScript(bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, artifact.source);
|
|
67
|
+
return formatBitAuthScript(bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, artifact.source, artifact.debug.sourceTags);
|
|
68
68
|
};
|
|
69
69
|
export const serialiseTokenDetails = (token) => {
|
|
70
70
|
if (!token)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Utxo, Network, VmTarget } from '../interfaces.js';
|
|
2
2
|
import NetworkProvider from './NetworkProvider.js';
|
|
3
3
|
export interface MockNetworkProviderOptions {
|
|
4
|
-
updateUtxoSet
|
|
4
|
+
updateUtxoSet?: boolean;
|
|
5
5
|
vmTarget?: VmTarget;
|
|
6
6
|
}
|
|
7
7
|
export default class MockNetworkProvider implements NetworkProvider {
|
|
8
8
|
private utxoSet;
|
|
9
9
|
private transactionMap;
|
|
10
|
+
private blockHeight;
|
|
10
11
|
network: Network;
|
|
11
|
-
blockHeight: number;
|
|
12
12
|
options: MockNetworkProviderOptions;
|
|
13
13
|
vmTarget: VmTarget;
|
|
14
14
|
constructor(options?: Partial<MockNetworkProviderOptions>);
|
|
@@ -8,8 +8,8 @@ export default class MockNetworkProvider {
|
|
|
8
8
|
// we use lockingBytecode hex as the key for utxoMap to make cash addresses and token addresses interchangeable
|
|
9
9
|
this.utxoSet = [];
|
|
10
10
|
this.transactionMap = {};
|
|
11
|
-
this.network = Network.MOCKNET;
|
|
12
11
|
this.blockHeight = 133700;
|
|
12
|
+
this.network = Network.MOCKNET;
|
|
13
13
|
this.options = { updateUtxoSet: true, ...options };
|
|
14
14
|
this.vmTarget = this.options.vmTarget ?? DEFAULT_VM_TARGET;
|
|
15
15
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Utxo } from './interfaces.js';
|
|
2
|
+
export interface GatherUtxosResult {
|
|
3
|
+
utxos: Utxo[];
|
|
4
|
+
totalAmount: bigint;
|
|
5
|
+
}
|
|
6
|
+
export declare function gatherBchUtxos(utxos: Utxo[], amount: bigint): GatherUtxosResult;
|
|
7
|
+
export declare function gatherFungibleTokenUtxos(utxos: Utxo[], tokenCategory: string, amount: bigint): GatherUtxosResult;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isFungibleTokenUtxo, isNonTokenUtxo } from './utils.js';
|
|
2
|
+
export function gatherBchUtxos(utxos, amount) {
|
|
3
|
+
const sortedBchUtxos = utxos
|
|
4
|
+
.filter(isNonTokenUtxo)
|
|
5
|
+
.toSorted((a, b) => Number(b.satoshis - a.satoshis));
|
|
6
|
+
const targetUtxos = [];
|
|
7
|
+
let total = 0n;
|
|
8
|
+
for (const utxo of sortedBchUtxos) {
|
|
9
|
+
if (total >= amount)
|
|
10
|
+
break;
|
|
11
|
+
total += utxo.satoshis;
|
|
12
|
+
targetUtxos.push(utxo);
|
|
13
|
+
}
|
|
14
|
+
if (total < amount) {
|
|
15
|
+
throw new Error('Not enough funds to cover the required amount');
|
|
16
|
+
}
|
|
17
|
+
return { utxos: targetUtxos, totalAmount: total };
|
|
18
|
+
}
|
|
19
|
+
export function gatherFungibleTokenUtxos(utxos, tokenCategory, amount) {
|
|
20
|
+
const sortedTokenUtxos = utxos
|
|
21
|
+
.filter((utxo) => isFungibleTokenUtxo(utxo) && utxo.token.category === tokenCategory)
|
|
22
|
+
.toSorted((a, b) => Number(b.token.amount - a.token.amount));
|
|
23
|
+
const targetUtxos = [];
|
|
24
|
+
let total = 0n;
|
|
25
|
+
for (const utxo of sortedTokenUtxos) {
|
|
26
|
+
if (total >= amount)
|
|
27
|
+
break;
|
|
28
|
+
total += utxo.token.amount;
|
|
29
|
+
targetUtxos.push(utxo);
|
|
30
|
+
}
|
|
31
|
+
if (total < amount) {
|
|
32
|
+
throw new Error('Not enough fungible tokens to cover the required amount');
|
|
33
|
+
}
|
|
34
|
+
return { utxos: targetUtxos, totalAmount: total };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=transaction-utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.6",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@bitauth/libauth": "^3.1.0-next.8",
|
|
45
|
-
"@cashscript/utils": "^0.13.0-next.
|
|
45
|
+
"@cashscript/utils": "^0.13.0-next.6",
|
|
46
46
|
"@electrum-cash/network": "^4.1.3",
|
|
47
47
|
"@mr-zwets/bchn-api-wrapper": "^1.0.1",
|
|
48
48
|
"fflate": "^0.8.2",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"typescript": "^5.9.2",
|
|
57
57
|
"vitest": "^4.0.15"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "c204d0ccadc16bbce1206cf00c0960c254ce3af0"
|
|
60
60
|
}
|