@koralabs/kora-labs-common 6.14.2 → 6.15.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/cronLock/cqlCronLock.js +22 -7
- package/package.json +7 -2
- package/txBuild/blockfrost.js +1 -0
- package/txBuild/index.d.ts +1 -0
- package/txBuild/index.js +1 -0
- package/txBuild/scalus.d.ts +26 -0
- package/txBuild/scalus.js +119 -0
- package/txBuild/scriptTx.d.ts +12 -0
- package/txBuild/scriptTx.js +54 -6
package/cronLock/cqlCronLock.js
CHANGED
|
@@ -148,26 +148,41 @@ const acquireWithExecutor = async (executor, name, offline, topology, token = cr
|
|
|
148
148
|
// The LWT timed out: it may have been applied WITH OUR TOKEN, in which case nobody (not even
|
|
149
149
|
// us) could run until the lease expired. A read at the same serial consistency completes any
|
|
150
150
|
// in-flight Paxos round, so it tells us the real owner.
|
|
151
|
-
const
|
|
152
|
-
if (owner === token)
|
|
151
|
+
const read = await readOwner(executor, key, serialConsistency);
|
|
152
|
+
if (read.owner === token)
|
|
153
153
|
return { status: 'acquired', token };
|
|
154
|
-
if (owner)
|
|
154
|
+
if (read.owner)
|
|
155
155
|
return { status: 'held' };
|
|
156
|
+
if (read.error) {
|
|
157
|
+
// Still unknown and we will not run: retract our possibly-applied proposal so it cannot
|
|
158
|
+
// block every box for the whole lease. Conditional on our token, so it never frees another's lock.
|
|
159
|
+
const retracted = await deleteIfOwner(executor, key, token, serialConsistency, consistency);
|
|
160
|
+
return { status: 'unavailable', message: `${message}; owner read failed (${read.error}); ${retracted ? 'retracted our proposal' : 'retract failed, the lease will reclaim it'}` };
|
|
161
|
+
}
|
|
156
162
|
}
|
|
157
163
|
return { status: 'unavailable', message };
|
|
158
164
|
}
|
|
159
165
|
};
|
|
160
166
|
exports.acquireWithExecutor = acquireWithExecutor;
|
|
161
|
-
/** The lock's owner as a linearizable (serial) read sees it
|
|
167
|
+
/** The lock's owner as a linearizable (serial) read sees it. */
|
|
162
168
|
const readOwner = async (executor, key, serialConsistency) => {
|
|
163
169
|
var _a;
|
|
164
170
|
try {
|
|
165
171
|
const result = await executor.execute(`SELECT owner FROM ${KEYSPACE}.${LOCK_TABLE} WHERE name = ?`, [key], { prepare: true, consistency: serialConsistency });
|
|
166
172
|
const owner = (_a = result.rows[0]) === null || _a === void 0 ? void 0 : _a.owner;
|
|
167
|
-
return typeof owner === 'string' ? owner :
|
|
173
|
+
return typeof owner === 'string' ? { owner } : {};
|
|
168
174
|
}
|
|
169
|
-
catch (
|
|
170
|
-
return
|
|
175
|
+
catch (error) {
|
|
176
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const deleteIfOwner = async (executor, key, token, serialConsistency, consistency) => {
|
|
180
|
+
try {
|
|
181
|
+
await executor.execute(`DELETE FROM ${KEYSPACE}.${LOCK_TABLE} WHERE name = ? IF owner = ?`, [key, token], { prepare: true, serialConsistency, consistency });
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
catch (_a) {
|
|
185
|
+
return false;
|
|
171
186
|
}
|
|
172
187
|
};
|
|
173
188
|
const acquire = async (name, offline, options = {}) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koralabs/kora-labs-common",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.0",
|
|
4
4
|
"description": "Kora Labs Common Utilities",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"cassandra-driver": "^4.10.0",
|
|
53
53
|
"eslint": "^8.57.1",
|
|
54
54
|
"jest": "^30.4.2",
|
|
55
|
+
"scalus": "^0.15.0",
|
|
55
56
|
"ts-jest": "^29.4.11",
|
|
56
57
|
"typescript": "^5.9.3"
|
|
57
58
|
},
|
|
@@ -77,7 +78,8 @@
|
|
|
77
78
|
"@cardano-sdk/crypto": "^0.4.5",
|
|
78
79
|
"@emurgo/cardano-message-signing-nodejs": "^1.1.0",
|
|
79
80
|
"bip39": "^3.1.0",
|
|
80
|
-
"cassandra-driver": "^4.9.0"
|
|
81
|
+
"cassandra-driver": "^4.9.0",
|
|
82
|
+
"scalus": "^0.15.0"
|
|
81
83
|
},
|
|
82
84
|
"peerDependenciesMeta": {
|
|
83
85
|
"@aiken-lang/merkle-patricia-forestry": {
|
|
@@ -97,6 +99,9 @@
|
|
|
97
99
|
},
|
|
98
100
|
"cassandra-driver": {
|
|
99
101
|
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"scalus": {
|
|
104
|
+
"optional": true
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
}
|
package/txBuild/blockfrost.js
CHANGED
|
@@ -109,6 +109,7 @@ class BlockfrostTxClient {
|
|
|
109
109
|
priceSteps: (0, fees_1.ratio)(p.price_step),
|
|
110
110
|
minFeeRefScriptCostPerByte: (0, fees_1.ratio)((_a = p.min_fee_ref_script_cost_per_byte) !== null && _a !== void 0 ? _a : 0),
|
|
111
111
|
coinsPerUtxoByte: BigInt(p.coins_per_utxo_size),
|
|
112
|
+
stakeKeyDeposit: BigInt(p.key_deposit),
|
|
112
113
|
maxTxSize: Number(p.max_tx_size),
|
|
113
114
|
maxTxExUnits: { memory: Number(p.max_tx_ex_mem), steps: Number(p.max_tx_ex_steps) },
|
|
114
115
|
costModels
|
package/txBuild/index.d.ts
CHANGED
package/txBuild/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Cardano, Serialization } from '@cardano-sdk/core';
|
|
2
|
+
import { Evaluator } from './scriptTx';
|
|
3
|
+
/**
|
|
4
|
+
* Scripts come in two forms: "single CBOR" — a CBOR byte string of the flat program, as Aiken
|
|
5
|
+
* blueprints, Blockfrost `/scripts/{hash}/cbor` and cardano-sdk's `PlutusScript.bytes` hold it — and
|
|
6
|
+
* "double CBOR" — that wrapped once more, as scalus reads and writes it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const toDoubleCbor: (cborHex: string) => string;
|
|
9
|
+
export declare const toSingleCbor: (cborHex: string) => string;
|
|
10
|
+
/** Plutus data in the node's JSON form (what scalus.applyDataArgToScript reads). */
|
|
11
|
+
export declare const plutusDataToJson: (data: Serialization.PlutusData) => unknown;
|
|
12
|
+
/** Apply parameters (in order) to a compiled validator; returns the script in single-CBOR form. */
|
|
13
|
+
export declare const applyParamsToScript: (compiledCode: string, params: Serialization.PlutusData[]) => string;
|
|
14
|
+
/** A Plutus script (single- or double-CBOR) as a cardano-sdk Script. */
|
|
15
|
+
export declare const plutusScript: (cborHex: string, version: Cardano.PlutusLanguageVersion) => Cardano.PlutusScript;
|
|
16
|
+
/** The script hash of a (single- or double-CBOR) Plutus script of `version`. */
|
|
17
|
+
export declare const plutusScriptHash: (cborHex: string, version: Cardano.PlutusLanguageVersion) => string;
|
|
18
|
+
/**
|
|
19
|
+
* An Evaluator that runs the tx's scripts locally (scalus) against `utxos` — every spent AND
|
|
20
|
+
* referenced UTxO, reference scripts included. Offline stand-in for the node's evaluation.
|
|
21
|
+
*/
|
|
22
|
+
export declare const localEvaluator: ({ utxos, costModels, network }: {
|
|
23
|
+
utxos: Cardano.Utxo[];
|
|
24
|
+
costModels: Map<Cardano.PlutusLanguageVersion, number[]>;
|
|
25
|
+
network: "mainnet" | "preview" | "preprod";
|
|
26
|
+
}) => Evaluator;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.localEvaluator = exports.plutusScriptHash = exports.plutusScript = exports.applyParamsToScript = exports.plutusDataToJson = exports.toSingleCbor = exports.toDoubleCbor = void 0;
|
|
4
|
+
// Local UPLC work on scalus (optional peer dependency, loaded on first use): applying parameters to a
|
|
5
|
+
// compiled validator (replaces Helios' UplcProgram.apply) and evaluating a tx's scripts offline.
|
|
6
|
+
// Production code evaluates with the node (BlockfrostTxClient.evaluateTx); the local evaluator is for
|
|
7
|
+
// tests and tooling that must run without a network.
|
|
8
|
+
const core_1 = require("@cardano-sdk/core");
|
|
9
|
+
let scalusModule;
|
|
10
|
+
// Lazy: scalus is an optional peer, needed only by callers of these helpers.
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
12
|
+
const scalus = () => (scalusModule !== null && scalusModule !== void 0 ? scalusModule : (scalusModule = require('scalus')));
|
|
13
|
+
const cborByteStringHeader = (length) => {
|
|
14
|
+
if (length <= 23)
|
|
15
|
+
return Buffer.from([0x40 | length]);
|
|
16
|
+
if (length <= 0xff)
|
|
17
|
+
return Buffer.from([0x58, length]);
|
|
18
|
+
if (length <= 0xffff)
|
|
19
|
+
return Buffer.from([0x59, length >> 8, length & 0xff]);
|
|
20
|
+
return Buffer.from([0x5a, (length >>> 24) & 0xff, (length >> 16) & 0xff, (length >> 8) & 0xff, length & 0xff]);
|
|
21
|
+
};
|
|
22
|
+
/** [header length, content length] of the CBOR byte string at the start of `bytes`. */
|
|
23
|
+
const byteStringHeader = (bytes) => {
|
|
24
|
+
if (bytes.length === 0 || bytes[0] >> 5 !== 2)
|
|
25
|
+
throw new Error('expected a CBOR byte string');
|
|
26
|
+
const info = bytes[0] & 0x1f;
|
|
27
|
+
if (info <= 23)
|
|
28
|
+
return [1, info];
|
|
29
|
+
if (info === 24)
|
|
30
|
+
return [2, bytes[1]];
|
|
31
|
+
if (info === 25)
|
|
32
|
+
return [3, bytes.readUInt16BE(1)];
|
|
33
|
+
if (info === 26)
|
|
34
|
+
return [5, bytes.readUInt32BE(1)];
|
|
35
|
+
throw new Error(`unsupported CBOR byte string header 0x${bytes[0].toString(16)}`);
|
|
36
|
+
};
|
|
37
|
+
const isDoubleCbor = (bytes) => {
|
|
38
|
+
const [headerLength] = byteStringHeader(bytes);
|
|
39
|
+
return bytes[headerLength] >> 5 === 2;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Scripts come in two forms: "single CBOR" — a CBOR byte string of the flat program, as Aiken
|
|
43
|
+
* blueprints, Blockfrost `/scripts/{hash}/cbor` and cardano-sdk's `PlutusScript.bytes` hold it — and
|
|
44
|
+
* "double CBOR" — that wrapped once more, as scalus reads and writes it.
|
|
45
|
+
*/
|
|
46
|
+
const toDoubleCbor = (cborHex) => {
|
|
47
|
+
const bytes = Buffer.from(cborHex, 'hex');
|
|
48
|
+
return isDoubleCbor(bytes) ? cborHex : Buffer.concat([cborByteStringHeader(bytes.length), bytes]).toString('hex');
|
|
49
|
+
};
|
|
50
|
+
exports.toDoubleCbor = toDoubleCbor;
|
|
51
|
+
const toSingleCbor = (cborHex) => {
|
|
52
|
+
const bytes = Buffer.from(cborHex, 'hex');
|
|
53
|
+
if (!isDoubleCbor(bytes))
|
|
54
|
+
return cborHex;
|
|
55
|
+
const [headerLength, length] = byteStringHeader(bytes);
|
|
56
|
+
return bytes.subarray(headerLength, headerLength + length).toString('hex');
|
|
57
|
+
};
|
|
58
|
+
exports.toSingleCbor = toSingleCbor;
|
|
59
|
+
/** Plutus data in the node's JSON form (what scalus.applyDataArgToScript reads). */
|
|
60
|
+
const plutusDataToJson = (data) => {
|
|
61
|
+
const c = data.asConstrPlutusData();
|
|
62
|
+
if (c) {
|
|
63
|
+
const fields = c.getData();
|
|
64
|
+
return { constructor: Number(c.getAlternative()), fields: Array.from({ length: fields.getLength() }, (_, i) => (0, exports.plutusDataToJson)(fields.get(i))) };
|
|
65
|
+
}
|
|
66
|
+
const bytes = data.asBoundedBytes();
|
|
67
|
+
if (bytes)
|
|
68
|
+
return { bytes: Buffer.from(bytes).toString('hex') };
|
|
69
|
+
const integer = data.asInteger();
|
|
70
|
+
if (integer !== undefined) {
|
|
71
|
+
if (integer > BigInt(Number.MAX_SAFE_INTEGER) || integer < BigInt(Number.MIN_SAFE_INTEGER))
|
|
72
|
+
throw new Error(`integer parameter ${integer} is outside the safe range scalus accepts`);
|
|
73
|
+
return { int: Number(integer) };
|
|
74
|
+
}
|
|
75
|
+
const items = data.asList();
|
|
76
|
+
if (items)
|
|
77
|
+
return { list: Array.from({ length: items.getLength() }, (_, i) => (0, exports.plutusDataToJson)(items.get(i))) };
|
|
78
|
+
const map = data.asMap();
|
|
79
|
+
if (map) {
|
|
80
|
+
const keys = map.getKeys();
|
|
81
|
+
return { map: Array.from({ length: keys.getLength() }, (_, i) => ({ k: (0, exports.plutusDataToJson)(keys.get(i)), v: (0, exports.plutusDataToJson)(map.get(keys.get(i))) })) };
|
|
82
|
+
}
|
|
83
|
+
throw new Error('unsupported Plutus data');
|
|
84
|
+
};
|
|
85
|
+
exports.plutusDataToJson = plutusDataToJson;
|
|
86
|
+
/** Apply parameters (in order) to a compiled validator; returns the script in single-CBOR form. */
|
|
87
|
+
const applyParamsToScript = (compiledCode, params) => (0, exports.toSingleCbor)(params.reduce((script, param) => scalus().Scalus.applyDataArgToScript(script, JSON.stringify((0, exports.plutusDataToJson)(param))), (0, exports.toDoubleCbor)(compiledCode)));
|
|
88
|
+
exports.applyParamsToScript = applyParamsToScript;
|
|
89
|
+
/** A Plutus script (single- or double-CBOR) as a cardano-sdk Script. */
|
|
90
|
+
const plutusScript = (cborHex, version) => ({
|
|
91
|
+
__type: core_1.Cardano.ScriptType.Plutus,
|
|
92
|
+
version,
|
|
93
|
+
bytes: (0, exports.toSingleCbor)(cborHex)
|
|
94
|
+
});
|
|
95
|
+
exports.plutusScript = plutusScript;
|
|
96
|
+
/** The script hash of a (single- or double-CBOR) Plutus script of `version`. */
|
|
97
|
+
const plutusScriptHash = (cborHex, version) => core_1.Serialization.Script.fromCore((0, exports.plutusScript)(cborHex, version)).hash();
|
|
98
|
+
exports.plutusScriptHash = plutusScriptHash;
|
|
99
|
+
const PURPOSE = { spend: 'spend', mint: 'mint', cert: 'certificate', certificate: 'certificate', reward: 'withdrawal', withdraw: 'withdrawal', withdrawal: 'withdrawal' };
|
|
100
|
+
/**
|
|
101
|
+
* An Evaluator that runs the tx's scripts locally (scalus) against `utxos` — every spent AND
|
|
102
|
+
* referenced UTxO, reference scripts included. Offline stand-in for the node's evaluation.
|
|
103
|
+
*/
|
|
104
|
+
const localEvaluator = ({ utxos, costModels, network }) => {
|
|
105
|
+
const writer = new core_1.Serialization.CborWriter();
|
|
106
|
+
writer.writeStartMap(utxos.length);
|
|
107
|
+
for (const [txIn, txOut] of utxos) {
|
|
108
|
+
writer.writeEncodedValue(Buffer.from(core_1.Serialization.TransactionInput.fromCore(txIn).toCbor(), 'hex'));
|
|
109
|
+
writer.writeEncodedValue(Buffer.from(core_1.Serialization.TransactionOutput.fromCore(txOut).toCbor(), 'hex'));
|
|
110
|
+
}
|
|
111
|
+
const utxoCbor = writer.encode();
|
|
112
|
+
const models = [core_1.Cardano.PlutusLanguageVersion.V1, core_1.Cardano.PlutusLanguageVersion.V2, core_1.Cardano.PlutusLanguageVersion.V3].map((v) => { var _a; return (_a = costModels.get(v)) !== null && _a !== void 0 ? _a : []; });
|
|
113
|
+
return async (txCbor) => {
|
|
114
|
+
const { Scalus, SlotConfig } = scalus();
|
|
115
|
+
const results = Scalus.evalPlutusScripts(Buffer.from(txCbor, 'hex'), utxoCbor, SlotConfig[network], models);
|
|
116
|
+
return new Map(results.map((r) => { var _a; return [`${(_a = PURPOSE[String(r.tag).toLowerCase()]) !== null && _a !== void 0 ? _a : String(r.tag).toLowerCase()}:${r.index}`, { memory: Number(r.budget.memory), steps: Number(r.budget.steps) }]; }));
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
exports.localEvaluator = localEvaluator;
|
package/txBuild/scriptTx.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { ExUnits, FeeParameters } from './fees';
|
|
|
3
3
|
export type PlutusData = Serialization.PlutusData;
|
|
4
4
|
export interface ScriptTxProtocolParameters extends FeeParameters {
|
|
5
5
|
coinsPerUtxoByte: bigint;
|
|
6
|
+
/** key_deposit: what a (pre-Conway-style) stake registration certificate locks. */
|
|
7
|
+
stakeKeyDeposit: bigint;
|
|
6
8
|
maxTxSize: number;
|
|
7
9
|
maxTxExUnits: {
|
|
8
10
|
memory: number;
|
|
@@ -32,6 +34,8 @@ export interface ScriptTxPlan {
|
|
|
32
34
|
quantity: bigint;
|
|
33
35
|
redeemer?: PlutusData;
|
|
34
36
|
}[];
|
|
37
|
+
/** Stake (de)registrations; their deposits/refunds are balanced into the change. No certificate redeemers. */
|
|
38
|
+
certificates?: Cardano.Certificate[];
|
|
35
39
|
requiredSigners?: string[];
|
|
36
40
|
nativeScripts?: Cardano.NativeScript[];
|
|
37
41
|
validityInterval?: Cardano.ValidityInterval;
|
|
@@ -67,7 +71,15 @@ export declare const computeScriptDataHash: ({ redeemersCbor, datumsCbor, costMo
|
|
|
67
71
|
costModels: Map<Cardano.PlutusLanguageVersion, number[]>;
|
|
68
72
|
languages: Cardano.PlutusLanguageVersion[];
|
|
69
73
|
}) => string | undefined;
|
|
74
|
+
/** Lovelace a certificate locks (positive) or releases (negative). */
|
|
75
|
+
export declare const certificateDeposit: (certificate: Cardano.Certificate, stakeKeyDeposit: bigint) => bigint;
|
|
70
76
|
export declare const finalizeScriptTx: (plan: ScriptTxPlan, params: ScriptTxProtocolParameters, evaluate: Evaluator) => Promise<FinalizedScriptTx>;
|
|
77
|
+
/**
|
|
78
|
+
* Wallet inputs for a tx whose other inputs are fixed: ADA-only UTxOs first, largest first, until they
|
|
79
|
+
* hold `lovelace` (callers include a fee/min-UTxO allowance; the finalizer returns the excess as change).
|
|
80
|
+
* UTxOs carrying tokens are only used when ADA-only ones do not suffice (their tokens go to change).
|
|
81
|
+
*/
|
|
82
|
+
export declare const selectWalletInputs: (utxos: Cardano.Utxo[], lovelace: bigint, exclude?: Set<string>) => Cardano.Utxo[];
|
|
71
83
|
/**
|
|
72
84
|
* Add vkey witnesses to an unsigned tx by splicing them into its witness set byte-for-byte
|
|
73
85
|
* (`mergeWitnessSet`): the body (so the tx id and every signature) and the redeemer bytes the
|
package/txBuild/scriptTx.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addVkeyWitnesses = exports.finalizeScriptTx = exports.computeScriptDataHash = exports.withMinAda = void 0;
|
|
3
|
+
exports.addVkeyWitnesses = exports.selectWalletInputs = exports.finalizeScriptTx = exports.certificateDeposit = exports.computeScriptDataHash = exports.withMinAda = void 0;
|
|
4
4
|
// Deterministic Plutus transaction finalizer on @cardano-sdk/core (no input selection, no Helios).
|
|
5
5
|
//
|
|
6
6
|
// The caller fixes every input; this module derives redeemer pointers from the ledger's canonical
|
|
@@ -60,6 +60,22 @@ const placeholderSignatures = (count) => {
|
|
|
60
60
|
signatures.set((i + 1).toString(16).padStart(64, '0'), '0'.repeat(128));
|
|
61
61
|
return signatures;
|
|
62
62
|
};
|
|
63
|
+
/** Lovelace a certificate locks (positive) or releases (negative). */
|
|
64
|
+
const certificateDeposit = (certificate, stakeKeyDeposit) => {
|
|
65
|
+
switch (certificate.__typename) {
|
|
66
|
+
case core_1.Cardano.CertificateType.StakeRegistration:
|
|
67
|
+
return stakeKeyDeposit;
|
|
68
|
+
case core_1.Cardano.CertificateType.StakeDeregistration:
|
|
69
|
+
return -stakeKeyDeposit;
|
|
70
|
+
case core_1.Cardano.CertificateType.Registration:
|
|
71
|
+
return certificate.deposit;
|
|
72
|
+
case core_1.Cardano.CertificateType.Unregistration:
|
|
73
|
+
return -certificate.deposit;
|
|
74
|
+
default:
|
|
75
|
+
throw new Error(`Certificate ${certificate.__typename} is not supported by finalizeScriptTx`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
exports.certificateDeposit = certificateDeposit;
|
|
63
79
|
const sumValues = (values) => {
|
|
64
80
|
var _a, _b;
|
|
65
81
|
let coins = BigInt(0);
|
|
@@ -72,7 +88,7 @@ const sumValues = (values) => {
|
|
|
72
88
|
return { coins, assets };
|
|
73
89
|
};
|
|
74
90
|
const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
75
|
-
var _a, _b;
|
|
91
|
+
var _a, _b, _c;
|
|
76
92
|
const inputs = [...plan.inputs].sort((a, b) => byTxIn(a.utxo[0], b.utxo[0]));
|
|
77
93
|
const mint = [...((_a = plan.mint) !== null && _a !== void 0 ? _a : [])].sort((a, b) => hexCompare(a.policyId, b.policyId));
|
|
78
94
|
const withdrawals = [...((_b = plan.withdrawals) !== null && _b !== void 0 ? _b : [])].sort((a, b) => hexCompare(rewardAccountBytes(a.rewardAccount), rewardAccountBytes(b.rewardAccount)));
|
|
@@ -90,10 +106,11 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
|
90
106
|
}
|
|
91
107
|
const inValue = sumValues(inputs.map(({ utxo }) => utxo[1].value));
|
|
92
108
|
const withdrawn = withdrawals.reduce((sum, w) => sum + w.quantity, BigInt(0));
|
|
109
|
+
const deposited = ((_c = plan.certificates) !== null && _c !== void 0 ? _c : []).reduce((sum, c) => sum + (0, exports.certificateDeposit)(c, params.stakeKeyDeposit), BigInt(0));
|
|
93
110
|
const produced = sumValues(outputs.map((o) => o.value));
|
|
94
111
|
const changeFor = (fee) => {
|
|
95
112
|
var _a, _b, _c;
|
|
96
|
-
const coins = inValue.coins + withdrawn - produced.coins - fee;
|
|
113
|
+
const coins = inValue.coins + withdrawn - deposited - produced.coins - fee;
|
|
97
114
|
if (coins < BigInt(0))
|
|
98
115
|
throw new Error(`Inputs do not cover outputs + fee: ${-coins} lovelace short`);
|
|
99
116
|
const assets = new Map();
|
|
@@ -108,7 +125,7 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
|
108
125
|
return { address: plan.changeAddress, value: { coins, ...(assets.size ? { assets } : {}) } };
|
|
109
126
|
};
|
|
110
127
|
const assemble = (fee, exUnits, signatures) => {
|
|
111
|
-
var _a, _b, _c, _d;
|
|
128
|
+
var _a, _b, _c, _d, _e;
|
|
112
129
|
const change = changeFor(fee);
|
|
113
130
|
const redeemers = pointers.map(({ purpose, index, data }) => {
|
|
114
131
|
const units = exUnits.get(key(purpose, index));
|
|
@@ -125,8 +142,9 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
|
125
142
|
...(((_a = plan.referenceInputs) === null || _a === void 0 ? void 0 : _a.length) ? { referenceInputs: [...plan.referenceInputs].sort(byTxIn) } : {}),
|
|
126
143
|
...(((_b = plan.collateral) === null || _b === void 0 ? void 0 : _b.length) ? { collaterals: [...plan.collateral].sort(byTxIn) } : {}),
|
|
127
144
|
...(tokenMap.size ? { mint: tokenMap } : {}),
|
|
145
|
+
...(((_c = plan.certificates) === null || _c === void 0 ? void 0 : _c.length) ? { certificates: plan.certificates } : {}),
|
|
128
146
|
...(withdrawals.length ? { withdrawals: withdrawals.map((w) => ({ stakeAddress: w.rewardAccount, quantity: w.quantity })) } : {}),
|
|
129
|
-
...(((
|
|
147
|
+
...(((_d = plan.requiredSigners) === null || _d === void 0 ? void 0 : _d.length) ? { requiredExtraSignatures: [...new Set(plan.requiredSigners)].sort(hexCompare) } : {}),
|
|
130
148
|
...(plan.validityInterval ? { validityInterval: plan.validityInterval } : {}),
|
|
131
149
|
...(scriptIntegrityHash ? { scriptIntegrityHash: scriptIntegrityHash } : {})
|
|
132
150
|
};
|
|
@@ -136,7 +154,7 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
|
136
154
|
witness: {
|
|
137
155
|
signatures: signatures,
|
|
138
156
|
...(redeemers.length ? { redeemers } : {}),
|
|
139
|
-
...(((
|
|
157
|
+
...(((_e = plan.nativeScripts) === null || _e === void 0 ? void 0 : _e.length) ? { scripts: plan.nativeScripts } : {})
|
|
140
158
|
},
|
|
141
159
|
isValid: true
|
|
142
160
|
});
|
|
@@ -183,6 +201,36 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
|
|
|
183
201
|
return { cbor: tx.toCbor(), txId: tx.getId(), fee, outputs: [...outputs, change], redeemers };
|
|
184
202
|
};
|
|
185
203
|
exports.finalizeScriptTx = finalizeScriptTx;
|
|
204
|
+
/**
|
|
205
|
+
* Wallet inputs for a tx whose other inputs are fixed: ADA-only UTxOs first, largest first, until they
|
|
206
|
+
* hold `lovelace` (callers include a fee/min-UTxO allowance; the finalizer returns the excess as change).
|
|
207
|
+
* UTxOs carrying tokens are only used when ADA-only ones do not suffice (their tokens go to change).
|
|
208
|
+
*/
|
|
209
|
+
const selectWalletInputs = (utxos, lovelace, exclude = new Set()) => {
|
|
210
|
+
const key = ([txIn]) => `${txIn.txId}#${txIn.index}`;
|
|
211
|
+
const candidates = utxos
|
|
212
|
+
.filter((u) => !exclude.has(key(u)))
|
|
213
|
+
.sort((a, b) => {
|
|
214
|
+
var _a, _b;
|
|
215
|
+
const tokensA = ((_a = a[1].value.assets) === null || _a === void 0 ? void 0 : _a.size) ? 1 : 0;
|
|
216
|
+
const tokensB = ((_b = b[1].value.assets) === null || _b === void 0 ? void 0 : _b.size) ? 1 : 0;
|
|
217
|
+
if (tokensA !== tokensB)
|
|
218
|
+
return tokensA - tokensB;
|
|
219
|
+
return a[1].value.coins === b[1].value.coins ? 0 : a[1].value.coins > b[1].value.coins ? -1 : 1;
|
|
220
|
+
});
|
|
221
|
+
const selected = [];
|
|
222
|
+
let total = BigInt(0);
|
|
223
|
+
for (const utxo of candidates) {
|
|
224
|
+
if (total >= lovelace)
|
|
225
|
+
break;
|
|
226
|
+
selected.push(utxo);
|
|
227
|
+
total += utxo[1].value.coins;
|
|
228
|
+
}
|
|
229
|
+
if (total < lovelace)
|
|
230
|
+
throw new Error(`Wallet holds ${total} spendable lovelace; ${lovelace} needed`);
|
|
231
|
+
return selected;
|
|
232
|
+
};
|
|
233
|
+
exports.selectWalletInputs = selectWalletInputs;
|
|
186
234
|
/**
|
|
187
235
|
* Add vkey witnesses to an unsigned tx by splicing them into its witness set byte-for-byte
|
|
188
236
|
* (`mergeWitnessSet`): the body (so the tx id and every signature) and the redeemer bytes the
|