@hazbase/simplicity 0.0.5 → 0.1.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 +645 -148
- package/dist/cli.js +2125 -159
- package/dist/client/SimplicityClient.d.ts +64 -290
- package/dist/client/SimplicityClient.js +65 -23
- package/dist/core/executor.js +67 -92
- package/dist/core/outputBinding.d.ts +61 -0
- package/dist/core/outputBinding.js +552 -0
- package/dist/core/schnorr.d.ts +5 -0
- package/dist/core/schnorr.js +34 -0
- package/dist/core/types.d.ts +553 -1
- package/dist/docs/definitions/bond-anchor.simf +19 -0
- package/dist/docs/definitions/bond-definition.json +10 -0
- package/dist/docs/definitions/bond-descriptor-bound-settlement-machine.simf +144 -0
- package/dist/docs/definitions/bond-issuance-anchor.simf +26 -0
- package/dist/docs/definitions/bond-issuance-state-partial-redemption.json +18 -0
- package/dist/docs/definitions/bond-issuance-state-redeemed.json +18 -0
- package/dist/docs/definitions/bond-issuance-state.json +12 -0
- package/dist/docs/definitions/bond-redemption-state-machine.simf +118 -0
- package/dist/docs/definitions/bond-redemption-transition.simf +41 -0
- package/dist/docs/definitions/bond-script-bound-settlement-machine.simf +142 -0
- package/dist/docs/definitions/fund-capital-call-open.simf +82 -0
- package/dist/docs/definitions/fund-capital-call-refund-only.simf +33 -0
- package/dist/docs/definitions/fund-capital-call-state.json +11 -0
- package/dist/docs/definitions/fund-definition.json +8 -0
- package/dist/docs/definitions/fund-distribution-claim.simf +57 -0
- package/dist/docs/definitions/recursive-delay-direct-next.simf +60 -0
- package/dist/docs/definitions/recursive-delay-optional.simf +88 -0
- package/dist/docs/definitions/recursive-delay-required.simf +72 -0
- package/dist/docs/definitions/recursive-delay.simf +83 -0
- package/dist/docs/definitions/recursive-policy-transfer-machine.simf +65 -0
- package/dist/domain/bond.d.ts +8583 -721
- package/dist/domain/bond.js +1272 -31
- package/dist/domain/bondSettlementValidation.d.ts +2 -0
- package/dist/domain/bondSettlementValidation.js +28 -0
- package/dist/domain/bondValidation.d.ts +6 -0
- package/dist/domain/bondValidation.js +65 -3
- package/dist/domain/fund.d.ts +2069 -0
- package/dist/domain/fund.js +1384 -0
- package/dist/domain/fundValidation.d.ts +122 -0
- package/dist/domain/fundValidation.js +635 -0
- package/dist/domain/policies.d.ts +1051 -0
- package/dist/domain/policies.js +1605 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +85 -21
- package/package.json +15 -2
package/dist/core/executor.js
CHANGED
|
@@ -15,6 +15,7 @@ const summary_1 = require("./summary");
|
|
|
15
15
|
const templating_1 = require("./templating");
|
|
16
16
|
const presets_1 = require("./presets");
|
|
17
17
|
const toolchain_1 = require("./toolchain");
|
|
18
|
+
const rpc_1 = require("./rpc");
|
|
18
19
|
const DEFAULT_FEE_SAT = 100;
|
|
19
20
|
const DEFAULT_SEQUENCE = 4294967293;
|
|
20
21
|
const DUST_MARGIN_SAT = 600;
|
|
@@ -51,6 +52,13 @@ function getArtifactLocktime(artifact) {
|
|
|
51
52
|
}
|
|
52
53
|
return 0;
|
|
53
54
|
}
|
|
55
|
+
function getEffectiveLocktime(artifact, input) {
|
|
56
|
+
const candidate = input.locktimeHeight;
|
|
57
|
+
if (typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 0) {
|
|
58
|
+
return Math.trunc(candidate);
|
|
59
|
+
}
|
|
60
|
+
return getArtifactLocktime(artifact);
|
|
61
|
+
}
|
|
54
62
|
function parseSimcWitness(output) {
|
|
55
63
|
const lines = output
|
|
56
64
|
.split("\n")
|
|
@@ -97,13 +105,15 @@ function chooseUtxo(utxos, minSat, policy) {
|
|
|
97
105
|
const unconfirmed = sortCandidates(utxos.filter((utxo) => !utxo.confirmed).filter((utxo) => utxo.sat >= minSat));
|
|
98
106
|
return unconfirmed[0] ?? null;
|
|
99
107
|
}
|
|
100
|
-
async function
|
|
101
|
-
const
|
|
102
|
-
|
|
108
|
+
async function callRpc(config, method, params = [], wallet) {
|
|
109
|
+
const client = new rpc_1.ElementsRpcClient(config.rpc);
|
|
110
|
+
return client.call(method, params, wallet);
|
|
111
|
+
}
|
|
112
|
+
async function findContractUtxosInMempool(config, contractAddress) {
|
|
113
|
+
const txids = await callRpc(config, "getrawmempool", []);
|
|
103
114
|
const matches = [];
|
|
104
115
|
for (const txid of txids) {
|
|
105
|
-
const
|
|
106
|
-
const parsed = JSON.parse(tx.stdout);
|
|
116
|
+
const parsed = await callRpc(config, "getrawtransaction", [txid, true]);
|
|
107
117
|
for (const output of parsed.vout ?? []) {
|
|
108
118
|
if (output.scriptPubKey?.address !== contractAddress)
|
|
109
119
|
continue;
|
|
@@ -119,10 +129,8 @@ async function findContractUtxosInMempool(elementsCliPath, contractAddress) {
|
|
|
119
129
|
}
|
|
120
130
|
return matches;
|
|
121
131
|
}
|
|
122
|
-
async function scanUtxosByAddress(
|
|
123
|
-
const
|
|
124
|
-
const result = await (0, toolchain_1.runCommand)(elementsCliPath, ["scantxoutset", "start", pattern]);
|
|
125
|
-
const parsed = JSON.parse(result.stdout);
|
|
132
|
+
async function scanUtxosByAddress(config, contractAddress) {
|
|
133
|
+
const parsed = await callRpc(config, "scantxoutset", ["start", [`addr(${contractAddress})`]]);
|
|
126
134
|
if (!parsed.success || !Array.isArray(parsed.unspents)) {
|
|
127
135
|
throw new errors_1.ExecutionError("scantxoutset failed", parsed);
|
|
128
136
|
}
|
|
@@ -135,33 +143,29 @@ async function scanUtxosByAddress(elementsCliPath, contractAddress) {
|
|
|
135
143
|
height: utxo.height,
|
|
136
144
|
confirmed: true,
|
|
137
145
|
}));
|
|
138
|
-
const mempool = confirmed.length === 0 ? await findContractUtxosInMempool(
|
|
146
|
+
const mempool = confirmed.length === 0 ? await findContractUtxosInMempool(config, contractAddress) : [];
|
|
139
147
|
return [...confirmed, ...mempool];
|
|
140
148
|
}
|
|
141
|
-
async function decodePsbt(
|
|
142
|
-
|
|
143
|
-
return JSON.parse(result.stdout);
|
|
149
|
+
async function decodePsbt(config, psetBase64, wallet) {
|
|
150
|
+
return callRpc(config, "decodepsbt", [psetBase64], wallet);
|
|
144
151
|
}
|
|
145
|
-
async function getAddressInfo(
|
|
146
|
-
const
|
|
147
|
-
const parsed = JSON.parse(result.stdout);
|
|
152
|
+
async function getAddressInfo(config, wallet, address) {
|
|
153
|
+
const parsed = await callRpc(config, "getaddressinfo", [address], wallet);
|
|
148
154
|
if (!parsed.scriptPubKey) {
|
|
149
155
|
throw new errors_1.ExecutionError(`Could not get scriptPubKey from address: ${address}`);
|
|
150
156
|
}
|
|
151
157
|
return { scriptPubKey: parsed.scriptPubKey, unconfidential: parsed.unconfidential };
|
|
152
158
|
}
|
|
153
|
-
async function allocateWalletAddress(
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const info = await getAddressInfo(elementsCliPath, wallet, address);
|
|
159
|
+
async function allocateWalletAddress(config, wallet) {
|
|
160
|
+
const address = await callRpc(config, "getnewaddress", [], wallet);
|
|
161
|
+
const info = await getAddressInfo(config, wallet, address);
|
|
157
162
|
return {
|
|
158
163
|
address: info.unconfidential ?? address,
|
|
159
164
|
scriptPubKey: info.scriptPubKey,
|
|
160
165
|
};
|
|
161
166
|
}
|
|
162
|
-
async function listWalletUtxos(
|
|
163
|
-
const
|
|
164
|
-
const parsed = JSON.parse(result.stdout);
|
|
167
|
+
async function listWalletUtxos(config, wallet) {
|
|
168
|
+
const parsed = await callRpc(config, "listunspent", [0, 9999999, [], true], wallet);
|
|
165
169
|
return parsed.map((entry) => ({
|
|
166
170
|
txid: entry.txid,
|
|
167
171
|
vout: entry.vout,
|
|
@@ -233,18 +237,17 @@ function buildPsetSummary(decoded, meta) {
|
|
|
233
237
|
fee: decoded.fees ?? decoded.fee ?? null,
|
|
234
238
|
};
|
|
235
239
|
}
|
|
236
|
-
async function getScriptPubKeyHexFromAddress(
|
|
237
|
-
const
|
|
238
|
-
const parsed = JSON.parse(result.stdout);
|
|
240
|
+
async function getScriptPubKeyHexFromAddress(config, address, wallet) {
|
|
241
|
+
const parsed = await callRpc(config, "getaddressinfo", [address], wallet);
|
|
239
242
|
if (!parsed.scriptPubKey) {
|
|
240
243
|
throw new errors_1.ExecutionError(`Could not get scriptPubKey from address: ${address}`);
|
|
241
244
|
}
|
|
242
245
|
return parsed.scriptPubKey.toLowerCase();
|
|
243
246
|
}
|
|
244
|
-
async function assertSummaryPolicy(
|
|
247
|
+
async function assertSummaryPolicy(config, summary, expectedLiquidReceiver, wallet) {
|
|
245
248
|
if (!expectedLiquidReceiver)
|
|
246
249
|
return;
|
|
247
|
-
const expectedSpk = await getScriptPubKeyHexFromAddress(
|
|
250
|
+
const expectedSpk = await getScriptPubKeyHexFromAddress(config, expectedLiquidReceiver, wallet);
|
|
248
251
|
const normalOutputs = summary.outputs.filter((output) => output.isFee !== true);
|
|
249
252
|
const found = normalOutputs.some((output) => (output.scriptPubKeyHex ?? "").toLowerCase() === expectedSpk);
|
|
250
253
|
if (!found) {
|
|
@@ -256,14 +259,13 @@ async function assertSummaryPolicy(elementsCliPath, summary, expectedLiquidRecei
|
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
261
|
async function buildExecutionState(config, artifact, input) {
|
|
259
|
-
|
|
260
|
-
await (0, toolchain_1.runCommand)(elementsCliPath, [`-rpcwallet=${input.wallet}`, "getwalletinfo"]);
|
|
262
|
+
await callRpc(config, "getwalletinfo", [], input.wallet);
|
|
261
263
|
const feeSat = input.feeSat ?? config.defaults?.feeSat ?? DEFAULT_FEE_SAT;
|
|
262
264
|
const minSat = feeSat + DUST_MARGIN_SAT;
|
|
263
265
|
const utxoPolicy = input.utxoPolicy ?? config.defaults?.utxoPolicy ?? "smallest_over";
|
|
264
|
-
const recipientInfo = await getAddressInfo(
|
|
266
|
+
const recipientInfo = await getAddressInfo(config, input.wallet, input.toAddress);
|
|
265
267
|
const recipientAddress = recipientInfo.unconfidential ?? input.toAddress;
|
|
266
|
-
const utxos = await scanUtxosByAddress(
|
|
268
|
+
const utxos = await scanUtxosByAddress(config, artifact.compiled.contractAddress);
|
|
267
269
|
const contractUtxo = chooseUtxo(utxos, minSat, utxoPolicy);
|
|
268
270
|
if (!contractUtxo) {
|
|
269
271
|
throw new errors_1.UtxoNotFoundError(`No contract UTXO found for address=${artifact.compiled.contractAddress} satisfying minSat=${minSat}`);
|
|
@@ -278,29 +280,24 @@ async function buildExecutionState(config, artifact, input) {
|
|
|
278
280
|
sendSat,
|
|
279
281
|
});
|
|
280
282
|
}
|
|
281
|
-
const inputsJson =
|
|
283
|
+
const inputsJson = [
|
|
282
284
|
{
|
|
283
285
|
txid: contractUtxo.txid,
|
|
284
286
|
vout: contractUtxo.vout,
|
|
285
287
|
sequence: input.sequence ?? DEFAULT_SEQUENCE,
|
|
286
288
|
},
|
|
287
|
-
]
|
|
288
|
-
const outputsJson =
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
outputsJson,
|
|
293
|
-
String(getArtifactLocktime(artifact)),
|
|
294
|
-
"true",
|
|
295
|
-
]);
|
|
296
|
-
const psetUpdated = await (0, toolchain_1.runCommand)(elementsCliPath, ["utxoupdatepsbt", pset1.stdout]);
|
|
289
|
+
];
|
|
290
|
+
const outputsJson = [{ [recipientAddress]: satToBtcNumber(sendSat) }, { fee: satToBtcNumber(feeSat) }];
|
|
291
|
+
const locktime = getEffectiveLocktime(artifact, input);
|
|
292
|
+
const pset1 = await callRpc(config, "createpsbt", [inputsJson, outputsJson, locktime, true], input.wallet);
|
|
293
|
+
const psetUpdated = await callRpc(config, "utxoupdatepsbt", [pset1], input.wallet);
|
|
297
294
|
const utxoSpec = `${contractUtxo.scriptPubKey}:${contractUtxo.asset}:${satToBtcStringFromNumber(contractUtxo.sat)}`;
|
|
298
|
-
const updateJson = (await (0, toolchain_1.runHalUpdateInput)(config.toolchain.halSimplicityPath, psetUpdated
|
|
295
|
+
const updateJson = (await (0, toolchain_1.runHalUpdateInput)(config.toolchain.halSimplicityPath, psetUpdated, 0, utxoSpec, artifact.compiled.cmr, artifact.compiled.internalKey));
|
|
299
296
|
const pset2 = updateJson.pset;
|
|
300
297
|
if (!pset2) {
|
|
301
298
|
throw new errors_1.ExecutionError("update-input did not return a pset", updateJson);
|
|
302
299
|
}
|
|
303
|
-
const decoded = await decodePsbt(
|
|
300
|
+
const decoded = await decodePsbt(config, pset2, input.wallet);
|
|
304
301
|
const summary = buildPsetSummary(decoded, {
|
|
305
302
|
network: artifact.network,
|
|
306
303
|
purpose: input.purpose ?? "sdk_execute",
|
|
@@ -321,7 +318,7 @@ async function buildExecutionState(config, artifact, input) {
|
|
|
321
318
|
cmr: artifact.compiled.cmr,
|
|
322
319
|
internalKey: artifact.compiled.internalKey,
|
|
323
320
|
program: artifact.compiled.program,
|
|
324
|
-
minHeight:
|
|
321
|
+
minHeight: locktime || undefined,
|
|
325
322
|
});
|
|
326
323
|
const { canonicalJson: summaryCanonicalJson, hash: summaryHash } = (0, summary_1.summarize)(summary);
|
|
327
324
|
return {
|
|
@@ -347,9 +344,8 @@ async function inspectContractCall(config, artifact, input) {
|
|
|
347
344
|
};
|
|
348
345
|
}
|
|
349
346
|
async function executeContractCall(config, artifact, input) {
|
|
350
|
-
const elementsCliPath = config.toolchain.elementsCliPath ?? "eltc";
|
|
351
347
|
const state = await buildExecutionState(config, artifact, input);
|
|
352
|
-
await assertSummaryPolicy(
|
|
348
|
+
await assertSummaryPolicy(config, state.summary, input.expectedLiquidReceiver, input.wallet);
|
|
353
349
|
const finalizedPset = await signContractInput(config, artifact, state.pset2, input.signer.privkeyHex, input.witness);
|
|
354
350
|
const finalized = { pset: finalizedPset };
|
|
355
351
|
if (!finalized.pset) {
|
|
@@ -358,13 +354,11 @@ async function executeContractCall(config, artifact, input) {
|
|
|
358
354
|
const rawTxHex = normalizeRawTx(await (0, toolchain_1.runHalExtract)(config.toolchain.halSimplicityPath, finalized.pset));
|
|
359
355
|
let txId;
|
|
360
356
|
if (input.broadcast) {
|
|
361
|
-
const
|
|
362
|
-
const mempoolParsed = JSON.parse(mempool.stdout);
|
|
357
|
+
const mempoolParsed = await callRpc(config, "testmempoolaccept", [[rawTxHex]], input.wallet);
|
|
363
358
|
if (!Array.isArray(mempoolParsed) || mempoolParsed[0]?.allowed !== true) {
|
|
364
359
|
throw new errors_1.ExecutionError("testmempoolaccept rejected transaction", mempoolParsed);
|
|
365
360
|
}
|
|
366
|
-
|
|
367
|
-
txId = sendTx.stdout.trim();
|
|
361
|
+
txId = await callRpc(config, "sendrawtransaction", [rawTxHex], input.wallet);
|
|
368
362
|
}
|
|
369
363
|
return {
|
|
370
364
|
mode: "execute",
|
|
@@ -379,21 +373,19 @@ async function executeContractCall(config, artifact, input) {
|
|
|
379
373
|
};
|
|
380
374
|
}
|
|
381
375
|
async function findContractUtxos(config, artifact) {
|
|
382
|
-
|
|
383
|
-
return scanUtxosByAddress(elementsCliPath, artifact.compiled.contractAddress);
|
|
376
|
+
return scanUtxosByAddress(config, artifact.compiled.contractAddress);
|
|
384
377
|
}
|
|
385
378
|
async function executeGaslessContractCall(config, artifact, input) {
|
|
386
379
|
if (input.relayer) {
|
|
387
380
|
return executeRelayedGaslessContractCall(config, artifact, input, input.relayer);
|
|
388
381
|
}
|
|
389
|
-
const elementsCliPath = config.toolchain.elementsCliPath ?? "eltc";
|
|
390
382
|
const wallet = input.wallet ?? config.rpc.wallet ?? "simplicity-test";
|
|
391
383
|
if (!input.sponsorWallet) {
|
|
392
384
|
throw new errors_1.ValidationError("sponsorWallet is required when relayer is not provided");
|
|
393
385
|
}
|
|
394
|
-
await (
|
|
395
|
-
await (
|
|
396
|
-
const contractUtxos = await scanUtxosByAddress(
|
|
386
|
+
await callRpc(config, "getwalletinfo", [], wallet);
|
|
387
|
+
await callRpc(config, "getwalletinfo", [], input.sponsorWallet);
|
|
388
|
+
const contractUtxos = await scanUtxosByAddress(config, artifact.compiled.contractAddress);
|
|
397
389
|
const contractUtxo = chooseUtxo(contractUtxos, input.sendAmount ? Math.round(input.sendAmount * 1e8) : 1, input.utxoPolicy ?? config.defaults?.utxoPolicy ?? "smallest_over");
|
|
398
390
|
if (!contractUtxo) {
|
|
399
391
|
throw new errors_1.UtxoNotFoundError(`No contract UTXO found for address=${artifact.compiled.contractAddress}`);
|
|
@@ -410,11 +402,11 @@ async function executeGaslessContractCall(config, artifact, input) {
|
|
|
410
402
|
if (!input.contractChangeAddress) {
|
|
411
403
|
throw new errors_1.ValidationError("contractChangeAddress is required when sendAmount is smaller than the contract UTXO");
|
|
412
404
|
}
|
|
413
|
-
const changeInfo = await getAddressInfo(
|
|
405
|
+
const changeInfo = await getAddressInfo(config, wallet, input.contractChangeAddress);
|
|
414
406
|
contractChangeAddress = changeInfo.unconfidential ?? input.contractChangeAddress;
|
|
415
407
|
contractChangeScriptPubKey = changeInfo.scriptPubKey;
|
|
416
408
|
}
|
|
417
|
-
const sponsorUtxos = await listWalletUtxos(
|
|
409
|
+
const sponsorUtxos = await listWalletUtxos(config, input.sponsorWallet);
|
|
418
410
|
const sponsorInput = chooseSponsorUtxo(sponsorUtxos, feeSat);
|
|
419
411
|
if (!sponsorInput) {
|
|
420
412
|
throw new errors_1.UtxoNotFoundError(`No sponsor UTXO found in wallet=${input.sponsorWallet} for feeSat=${feeSat}`);
|
|
@@ -424,17 +416,17 @@ async function executeGaslessContractCall(config, artifact, input) {
|
|
|
424
416
|
let sponsorChangeScriptPubKey;
|
|
425
417
|
if (sponsorChangeSat > 0) {
|
|
426
418
|
if (input.sponsorChangeAddress) {
|
|
427
|
-
const info = await getAddressInfo(
|
|
419
|
+
const info = await getAddressInfo(config, input.sponsorWallet, input.sponsorChangeAddress);
|
|
428
420
|
sponsorChangeAddress = info.unconfidential ?? input.sponsorChangeAddress;
|
|
429
421
|
sponsorChangeScriptPubKey = info.scriptPubKey;
|
|
430
422
|
}
|
|
431
423
|
else {
|
|
432
|
-
const allocated = await allocateWalletAddress(
|
|
424
|
+
const allocated = await allocateWalletAddress(config, input.sponsorWallet);
|
|
433
425
|
sponsorChangeAddress = allocated.address;
|
|
434
426
|
sponsorChangeScriptPubKey = allocated.scriptPubKey;
|
|
435
427
|
}
|
|
436
428
|
}
|
|
437
|
-
const recipientInfo = await getAddressInfo(
|
|
429
|
+
const recipientInfo = await getAddressInfo(config, wallet, input.toAddress);
|
|
438
430
|
const recipientAddress = recipientInfo.unconfidential ?? input.toAddress;
|
|
439
431
|
const outputs = [{ [recipientAddress]: satToBtcNumber(sendSat) }];
|
|
440
432
|
if (contractChangeSat > 0 && contractChangeAddress) {
|
|
@@ -444,25 +436,19 @@ async function executeGaslessContractCall(config, artifact, input) {
|
|
|
444
436
|
outputs.push({ [sponsorChangeAddress]: satToBtcNumber(sponsorChangeSat) });
|
|
445
437
|
}
|
|
446
438
|
outputs.push({ fee: satToBtcNumber(feeSat) });
|
|
447
|
-
const inputsJson =
|
|
439
|
+
const inputsJson = [
|
|
448
440
|
{ txid: contractUtxo.txid, vout: contractUtxo.vout, sequence: DEFAULT_SEQUENCE },
|
|
449
441
|
{ txid: sponsorInput.txid, vout: sponsorInput.vout, sequence: DEFAULT_SEQUENCE },
|
|
450
|
-
]
|
|
451
|
-
const
|
|
452
|
-
const pset1 = await (
|
|
453
|
-
|
|
454
|
-
inputsJson,
|
|
455
|
-
outputsJson,
|
|
456
|
-
String(getArtifactLocktime(artifact)),
|
|
457
|
-
"true",
|
|
458
|
-
]);
|
|
459
|
-
const psetUpdated = await (0, toolchain_1.runCommand)(elementsCliPath, ["utxoupdatepsbt", pset1.stdout]);
|
|
442
|
+
];
|
|
443
|
+
const locktime = getEffectiveLocktime(artifact, input);
|
|
444
|
+
const pset1 = await callRpc(config, "createpsbt", [inputsJson, outputs, locktime, true], wallet);
|
|
445
|
+
const psetUpdated = await callRpc(config, "utxoupdatepsbt", [pset1], wallet);
|
|
460
446
|
const contractSpec = `${contractUtxo.scriptPubKey}:${contractUtxo.asset}:${satToBtcStringFromNumber(contractUtxo.sat)}`;
|
|
461
|
-
const contractUpdated = (await (0, toolchain_1.runHalUpdateInput)(config.toolchain.halSimplicityPath, psetUpdated
|
|
447
|
+
const contractUpdated = (await (0, toolchain_1.runHalUpdateInput)(config.toolchain.halSimplicityPath, psetUpdated, 0, contractSpec, artifact.compiled.cmr, artifact.compiled.internalKey));
|
|
462
448
|
if (!contractUpdated.pset) {
|
|
463
449
|
throw new errors_1.ExecutionError("update-input did not return a pset", contractUpdated);
|
|
464
450
|
}
|
|
465
|
-
const decoded = await decodePsbt(
|
|
451
|
+
const decoded = await decodePsbt(config, contractUpdated.pset, input.sponsorWallet);
|
|
466
452
|
const summary = buildPsetSummary(decoded, {
|
|
467
453
|
network: artifact.network,
|
|
468
454
|
purpose: "sdk_gasless_execute",
|
|
@@ -481,37 +467,26 @@ async function executeGaslessContractCall(config, artifact, input) {
|
|
|
481
467
|
cmr: artifact.compiled.cmr,
|
|
482
468
|
internalKey: artifact.compiled.internalKey,
|
|
483
469
|
program: artifact.compiled.program,
|
|
484
|
-
minHeight:
|
|
470
|
+
minHeight: locktime || undefined,
|
|
485
471
|
expectedLiquidReceiver: recipientAddress,
|
|
486
472
|
});
|
|
487
473
|
const { canonicalJson: summaryCanonicalJson, hash: summaryHash } = (0, summary_1.summarize)(summary);
|
|
488
474
|
const contractSignedPset = await signContractInput(config, artifact, contractUpdated.pset, input.signer.privkeyHex, input.witness);
|
|
489
|
-
const
|
|
490
|
-
`-rpcwallet=${input.sponsorWallet}`,
|
|
491
|
-
"walletprocesspsbt",
|
|
492
|
-
contractSignedPset,
|
|
493
|
-
"true",
|
|
494
|
-
"ALL",
|
|
495
|
-
"true",
|
|
496
|
-
]);
|
|
497
|
-
const sponsorSignedParsed = JSON.parse(sponsorSigned.stdout);
|
|
475
|
+
const sponsorSignedParsed = await callRpc(config, "walletprocesspsbt", [contractSignedPset, true, "ALL", true], input.sponsorWallet);
|
|
498
476
|
if (!sponsorSignedParsed.psbt) {
|
|
499
477
|
throw new errors_1.ExecutionError("walletprocesspsbt did not return a sponsor-signed pset", sponsorSignedParsed);
|
|
500
478
|
}
|
|
501
|
-
const
|
|
502
|
-
const finalizedParsed = JSON.parse(finalized.stdout);
|
|
479
|
+
const finalizedParsed = await callRpc(config, "finalizepsbt", [sponsorSignedParsed.psbt, true]);
|
|
503
480
|
if (!finalizedParsed.complete || !finalizedParsed.hex) {
|
|
504
481
|
throw new errors_1.ExecutionError("PSET was not complete after sponsor signing", finalizedParsed);
|
|
505
482
|
}
|
|
506
483
|
let txId;
|
|
507
484
|
if (input.broadcast) {
|
|
508
|
-
const
|
|
509
|
-
const mempoolParsed = JSON.parse(mempool.stdout);
|
|
485
|
+
const mempoolParsed = await callRpc(config, "testmempoolaccept", [[finalizedParsed.hex]], input.sponsorWallet);
|
|
510
486
|
if (!Array.isArray(mempoolParsed) || mempoolParsed[0]?.allowed !== true) {
|
|
511
487
|
throw new errors_1.ExecutionError("testmempoolaccept rejected transaction", mempoolParsed);
|
|
512
488
|
}
|
|
513
|
-
|
|
514
|
-
txId = sent.stdout.trim();
|
|
489
|
+
txId = await callRpc(config, "sendrawtransaction", [finalizedParsed.hex], input.sponsorWallet);
|
|
515
490
|
}
|
|
516
491
|
return {
|
|
517
492
|
mode: "gasless-execute",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { SimplicityClient } from "../client/SimplicityClient";
|
|
2
|
+
import type { BondOutputBindingMode, OutputBindingSupportEvaluation, OutputBindingSupportMatrix, OutputBindingReasonCode, OutputBindingSupportedForm, OutputForm, OutputRawFields } from "./types";
|
|
3
|
+
export declare const OUTPUT_BINDING_SUPPORT_MATRIX: OutputBindingSupportMatrix;
|
|
4
|
+
export declare function describeOutputBindingSupport(): OutputBindingSupportMatrix;
|
|
5
|
+
export declare function normalizeOutputForm(input?: Partial<OutputForm>): OutputForm;
|
|
6
|
+
interface ResolvedOutputRawFields {
|
|
7
|
+
assetBytesHex: string;
|
|
8
|
+
amountBytesHex: string;
|
|
9
|
+
nonceBytesHex: string;
|
|
10
|
+
scriptPubKeyHashHex: string;
|
|
11
|
+
rangeProofHashHex: string;
|
|
12
|
+
scriptComponentSource: "raw-bytes" | "hash";
|
|
13
|
+
rangeProofComponentSource: "raw-bytes" | "hash";
|
|
14
|
+
}
|
|
15
|
+
export declare function normalizeOutputRawFields(input?: Partial<OutputRawFields>): Partial<OutputRawFields> | undefined;
|
|
16
|
+
export declare function analyzeOutputRawFields(input?: Partial<OutputRawFields>): {
|
|
17
|
+
provided: boolean;
|
|
18
|
+
complete: boolean;
|
|
19
|
+
valid: boolean;
|
|
20
|
+
normalized?: ResolvedOutputRawFields;
|
|
21
|
+
missingFields: string[];
|
|
22
|
+
invalidFields: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function isExplicitV1OutputForm(input: OutputForm): boolean;
|
|
25
|
+
export declare function isExplicitAssetInputSupported(assetId: string): boolean;
|
|
26
|
+
export declare function resolveOutputBindingMode(requested: BondOutputBindingMode | undefined, nextOutputHash: string | undefined, nextOutputScriptHash: string | undefined): BondOutputBindingMode;
|
|
27
|
+
export declare function hashHexBytes(hex: string): string;
|
|
28
|
+
export declare function computeExplicitV1OutputHash(input: {
|
|
29
|
+
assetHex: string;
|
|
30
|
+
nextAmountSat: number;
|
|
31
|
+
nextOutputScriptHash: string;
|
|
32
|
+
}): string;
|
|
33
|
+
export declare function computeRawOutputV1Hash(input: Partial<OutputRawFields>): string;
|
|
34
|
+
export declare function getScriptPubKeyHexViaRpc(sdk: SimplicityClient, address: string): Promise<string>;
|
|
35
|
+
export declare function resolveExplicitAssetHex(sdk: SimplicityClient, assetId: string): Promise<string | undefined>;
|
|
36
|
+
export declare function resolveOutputBindingDecision(input: {
|
|
37
|
+
requestedBindingMode?: BondOutputBindingMode;
|
|
38
|
+
nextOutputHash?: string;
|
|
39
|
+
nextOutputScriptHash?: string;
|
|
40
|
+
autoDerivedNextOutputHash?: boolean;
|
|
41
|
+
explicitAssetSupported: boolean;
|
|
42
|
+
outputForm?: Partial<OutputForm>;
|
|
43
|
+
rawOutput?: Partial<OutputRawFields>;
|
|
44
|
+
}): {
|
|
45
|
+
requestedBindingMode: BondOutputBindingMode;
|
|
46
|
+
outputBindingMode: BondOutputBindingMode;
|
|
47
|
+
supportedForm: OutputBindingSupportedForm;
|
|
48
|
+
reasonCode: OutputBindingReasonCode;
|
|
49
|
+
autoDerived: boolean;
|
|
50
|
+
fallbackReason?: string;
|
|
51
|
+
outputForm: OutputForm;
|
|
52
|
+
};
|
|
53
|
+
export declare function evaluateOutputBindingSupport(input: {
|
|
54
|
+
assetId: string;
|
|
55
|
+
requestedBindingMode?: BondOutputBindingMode;
|
|
56
|
+
outputForm?: Partial<OutputForm>;
|
|
57
|
+
rawOutput?: Partial<OutputRawFields>;
|
|
58
|
+
nextOutputHash?: string;
|
|
59
|
+
nextOutputScriptAvailable?: boolean;
|
|
60
|
+
}): OutputBindingSupportEvaluation;
|
|
61
|
+
export {};
|