cashscript 0.11.4 → 0.12.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.
- package/README.md +18 -6
- package/dist/Argument.js +12 -8
- package/dist/Contract.d.ts +3 -9
- package/dist/Contract.js +4 -31
- package/dist/Errors.d.ts +2 -2
- package/dist/SignatureTemplate.d.ts +1 -0
- package/dist/SignatureTemplate.js +13 -4
- package/dist/TransactionBuilder.d.ts +8 -4
- package/dist/TransactionBuilder.js +74 -22
- package/dist/debugging.js +53 -20
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/interfaces.d.ts +10 -6
- package/dist/interfaces.js +6 -3
- package/dist/libauth-template/LibauthTemplate.d.ts +6 -0
- package/dist/libauth-template/LibauthTemplate.js +445 -0
- package/dist/libauth-template/utils.d.ts +27 -0
- package/dist/libauth-template/utils.js +89 -0
- package/dist/network/MockNetworkProvider.d.ts +4 -3
- package/dist/network/MockNetworkProvider.js +4 -14
- package/dist/test/JestExtensions.js +5 -26
- package/dist/types/type-inference.d.ts +8 -6
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +3 -23
- package/package.json +4 -7
- package/dist/LibauthTemplate.d.ts +0 -32
- package/dist/LibauthTemplate.js +0 -324
- package/dist/Transaction.d.ts +0 -45
- package/dist/Transaction.js +0 -385
- package/dist/advanced/LibauthTemplate.d.ts +0 -45
- package/dist/advanced/LibauthTemplate.js +0 -426
package/dist/Transaction.js
DELETED
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import { hexToBin, decodeTransaction, } from '@bitauth/libauth';
|
|
2
|
-
import delay from 'delay';
|
|
3
|
-
import { encodeBip68, placeholder, } from '@cashscript/utils';
|
|
4
|
-
import deepEqual from 'fast-deep-equal';
|
|
5
|
-
import { isUtxoP2PKH, SignatureAlgorithm, } from './interfaces.js';
|
|
6
|
-
import { createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, validateOutput, utxoComparator, calculateDust, getOutputSize, utxoTokenComparator, } from './utils.js';
|
|
7
|
-
import SignatureTemplate from './SignatureTemplate.js';
|
|
8
|
-
import { P2PKH_INPUT_SIZE } from './constants.js';
|
|
9
|
-
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
10
|
-
import { buildTemplate, getBitauthUri } from './LibauthTemplate.js';
|
|
11
|
-
import { debugTemplate } from './debugging.js';
|
|
12
|
-
import { FailedTransactionError } from './Errors.js';
|
|
13
|
-
import semver from 'semver';
|
|
14
|
-
export class Transaction {
|
|
15
|
-
constructor(contract, unlocker, abiFunction, encodedFunctionArgs, selector) {
|
|
16
|
-
this.contract = contract;
|
|
17
|
-
this.unlocker = unlocker;
|
|
18
|
-
this.abiFunction = abiFunction;
|
|
19
|
-
this.encodedFunctionArgs = encodedFunctionArgs;
|
|
20
|
-
this.selector = selector;
|
|
21
|
-
this.inputs = [];
|
|
22
|
-
this.outputs = [];
|
|
23
|
-
this.sequence = 0xfffffffe;
|
|
24
|
-
this.feePerByte = 1.0;
|
|
25
|
-
this.minChange = 0n;
|
|
26
|
-
this.tokenChange = true;
|
|
27
|
-
}
|
|
28
|
-
from(inputOrInputs) {
|
|
29
|
-
if (!Array.isArray(inputOrInputs)) {
|
|
30
|
-
inputOrInputs = [inputOrInputs];
|
|
31
|
-
}
|
|
32
|
-
this.inputs = this.inputs.concat(inputOrInputs);
|
|
33
|
-
return this;
|
|
34
|
-
}
|
|
35
|
-
fromP2PKH(inputOrInputs, template) {
|
|
36
|
-
if (!Array.isArray(inputOrInputs)) {
|
|
37
|
-
inputOrInputs = [inputOrInputs];
|
|
38
|
-
}
|
|
39
|
-
inputOrInputs = inputOrInputs.map((input) => ({ ...input, template }));
|
|
40
|
-
this.inputs = this.inputs.concat(inputOrInputs);
|
|
41
|
-
return this;
|
|
42
|
-
}
|
|
43
|
-
to(toOrOutputs, amount, token) {
|
|
44
|
-
if (typeof toOrOutputs === 'string' && typeof amount === 'bigint') {
|
|
45
|
-
const recipient = { to: toOrOutputs, amount, token };
|
|
46
|
-
return this.to([recipient]);
|
|
47
|
-
}
|
|
48
|
-
if (Array.isArray(toOrOutputs) && amount === undefined) {
|
|
49
|
-
toOrOutputs.forEach(validateOutput);
|
|
50
|
-
this.outputs = this.outputs.concat(toOrOutputs);
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
throw new Error('Incorrect arguments passed to function \'to\'');
|
|
54
|
-
}
|
|
55
|
-
withOpReturn(chunks) {
|
|
56
|
-
this.outputs.push(createOpReturnOutput(chunks));
|
|
57
|
-
return this;
|
|
58
|
-
}
|
|
59
|
-
withAge(age) {
|
|
60
|
-
this.sequence = encodeBip68({ blocks: age });
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
withTime(time) {
|
|
64
|
-
this.locktime = time;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
withHardcodedFee(hardcodedFee) {
|
|
68
|
-
this.hardcodedFee = hardcodedFee;
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
|
-
withFeePerByte(feePerByte) {
|
|
72
|
-
this.feePerByte = feePerByte;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
withMinChange(minChange) {
|
|
76
|
-
this.minChange = minChange;
|
|
77
|
-
return this;
|
|
78
|
-
}
|
|
79
|
-
withoutChange() {
|
|
80
|
-
return this.withMinChange(BigInt(Number.MAX_VALUE));
|
|
81
|
-
}
|
|
82
|
-
withoutTokenChange() {
|
|
83
|
-
this.tokenChange = false;
|
|
84
|
-
return this;
|
|
85
|
-
}
|
|
86
|
-
async build() {
|
|
87
|
-
this.locktime = this.locktime ?? await this.contract.provider.getBlockHeight();
|
|
88
|
-
await this.setInputsAndOutputs();
|
|
89
|
-
const builder = new TransactionBuilder({ provider: this.contract.provider });
|
|
90
|
-
this.inputs.forEach((utxo) => {
|
|
91
|
-
if (isUtxoP2PKH(utxo)) {
|
|
92
|
-
builder.addInput(utxo, utxo.template.unlockP2PKH(), { sequence: this.sequence });
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
builder.addInput(utxo, this.unlocker, { sequence: this.sequence });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
builder.addOutputs(this.outputs);
|
|
99
|
-
builder.setLocktime(this.locktime);
|
|
100
|
-
return builder.build();
|
|
101
|
-
}
|
|
102
|
-
async send(raw) {
|
|
103
|
-
const tx = await this.build();
|
|
104
|
-
// Debug the transaction locally before sending so any errors are caught early
|
|
105
|
-
await this.debug();
|
|
106
|
-
try {
|
|
107
|
-
const txid = await this.contract.provider.sendRawTransaction(tx);
|
|
108
|
-
return raw ? await this.getTxDetails(txid, raw) : await this.getTxDetails(txid);
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
const reason = error.error ?? error.message ?? error;
|
|
112
|
-
throw new FailedTransactionError(reason, await this.bitauthUri());
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// method to debug the transaction with libauth VM, throws upon evaluation error
|
|
116
|
-
async debug() {
|
|
117
|
-
if (!semver.satisfies(this.contract.artifact.compiler.version, '>=0.11.0')) {
|
|
118
|
-
console.warn('For the best debugging experience, please recompile your contract with cashc version 0.11.0 or newer.');
|
|
119
|
-
}
|
|
120
|
-
const template = await this.getLibauthTemplate();
|
|
121
|
-
return debugTemplate(template, [this.contract.artifact]);
|
|
122
|
-
}
|
|
123
|
-
async bitauthUri() {
|
|
124
|
-
console.warn('WARNING: it is unsafe to use this Bitauth URI when using real private keys as they are included in the transaction template');
|
|
125
|
-
const template = await this.getLibauthTemplate();
|
|
126
|
-
return getBitauthUri(template);
|
|
127
|
-
}
|
|
128
|
-
async getLibauthTemplate() {
|
|
129
|
-
return buildTemplate({ transaction: this });
|
|
130
|
-
}
|
|
131
|
-
async getTxDetails(txid, raw) {
|
|
132
|
-
for (let retries = 0; retries < 1200; retries += 1) {
|
|
133
|
-
await delay(500);
|
|
134
|
-
try {
|
|
135
|
-
const hex = await this.contract.provider.getRawTransaction(txid);
|
|
136
|
-
if (raw)
|
|
137
|
-
return hex;
|
|
138
|
-
const libauthTransaction = decodeTransaction(hexToBin(hex));
|
|
139
|
-
return { ...libauthTransaction, txid, hex };
|
|
140
|
-
}
|
|
141
|
-
catch (ignored) {
|
|
142
|
-
// ignored
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// Should not happen
|
|
146
|
-
throw new Error('Could not retrieve transaction details for over 10 minutes');
|
|
147
|
-
}
|
|
148
|
-
async setInputsAndOutputs() {
|
|
149
|
-
if (this.outputs.length === 0) {
|
|
150
|
-
throw new Error('Attempted to build a transaction without outputs');
|
|
151
|
-
}
|
|
152
|
-
// Fetched utxos are only used when no inputs are available, so only fetch in that case.
|
|
153
|
-
const allUtxos = this.inputs.length === 0
|
|
154
|
-
? await this.contract.provider.getUtxos(this.contract.address)
|
|
155
|
-
: [];
|
|
156
|
-
const tokenInputs = this.inputs.length > 0
|
|
157
|
-
? this.inputs.filter((input) => input.token)
|
|
158
|
-
: selectAllTokenUtxos(allUtxos, this.outputs);
|
|
159
|
-
// This throws if the manually selected inputs are not enough to cover the outputs
|
|
160
|
-
if (this.inputs.length > 0) {
|
|
161
|
-
selectAllTokenUtxos(this.inputs, this.outputs);
|
|
162
|
-
}
|
|
163
|
-
if (this.tokenChange) {
|
|
164
|
-
const tokenChangeOutputs = createFungibleTokenChangeOutputs(tokenInputs, this.outputs, this.contract.tokenAddress);
|
|
165
|
-
this.outputs.push(...tokenChangeOutputs);
|
|
166
|
-
}
|
|
167
|
-
// Construct list with all nfts in inputs
|
|
168
|
-
const listNftsInputs = [];
|
|
169
|
-
// If inputs are manually selected, add their tokens to balance
|
|
170
|
-
this.inputs.forEach((input) => {
|
|
171
|
-
if (!input.token)
|
|
172
|
-
return;
|
|
173
|
-
if (input.token.nft) {
|
|
174
|
-
listNftsInputs.push({ ...input.token.nft, category: input.token.category });
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
// Construct list with all nfts in outputs
|
|
178
|
-
let listNftsOutputs = [];
|
|
179
|
-
// Subtract all token outputs from the token balances
|
|
180
|
-
this.outputs.forEach((output) => {
|
|
181
|
-
if (!output.token)
|
|
182
|
-
return;
|
|
183
|
-
if (output.token.nft) {
|
|
184
|
-
listNftsOutputs.push({ ...output.token.nft, category: output.token.category });
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
// If inputs are manually provided, check token balances
|
|
188
|
-
if (this.inputs.length > 0) {
|
|
189
|
-
// Compare nfts in- and outputs, check if inputs have nfts corresponding to outputs
|
|
190
|
-
// Keep list of nfts in inputs without matching output
|
|
191
|
-
// First check immutable nfts, then mutable & minting nfts together
|
|
192
|
-
// This is so an immutable input gets matched first and is removed from the list of unused nfts
|
|
193
|
-
let unusedNfts = listNftsInputs;
|
|
194
|
-
for (const nftInput of listNftsInputs) {
|
|
195
|
-
if (nftInput.capability === 'none') {
|
|
196
|
-
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
197
|
-
// Deep equality check token objects
|
|
198
|
-
if (deepEqual(listNftsOutputs[i], nftInput)) {
|
|
199
|
-
listNftsOutputs.splice(i, 1);
|
|
200
|
-
unusedNfts = unusedNfts.filter((nft) => !deepEqual(nft, nftInput));
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
for (const nftInput of listNftsInputs) {
|
|
207
|
-
if (nftInput.capability === 'minting') {
|
|
208
|
-
// eslint-disable-next-line max-len
|
|
209
|
-
const newListNftsOutputs = listNftsOutputs.filter((nftOutput) => nftOutput.category !== nftInput.category);
|
|
210
|
-
if (newListNftsOutputs !== listNftsOutputs) {
|
|
211
|
-
unusedNfts = unusedNfts.filter((nft) => !deepEqual(nft, nftInput));
|
|
212
|
-
listNftsOutputs = newListNftsOutputs;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
if (nftInput.capability === 'mutable') {
|
|
216
|
-
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
217
|
-
if (listNftsOutputs[i].category === nftInput.category) {
|
|
218
|
-
listNftsOutputs.splice(i, 1);
|
|
219
|
-
unusedNfts = unusedNfts.filter((nft) => !deepEqual(nft, nftInput));
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
for (const nftOutput of listNftsOutputs) {
|
|
226
|
-
const genesisUtxo = getTokenGenesisUtxo(this.inputs, nftOutput.category);
|
|
227
|
-
if (genesisUtxo) {
|
|
228
|
-
listNftsOutputs = listNftsOutputs.filter((nft) => !deepEqual(nft, nftOutput));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (listNftsOutputs.length !== 0) {
|
|
232
|
-
throw new Error(`NFT output with token category ${listNftsOutputs[0].category} does not have corresponding input`);
|
|
233
|
-
}
|
|
234
|
-
if (this.tokenChange) {
|
|
235
|
-
for (const unusedNft of unusedNfts) {
|
|
236
|
-
const tokenDetails = {
|
|
237
|
-
category: unusedNft.category,
|
|
238
|
-
amount: BigInt(0),
|
|
239
|
-
nft: {
|
|
240
|
-
capability: unusedNft.capability,
|
|
241
|
-
commitment: unusedNft.commitment,
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
const nftChangeOutput = { to: this.contract.tokenAddress, amount: BigInt(1000), token: tokenDetails };
|
|
245
|
-
this.outputs.push(nftChangeOutput);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// Replace all SignatureTemplate with placeholder Uint8Arrays
|
|
250
|
-
const placeholderArgs = this.encodedFunctionArgs.map((arg) => {
|
|
251
|
-
if (!(arg instanceof SignatureTemplate))
|
|
252
|
-
return arg;
|
|
253
|
-
// Schnorr signatures are *always* 65 bytes: 64 for signature + 1 byte for hashtype.
|
|
254
|
-
if (arg.getSignatureAlgorithm() === SignatureAlgorithm.SCHNORR)
|
|
255
|
-
return placeholder(65);
|
|
256
|
-
// ECDSA signatures are at least 71 bytes: 64 bytes for signature + 1 byte for hashtype + 6 bytes for encoding
|
|
257
|
-
// overhead. But it may have up to 2 extra bytes for padding, so we overestimate by 2 bytes.
|
|
258
|
-
// (see https://transactionfee.info/charts/bitcoin-script-ecdsa-length/)
|
|
259
|
-
return placeholder(73);
|
|
260
|
-
});
|
|
261
|
-
// Create a placeholder input script for size calculation using the placeholder arguments
|
|
262
|
-
const placeholderScript = createInputScript(this.contract.redeemScript, placeholderArgs, this.selector);
|
|
263
|
-
// Add one extra byte per input to over-estimate tx-in count
|
|
264
|
-
const contractInputSize = getInputSize(placeholderScript) + 1;
|
|
265
|
-
// Note that we use the addPrecision function to add "decimal points" to BigInt numbers
|
|
266
|
-
// Calculate amount to send and base fee (excluding additional fees per UTXO)
|
|
267
|
-
let amount = addPrecision(this.outputs.reduce((acc, output) => acc + output.amount, 0n));
|
|
268
|
-
let fee = addPrecision(this.hardcodedFee ?? getTxSizeWithoutInputs(this.outputs) * this.feePerByte);
|
|
269
|
-
// Select and gather UTXOs and calculate fees and available funds
|
|
270
|
-
let satsAvailable = 0n;
|
|
271
|
-
if (this.inputs.length > 0) {
|
|
272
|
-
// If inputs are already defined, the user provided the UTXOs and we perform no further UTXO selection
|
|
273
|
-
if (!this.hardcodedFee) {
|
|
274
|
-
const totalInputSize = this.inputs.reduce((acc, input) => acc + (isUtxoP2PKH(input) ? P2PKH_INPUT_SIZE : contractInputSize), 0);
|
|
275
|
-
fee += addPrecision(totalInputSize * this.feePerByte);
|
|
276
|
-
}
|
|
277
|
-
satsAvailable = addPrecision(this.inputs.reduce((acc, input) => acc + input.satoshis, 0n));
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
// If inputs are not defined yet, we retrieve the contract's UTXOs and perform selection
|
|
281
|
-
const bchUtxos = allUtxos.filter((utxo) => !utxo.token);
|
|
282
|
-
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
283
|
-
// even if they report UTXOs in a different order
|
|
284
|
-
bchUtxos.sort(utxoComparator).reverse();
|
|
285
|
-
// Add all automatically added token inputs to the transaction
|
|
286
|
-
for (const utxo of tokenInputs) {
|
|
287
|
-
this.inputs.push(utxo);
|
|
288
|
-
satsAvailable += addPrecision(utxo.satoshis);
|
|
289
|
-
if (!this.hardcodedFee)
|
|
290
|
-
fee += addPrecision(contractInputSize * this.feePerByte);
|
|
291
|
-
}
|
|
292
|
-
for (const utxo of bchUtxos) {
|
|
293
|
-
if (satsAvailable > amount + fee)
|
|
294
|
-
break;
|
|
295
|
-
this.inputs.push(utxo);
|
|
296
|
-
satsAvailable += addPrecision(utxo.satoshis);
|
|
297
|
-
if (!this.hardcodedFee)
|
|
298
|
-
fee += addPrecision(contractInputSize * this.feePerByte);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
// Remove "decimal points" from BigInt numbers (rounding up for fee, down for others)
|
|
302
|
-
satsAvailable = removePrecisionFloor(satsAvailable);
|
|
303
|
-
amount = removePrecisionFloor(amount);
|
|
304
|
-
fee = removePrecisionCeil(fee);
|
|
305
|
-
// Calculate change and check available funds
|
|
306
|
-
let change = satsAvailable - amount - fee;
|
|
307
|
-
if (change < 0) {
|
|
308
|
-
throw new Error(`Insufficient funds: available (${satsAvailable}) < needed (${amount + fee}).`);
|
|
309
|
-
}
|
|
310
|
-
// Account for the fee of adding a change output
|
|
311
|
-
if (!this.hardcodedFee) {
|
|
312
|
-
const changeOutputSize = getOutputSize({ to: this.contract.address, amount: 0n });
|
|
313
|
-
change -= BigInt(changeOutputSize * this.feePerByte);
|
|
314
|
-
}
|
|
315
|
-
// Add a change output if applicable
|
|
316
|
-
const changeOutput = { to: this.contract.address, amount: change };
|
|
317
|
-
if (change >= this.minChange && change >= calculateDust(changeOutput)) {
|
|
318
|
-
this.outputs.push(changeOutput);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const getTokenGenesisUtxo = (utxos, tokenCategory) => {
|
|
323
|
-
const creationUtxo = utxos.find((utxo) => utxo.vout === 0 && utxo.txid === tokenCategory);
|
|
324
|
-
return creationUtxo;
|
|
325
|
-
};
|
|
326
|
-
const getTokenCategories = (outputs) => (outputs
|
|
327
|
-
.filter((output) => output.token)
|
|
328
|
-
.map((output) => output.token.category));
|
|
329
|
-
const calculateTotalTokenAmount = (outputs, tokenCategory) => (outputs
|
|
330
|
-
.filter((output) => output.token?.category === tokenCategory)
|
|
331
|
-
.reduce((acc, output) => acc + output.token.amount, 0n));
|
|
332
|
-
const selectTokenUtxos = (utxos, amountNeeded, tokenCategory) => {
|
|
333
|
-
const genesisUtxo = getTokenGenesisUtxo(utxos, tokenCategory);
|
|
334
|
-
if (genesisUtxo)
|
|
335
|
-
return [genesisUtxo];
|
|
336
|
-
const tokenUtxos = utxos.filter((utxo) => utxo.token?.category === tokenCategory && utxo.token?.amount > 0n);
|
|
337
|
-
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
338
|
-
// even if they report UTXOs in a different order
|
|
339
|
-
tokenUtxos.sort(utxoTokenComparator).reverse();
|
|
340
|
-
let amountAvailable = 0n;
|
|
341
|
-
const selectedUtxos = [];
|
|
342
|
-
// Add token UTXOs until we have enough to cover the amount needed (no fee calculation because it's a token)
|
|
343
|
-
for (const utxo of tokenUtxos) {
|
|
344
|
-
if (amountAvailable >= amountNeeded)
|
|
345
|
-
break;
|
|
346
|
-
selectedUtxos.push(utxo);
|
|
347
|
-
amountAvailable += utxo.token.amount;
|
|
348
|
-
}
|
|
349
|
-
if (amountAvailable < amountNeeded) {
|
|
350
|
-
throw new Error(`Insufficient funds for token ${tokenCategory}: available (${amountAvailable}) < needed (${amountNeeded}).`);
|
|
351
|
-
}
|
|
352
|
-
return selectedUtxos;
|
|
353
|
-
};
|
|
354
|
-
const selectAllTokenUtxos = (utxos, outputs) => {
|
|
355
|
-
const tokenCategories = getTokenCategories(outputs);
|
|
356
|
-
return tokenCategories.flatMap((tokenCategory) => selectTokenUtxos(utxos, calculateTotalTokenAmount(outputs, tokenCategory), tokenCategory));
|
|
357
|
-
};
|
|
358
|
-
const createFungibleTokenChangeOutputs = (utxos, outputs, address) => {
|
|
359
|
-
const tokenCategories = getTokenCategories(utxos);
|
|
360
|
-
const changeOutputs = tokenCategories.map((tokenCategory) => {
|
|
361
|
-
const required = calculateTotalTokenAmount(outputs, tokenCategory);
|
|
362
|
-
const available = calculateTotalTokenAmount(utxos, tokenCategory);
|
|
363
|
-
const change = available - required;
|
|
364
|
-
if (change === 0n)
|
|
365
|
-
return undefined;
|
|
366
|
-
return { to: address, amount: BigInt(1000), token: { category: tokenCategory, amount: change } };
|
|
367
|
-
});
|
|
368
|
-
return changeOutputs.filter((output) => output !== undefined);
|
|
369
|
-
};
|
|
370
|
-
// Note: the below is a very simple implementation of a "decimal point" system for BigInt numbers
|
|
371
|
-
// It is safe to use for UTXO fee calculations due to its low numbers, but should not be used for other purposes
|
|
372
|
-
// Also note that multiplication and division between two "decimal" bigints is not supported
|
|
373
|
-
// High precision may not work with some 'number' inputs, so we set the default to 6 "decimal places"
|
|
374
|
-
const addPrecision = (amount, precision = 6) => {
|
|
375
|
-
if (typeof amount === 'number') {
|
|
376
|
-
return BigInt(Math.ceil(amount * 10 ** precision));
|
|
377
|
-
}
|
|
378
|
-
return amount * BigInt(10 ** precision);
|
|
379
|
-
};
|
|
380
|
-
const removePrecisionFloor = (amount, precision = 6) => (amount / (10n ** BigInt(precision)));
|
|
381
|
-
const removePrecisionCeil = (amount, precision = 6) => {
|
|
382
|
-
const multiplier = 10n ** BigInt(precision);
|
|
383
|
-
return (amount + multiplier - 1n) / multiplier;
|
|
384
|
-
};
|
|
385
|
-
//# sourceMappingURL=Transaction.js.map
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { TransactionBch, WalletTemplate, WalletTemplateScenarioBytecode } from '@bitauth/libauth';
|
|
2
|
-
import { AbiFunction } from '@cashscript/utils';
|
|
3
|
-
import { EncodedConstructorArgument, EncodedFunctionArgument } from '../Argument.js';
|
|
4
|
-
import { Contract } from '../Contract.js';
|
|
5
|
-
import { DebugResults } from '../debugging.js';
|
|
6
|
-
import { StandardUnlockableUtxo } from '../interfaces.js';
|
|
7
|
-
import SignatureTemplate from '../SignatureTemplate.js';
|
|
8
|
-
import { Transaction } from '../Transaction.js';
|
|
9
|
-
import { TransactionBuilder } from '../TransactionBuilder.js';
|
|
10
|
-
/**
|
|
11
|
-
* Generates template entities for P2PKH (Pay to Public Key Hash) placeholder scripts.
|
|
12
|
-
*
|
|
13
|
-
* Follows the WalletTemplateEntity specification from:
|
|
14
|
-
* https://ide.bitauth.com/authentication-template-v0.schema.json
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
export declare const generateTemplateEntitiesP2PKH: (inputIndex: number) => WalletTemplate["entities"];
|
|
18
|
-
/**
|
|
19
|
-
* Generates template entities for P2SH (Pay to Script Hash) placeholder scripts.
|
|
20
|
-
*
|
|
21
|
-
* Follows the WalletTemplateEntity specification from:
|
|
22
|
-
* https://ide.bitauth.com/authentication-template-v0.schema.json
|
|
23
|
-
*
|
|
24
|
-
*/
|
|
25
|
-
export declare const generateTemplateEntitiesP2SH: (contract: Contract, abiFunction: AbiFunction, encodedFunctionArgs: EncodedFunctionArgument[], inputIndex: number) => WalletTemplate["entities"];
|
|
26
|
-
/**
|
|
27
|
-
* Generates template scripts for P2PKH (Pay to Public Key Hash) placeholder scripts.
|
|
28
|
-
*
|
|
29
|
-
* Follows the WalletTemplateScript specification from:
|
|
30
|
-
* https://ide.bitauth.com/authentication-template-v0.schema.json
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
export declare const generateTemplateScriptsP2PKH: (template: SignatureTemplate, inputIndex: number) => WalletTemplate["scripts"];
|
|
34
|
-
/**
|
|
35
|
-
* Generates template scripts for P2SH (Pay to Script Hash) placeholder scripts.
|
|
36
|
-
*
|
|
37
|
-
* Follows the WalletTemplateScript specification from:
|
|
38
|
-
* https://ide.bitauth.com/authentication-template-v0.schema.json
|
|
39
|
-
*
|
|
40
|
-
*/
|
|
41
|
-
export declare const generateTemplateScriptsP2SH: (contract: Contract, abiFunction: AbiFunction, encodedFunctionArgs: EncodedFunctionArgument[], encodedConstructorArgs: EncodedConstructorArgument[], inputIndex: number) => WalletTemplate["scripts"];
|
|
42
|
-
export declare const generateTemplateScenarios: (contract: Contract, libauthTransaction: TransactionBch, csTransaction: Transaction, abiFunction: AbiFunction, encodedFunctionArgs: EncodedFunctionArgument[], inputIndex: number) => WalletTemplate["scenarios"];
|
|
43
|
-
export declare const getLibauthTemplates: (txn: TransactionBuilder) => WalletTemplate;
|
|
44
|
-
export declare const debugLibauthTemplate: (template: WalletTemplate, transaction: TransactionBuilder) => DebugResults;
|
|
45
|
-
export declare const generateUnlockingScriptParams: (csInput: StandardUnlockableUtxo, p2pkhScriptNameTemplate: string, inputIndex: number) => WalletTemplateScenarioBytecode;
|