@kronsdk/kron-sdk 0.4.0 → 0.6.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/dist/index.js CHANGED
@@ -163,12 +163,24 @@ var ZERO_COVID = "00".repeat(32);
163
163
  // src/native/spend.ts
164
164
  var spend_exports = {};
165
165
  __export(spend_exports, {
166
+ COVENANT_COMPUTE: () => COVENANT_COMPUTE,
167
+ COVENANT_DUST: () => COVENANT_DUST,
168
+ FUNDING_COMPUTE: () => FUNDING_COMPUTE,
169
+ TOKEN_COMPUTE: () => TOKEN_COMPUTE,
170
+ TX_VERSION: () => TX_VERSION,
166
171
  assembleNativeTx: () => assembleNativeTx,
172
+ estimateNativeFee: () => estimateNativeFee,
167
173
  signFundingInputs: () => signFundingInputs,
168
174
  signPsktWithKey: () => signPsktWithKey,
169
175
  toPsktJson: () => toPsktJson
170
176
  });
177
+ var TX_VERSION = 1;
178
+ var FUNDING_COMPUTE = 10;
179
+ var TOKEN_COMPUTE = 500;
180
+ var COVENANT_COMPUTE = 2e3;
181
+ var COVENANT_DUST = 50000000n;
171
182
  var SUBNET_ZERO = "0000000000000000000000000000000000000000";
183
+ var budgetForRole = (role) => role === "curve" || role === "pool" ? COVENANT_COMPUTE : TOKEN_COMPUTE;
172
184
  function assembleNativeTx(k, opts) {
173
185
  const { spend, fundingEntries, changeAddress, networkFee } = opts;
174
186
  const kk = k;
@@ -178,6 +190,7 @@ function assembleNativeTx(k, opts) {
178
190
  signatureScript: ci.signatureScript,
179
191
  sequence: 0n,
180
192
  sigOpCount: 0,
193
+ computeBudget: ci.computeBudget ?? budgetForRole(ci.role),
181
194
  utxo: {
182
195
  outpoint: { transactionId: ci.transactionId, index: ci.index },
183
196
  amount: ci.value,
@@ -188,7 +201,7 @@ function assembleNativeTx(k, opts) {
188
201
  })
189
202
  );
190
203
  const fundingInputs = fundingEntries.map(
191
- (e) => new kk.TransactionInput({ previousOutpoint: e.outpoint, signatureScript: "", sequence: 0n, sigOpCount: 1, utxo: e })
204
+ (e) => new kk.TransactionInput({ previousOutpoint: e.outpoint, signatureScript: "", sequence: 0n, sigOpCount: 0, computeBudget: FUNDING_COMPUTE, utxo: e })
192
205
  );
193
206
  const covInValue = spend.inputs.reduce((s, ci) => s + ci.value, 0n);
194
207
  const fundingTotal = fundingEntries.reduce((s, e) => s + BigInt(e.amount), 0n);
@@ -196,10 +209,12 @@ function assembleNativeTx(k, opts) {
196
209
  const covenantOut = spend.outputs.reduce((s, o) => s + o.value, 0n);
197
210
  const change = totalIn - covenantOut - networkFee;
198
211
  if (change < 0n) throw new Error(`insufficient funding: need ${covenantOut + networkFee} sompi, have ${totalIn}`);
199
- const outputs = spend.outputs.map((o) => new kk.TransactionOutput(o.value, o.scriptPublicKey));
212
+ const outputs = spend.outputs.map(
213
+ (o) => o.binding ? new kk.TransactionOutput(o.value, o.scriptPublicKey, new kk.CovenantBinding(o.binding.authorizingInput, new kk.Hash(o.binding.covid))) : new kk.TransactionOutput(o.value, o.scriptPublicKey)
214
+ );
200
215
  outputs.push(new kk.TransactionOutput(change, kk.payToAddressScript(changeAddress)));
201
216
  const transaction = new kk.Transaction({
202
- version: 0,
217
+ version: TX_VERSION,
203
218
  inputs: [...covInputs, ...fundingInputs],
204
219
  outputs,
205
220
  lockTime: 0n,
@@ -215,6 +230,27 @@ function assembleNativeTx(k, opts) {
215
230
  change
216
231
  };
217
232
  }
233
+ function estimateNativeFee(k, networkId, asm, feeRateSompiPerGram) {
234
+ const kk = k;
235
+ const tx = asm.transaction;
236
+ const ins = tx.inputs;
237
+ const saved = asm.fundingInputIndexes.map((i) => ins[i].signatureScript);
238
+ for (const i of asm.fundingInputIndexes) ins[i].signatureScript = "00".repeat(66);
239
+ tx.inputs = ins;
240
+ let byteMass = 2000n;
241
+ try {
242
+ byteMass = BigInt(kk.calculateTransactionMass(networkId, tx));
243
+ } catch {
244
+ }
245
+ const ins2 = tx.inputs;
246
+ asm.fundingInputIndexes.forEach((i, j) => ins2[i].signatureScript = saved[j]);
247
+ tx.inputs = ins2;
248
+ let computeGrams = 0n;
249
+ for (const inp of tx.inputs) computeGrams += BigInt(inp.computeBudget || 0) * 100n;
250
+ const rate = BigInt(Math.max(Math.ceil(feeRateSompiPerGram), 1));
251
+ const fee = (byteMass + computeGrams) * rate * 3n / 2n;
252
+ return fee > 10000n ? fee : 10000n;
253
+ }
218
254
  function signFundingInputs(k, tx, privKey, fundingInputIndexes) {
219
255
  const inputs = tx.inputs;
220
256
  for (const idx of fundingInputIndexes) {
@@ -247,7 +283,9 @@ var kcc20Tx_exports = {};
247
283
  __export(kcc20Tx_exports, {
248
284
  IDENTIFIER: () => IDENTIFIER,
249
285
  addressPresenceOwned: () => addressPresenceOwned,
286
+ buildKcc20Send: () => buildKcc20Send,
250
287
  covenantIdOwned: () => covenantIdOwned,
288
+ decodeKcc20Redeem: () => decodeKcc20Redeem,
251
289
  kcc20Address: () => kcc20Address,
252
290
  kcc20Spk: () => kcc20Spk,
253
291
  kcc20SpkForState: () => kcc20SpkForState,
@@ -259,6 +297,7 @@ __export(kcc20Tx_exports, {
259
297
  transferSigScript: () => transferSigScript
260
298
  });
261
299
  var IDENTIFIER = { PUBKEY: 0, SCRIPT_HASH: 1, COVENANT_ID: 2, ADDRESS: 3 };
300
+ var STATE_LEN = 46;
262
301
  function materializeKcc20Script(tpl, state) {
263
302
  const s = tpl.stateStart;
264
303
  const t = tpl.script;
@@ -278,6 +317,25 @@ function materializeKcc20Script(tpl, state) {
278
317
  out[s + 45] = state.isMinter ? 1 : 0;
279
318
  return out;
280
319
  }
320
+ function decodeKcc20Redeem(redeem, opts = {}) {
321
+ const hits = [];
322
+ for (let s2 = 0; s2 + STATE_LEN <= redeem.length; s2++) {
323
+ if (redeem[s2] === 32 && redeem[s2 + 33] === 1 && redeem[s2 + 34] <= 3 && redeem[s2 + 35] === 8 && redeem[s2 + 44] === 1 && redeem[s2 + 45] <= 1) hits.push(s2);
324
+ }
325
+ if (hits.length !== 1) throw new Error(`could not locate the kcc20 state region in the redeem script (${hits.length} candidate offsets) \u2014 is this a kcc20 token UTXO?`);
326
+ const s = hits[0];
327
+ let amount = 0n;
328
+ for (let i = 7; i >= 0; i--) amount = amount << 8n | BigInt(redeem[s + 36 + i]);
329
+ return {
330
+ template: { script: redeem.slice(), stateStart: s, maxIns: opts.maxIns ?? 4, maxOuts: opts.maxOuts ?? 4 },
331
+ state: {
332
+ ownerIdentifier: redeem.slice(s + 1, s + 33),
333
+ identifierType: redeem[s + 34],
334
+ amount,
335
+ isMinter: redeem[s + 45] === 1
336
+ }
337
+ };
338
+ }
281
339
  var kcc20Spk = (k, redeem) => k.payToScriptHashScript(redeem);
282
340
  var kcc20SpkForState = (k, tpl, state) => kcc20Spk(k, materializeKcc20Script(tpl, state));
283
341
  function kcc20Address(k, tpl, state, network) {
@@ -327,6 +385,30 @@ function transferSigScript(k, redeem, newStates, witnesses, sigs = []) {
327
385
  b.redeem(redeem);
328
386
  return b.drain();
329
387
  }
388
+ function buildKcc20Send(k, tpl, senderTokens, recipientPubkey32, sendAmount, presenceWitnessIdx, tokenCovid, opts = {}) {
389
+ if (senderTokens.length < 1) throw new Error("send requires at least one token UTXO");
390
+ if (!tokenCovid) throw new Error("send requires the token covenant id (indexer token info `covenantId`) for the output bindings");
391
+ const total = senderTokens.reduce((s, t) => s + t.state.amount, 0n);
392
+ const change = total - sendAmount;
393
+ if (sendAmount < 1n || change < 0n) throw new Error(`send requires 1 <= sendAmount <= ${total} (the selected UTXOs' total)`);
394
+ const dust = opts.tokenDust ?? 50000000n;
395
+ const owner = senderTokens[0].state.ownerIdentifier;
396
+ const recipientOut = addressPresenceOwned(recipientPubkey32, sendAmount);
397
+ const newStates = change >= 1n ? [recipientOut, addressPresenceOwned(owner, change)] : [recipientOut];
398
+ const witnesses = senderTokens.map(() => presenceWitnessIdx);
399
+ const inputs = senderTokens.map((t) => {
400
+ const r = materializeKcc20Script(tpl, t.state);
401
+ return { transactionId: t.transactionId, index: t.index, value: t.value, scriptPublicKey: kcc20Spk(k, r), signatureScript: transferSigScript(k, r, newStates, witnesses), redeem: r, role: "token" };
402
+ });
403
+ const binding = { covid: tokenCovid, authorizingInput: 0 };
404
+ const outputs = newStates.map((st, i) => ({
405
+ value: dust,
406
+ scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tpl, st)),
407
+ role: i === 0 ? "send" : "change",
408
+ binding
409
+ }));
410
+ return { kind: "transfer", inputs, outputs, economics: { sendAmount, change }, covids: { tokenCovid } };
411
+ }
330
412
 
331
413
  // src/native/curveCpTx.ts
332
414
  var curveCpTx_exports = {};
@@ -496,6 +578,9 @@ function buildAddLiquidity(k, tpl, tokenTpl, utxo, lpInventory, poolCovid, lpDep
496
578
  if (lpDepositToken.state.amount !== q.dToken) throw new Error("LP deposit token UTXO must equal dToken exactly (split first)");
497
579
  const dust = opts.tokenDust ?? 1000n;
498
580
  const { kasReserve, tokenReserve, tokenCovid, lpCovid } = utxo.state;
581
+ const poolCovidHex = hexOf(poolCovid);
582
+ const tokenCovidHex = hexOf(tokenCovid);
583
+ const lpCovidHex = hexOf(lpCovid);
499
584
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
500
585
  const poolLpOut = covenantIdOwned(poolCovid, lpInventory.amount - q.dShares, false);
501
586
  const lpSharesOut = addressPresenceOwned(lpPubkey, q.dShares);
@@ -515,17 +600,20 @@ function buildAddLiquidity(k, tpl, tokenTpl, utxo, lpInventory, poolCovid, lpDep
515
600
  { transactionId: lpInventory.transactionId, index: lpInventory.index, value: lpInventory.value, scriptPublicKey: kcc20Spk(k, poolLpInvRedeem), signatureScript: transferSigScript(k, poolLpInvRedeem, lStates, lWitnesses), redeem: poolLpInvRedeem, role: "poolLpInventory" }
516
601
  ];
517
602
  const outputs = [
518
- { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool" },
519
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
520
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolLpOut)), role: "poolLpInventory" },
521
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, lpSharesOut)), role: "lpShares" }
603
+ { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
604
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken", binding: { covid: tokenCovidHex, authorizingInput: 2 } },
605
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolLpOut)), role: "poolLpInventory", binding: { covid: lpCovidHex, authorizingInput: 3 } },
606
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, lpSharesOut)), role: "lpShares", binding: { covid: lpCovidHex, authorizingInput: 3 } }
522
607
  ];
523
- return { kind: "addLiquidity", inputs, outputs, economics: { dKas: q.dKas, dToken: q.dToken, dShares: q.dShares, newShares: q.newShares }, covids: { poolCovid: hexOf(poolCovid), tokenCovid: hexOf(tokenCovid) } };
608
+ return { kind: "addLiquidity", inputs, outputs, economics: { dKas: q.dKas, dToken: q.dToken, dShares: q.dShares, newShares: q.newShares }, covids: { poolCovid: poolCovidHex, tokenCovid: tokenCovidHex } };
524
609
  }
525
610
  function buildRemoveLiquidity(k, tpl, tokenTpl, utxo, lpShares, poolCovid, lpPubkey, q, presenceWitnessIdx, opts = {}) {
526
611
  if (lpShares.state.amount !== q.dShares) throw new Error("LP shares UTXO must equal dShares exactly (split first)");
527
612
  const dust = opts.tokenDust ?? 1000n;
528
613
  const { kasReserve, tokenReserve, tokenCovid, lpCovid } = utxo.state;
614
+ const poolCovidHex = hexOf(poolCovid);
615
+ const tokenCovidHex = hexOf(tokenCovid);
616
+ const lpCovidHex = hexOf(lpCovid);
529
617
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
530
618
  const lpTokenOut = addressPresenceOwned(lpPubkey, q.dToken);
531
619
  const poolLpOut = covenantIdOwned(poolCovid, q.dShares, false);
@@ -543,12 +631,12 @@ function buildRemoveLiquidity(k, tpl, tokenTpl, utxo, lpShares, poolCovid, lpPub
543
631
  { transactionId: lpShares.transactionId, index: lpShares.index, value: lpShares.value, scriptPublicKey: kcc20Spk(k, lpSharesRedeem), signatureScript: transferSigScript(k, lpSharesRedeem, lStates, lWitnesses), redeem: lpSharesRedeem, role: "lpShares" }
544
632
  ];
545
633
  const outputs = [
546
- { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool" },
547
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
548
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, lpTokenOut)), role: "lpToken" },
549
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolLpOut)), role: "poolLpInventory" }
634
+ { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
635
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
636
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, lpTokenOut)), role: "lpToken", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
637
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolLpOut)), role: "poolLpInventory", binding: { covid: lpCovidHex, authorizingInput: 2 } }
550
638
  ];
551
- return { kind: "removeLiquidity", inputs, outputs, economics: { dShares: q.dShares, dKas: q.dKas, dToken: q.dToken, newShares: q.newShares }, covids: { poolCovid: hexOf(poolCovid), tokenCovid: hexOf(tokenCovid) } };
639
+ return { kind: "removeLiquidity", inputs, outputs, economics: { dShares: q.dShares, dKas: q.dKas, dToken: q.dToken, newShares: q.newShares }, covids: { poolCovid: poolCovidHex, tokenCovid: tokenCovidHex } };
552
640
  }
553
641
  function buildBindLp(k, tpl, tokenTpl, utxo, poolCovid, lockedShares, opts = {}) {
554
642
  if (utxo.state.lpCovid.length !== 32 || !utxo.state.lpCovid.every((b2) => b2 === 0)) throw new Error("pool lpCovid is already bound \u2014 bindLp is one-time");
@@ -576,12 +664,13 @@ function buildBindLp(k, tpl, tokenTpl, utxo, poolCovid, lockedShares, opts = {})
576
664
  const inputs = [
577
665
  { transactionId: utxo.transactionId, index: utxo.index, value: poolValue, scriptPublicKey: poolCpSpk(k, curRedeem), signatureScript: bindSig, redeem: curRedeem, role: "pool" }
578
666
  ];
667
+ const poolCovidHex = hexOf(poolCovid);
579
668
  const outputs = [
580
- { value: poolValue, scriptPublicKey: poolCpSpk(k, boundRedeem), role: "pool" },
581
- { value: dust, scriptPublicKey: floorSpk, role: "lpFloor" },
582
- { value: dust, scriptPublicKey: invSpk, role: "lpInventory" }
669
+ { value: poolValue, scriptPublicKey: poolCpSpk(k, boundRedeem), role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
670
+ { value: dust, scriptPublicKey: floorSpk, role: "lpFloor", binding: { covid: lpCovidHex, authorizingInput: 0 } },
671
+ { value: dust, scriptPublicKey: invSpk, role: "lpInventory", binding: { covid: lpCovidHex, authorizingInput: 0 } }
583
672
  ];
584
- return { kind: "bindLp", inputs, outputs, economics: { lockedShares, inventoryAmount }, covids: { poolCovid: hexOf(poolCovid), tokenCovid: hexOf(tokenCovid) }, lpCovidHex, lpInventoryAmount: inventoryAmount };
673
+ return { kind: "bindLp", inputs, outputs, economics: { lockedShares, inventoryAmount }, covids: { poolCovid: poolCovidHex, tokenCovid: hexOf(tokenCovid) }, lpCovidHex, lpInventoryAmount: inventoryAmount };
585
674
  }
586
675
 
587
676
  // src/native/curveCpTx.ts
@@ -639,6 +728,8 @@ function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey,
639
728
  if (tokenOut <= 0n || tokenOut >= inventory.amount) throw new Error("invalid tokenOut");
640
729
  if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
641
730
  const dust = opts.tokenDust ?? 1000n;
731
+ const curveCovidHex = hexOf2(curveCovid);
732
+ const tokenCovidHex = hexOf2(utxo.state.tokenCovid);
642
733
  const newKas = utxo.realKas + kasIn;
643
734
  if (newKas > MAX_KAS) throw new Error("buy exceeds the curve max raise (9,000,000 TKAS)");
644
735
  const newToken = inventory.amount - tokenOut;
@@ -664,13 +755,13 @@ function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey,
664
755
  })
665
756
  ];
666
757
  const outputs = [
667
- { value: newKas, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
668
- { value: dust, scriptPublicKey: kcc20Spk(k, invOutRedeem), role: "inventory" },
669
- { value: dust, scriptPublicKey: kcc20Spk(k, buyerRedeem), role: "recipient" },
758
+ { value: newKas, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve", binding: { covid: curveCovidHex, authorizingInput: 0 } },
759
+ { value: dust, scriptPublicKey: kcc20Spk(k, invOutRedeem), role: "inventory", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
760
+ { value: dust, scriptPublicKey: kcc20Spk(k, buyerRedeem), role: "recipient", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
670
761
  { value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
671
762
  { value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
672
763
  ];
673
- return { kind: "buy", inputs, outputs, economics: { kasIn, tokenOut, creatorFee, platformFee, newRealKas: newKas, newTokenReserve: newToken, merged: mergeSum }, covids: { tokenCovid: hexOf2(utxo.state.tokenCovid) } };
764
+ return { kind: "buy", inputs, outputs, economics: { kasIn, tokenOut, creatorFee, platformFee, newRealKas: newKas, newTokenReserve: newToken, merged: mergeSum }, covids: { tokenCovid: tokenCovidHex } };
674
765
  }
675
766
  function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid, traderPubkey, tokenIn, kasOut, presenceWitnessIdx, opts = {}) {
676
767
  if (utxo.state.graduated) throw new Error("curve has graduated \u2014 sells are locked");
@@ -679,6 +770,8 @@ function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid
679
770
  if (kasOut <= 0n || kasOut % SCALE2 !== 0n || kasOut > utxo.realKas) throw new Error("invalid kasOut");
680
771
  if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
681
772
  const dust = opts.tokenDust ?? 1000n;
773
+ const curveCovidHex = hexOf2(curveCovid);
774
+ const tokenCovidHex = hexOf2(utxo.state.tokenCovid);
682
775
  const sellerIn = sellerTokens.reduce((s, t) => s + t.state.amount, 0n);
683
776
  const change = sellerIn - tokenIn;
684
777
  if (change < 0n) throw new Error("seller inputs are less than the sell amount");
@@ -703,13 +796,13 @@ function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid
703
796
  })
704
797
  ];
705
798
  const outputs = [
706
- { value: utxo.realKas - kasOut, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
707
- { value: dust, scriptPublicKey: kcc20Spk(k, invOutRedeem), role: "inventory" },
799
+ { value: utxo.realKas - kasOut, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve", binding: { covid: curveCovidHex, authorizingInput: 0 } },
800
+ { value: dust, scriptPublicKey: kcc20Spk(k, invOutRedeem), role: "inventory", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
708
801
  { value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
709
802
  { value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
710
803
  ];
711
- if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "seller" });
712
- return { kind: "sell", inputs, outputs, economics: { tokenIn, kasOut, change, creatorFee, platformFee, newRealKas: utxo.realKas - kasOut, newTokenReserve: newToken }, covids: { tokenCovid: hexOf2(utxo.state.tokenCovid) } };
804
+ if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "seller", binding: { covid: tokenCovidHex, authorizingInput: 1 } });
805
+ return { kind: "sell", inputs, outputs, economics: { tokenIn, kasOut, change, creatorFee, platformFee, newRealKas: utxo.realKas - kasOut, newTokenReserve: newToken }, covids: { tokenCovid: tokenCovidHex } };
713
806
  }
714
807
  function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveCovid, poolLockedShares, opts = {}) {
715
808
  if (utxo.state.graduated) throw new Error("already graduated");
@@ -740,10 +833,12 @@ function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveC
740
833
  { transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: graduateSigV2(k, curRedeem, poolState, poolTokens), redeem: curRedeem, role: "curve" },
741
834
  { transactionId: inventory.transactionId, index: inventory.index, value: inventory.value, scriptPublicKey: kcc20Spk(k, invRedeem), signatureScript: transferSigScript(k, invRedeem, [poolTokens], [0]), redeem: invRedeem, role: "inventory" }
742
835
  ];
836
+ const curveCovidHex = hexOf2(curveCovid);
837
+ const tokenCovidHex = hexOf2(A);
743
838
  const outputs = [
744
- { value: lockedValue, scriptPublicKey: cpSpk(k, lockedRedeem), role: "curve" },
745
- { value: poolKas, scriptPublicKey: poolSpkV, role: "pool" },
746
- { value: dust, scriptPublicKey: kcc20Spk(k, poolTokenRedeem), role: "poolToken" },
839
+ { value: lockedValue, scriptPublicKey: cpSpk(k, lockedRedeem), role: "curve", binding: { covid: curveCovidHex, authorizingInput: 0 } },
840
+ { value: poolKas, scriptPublicKey: poolSpkV, role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
841
+ { value: dust, scriptPublicKey: kcc20Spk(k, poolTokenRedeem), role: "poolToken", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
747
842
  { value: padFee3(gradFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "gradFee" }
748
843
  ];
749
844
  return { kind: "graduate", inputs, outputs, economics: { poolKas, gradFee, leftover, poolLockedShares }, covids: { tokenCovid: hexOf2(A), poolCovid: poolCovidHex } };
@@ -756,14 +851,15 @@ function buildSplitToken(k, tokenTpl, sellerToken, sellAmount, presenceWitnessId
756
851
  const out1 = addressPresenceOwned(owner, sellAmount);
757
852
  const out2 = addressPresenceOwned(owner, change);
758
853
  const redeem = materializeKcc20Script(tokenTpl, sellerToken.state);
854
+ const binding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 0 } : void 0;
759
855
  const inputs = [
760
856
  { transactionId: sellerToken.transactionId, index: sellerToken.index, value: sellerToken.value, scriptPublicKey: kcc20Spk(k, redeem), signatureScript: transferSigScript(k, redeem, [out1, out2], [presenceWitnessIdx]), redeem, role: "sellerToken" }
761
857
  ];
762
858
  const outputs = [
763
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out1)), role: "split" },
764
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out2)), role: "change" }
859
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out1)), role: "split", binding },
860
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out2)), role: "change", binding }
765
861
  ];
766
- return { kind: "sell", inputs, outputs, economics: { sellAmount, change }, covids: { tokenCovid: hexOf2(owner) } };
862
+ return { kind: "sell", inputs, outputs, economics: { sellAmount, change }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
767
863
  }
768
864
  function buildConsolidate(k, tokenTpl, tokens, presenceWitnessIdx, opts = {}) {
769
865
  if (tokens.length < 2) throw new Error("consolidate needs at least 2 UTXOs");
@@ -777,10 +873,11 @@ function buildConsolidate(k, tokenTpl, tokens, presenceWitnessIdx, opts = {}) {
777
873
  const r = materializeKcc20Script(tokenTpl, t.state);
778
874
  return { transactionId: t.transactionId, index: t.index, value: t.value, scriptPublicKey: kcc20Spk(k, r), signatureScript: transferSigScript(k, r, newStates, witnesses), redeem: r, role: "token" };
779
875
  });
876
+ const binding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 0 } : void 0;
780
877
  const outputs = [
781
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, merged)), role: "merged" }
878
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, merged)), role: "merged", binding }
782
879
  ];
783
- return { kind: "sell", inputs, outputs, economics: { total }, covids: { tokenCovid: hexOf2(owner) } };
880
+ return { kind: "sell", inputs, outputs, economics: { total }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
784
881
  }
785
882
  var hexOf2 = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
786
883
 
@@ -825,6 +922,8 @@ function v3SwapSellSig(k, redeem, kasOutUnits, poolTokenOut, traderChangeOut) {
825
922
  function buildPoolV3SwapKasForToken(k, tpl, tokenTpl, params, utxo, poolCovid, traderPubkey, q, mergeTokens = [], presenceWitnessIdx = 0, opts = {}) {
826
923
  const dust = opts.tokenDust ?? 1000n;
827
924
  const { kasReserve, tokenReserve, tokenCovid, totalShares, lpCovid } = utxo.state;
925
+ const poolCovidHex = hexOf3(poolCovid);
926
+ const tokenCovidHex = hexOf3(tokenCovid);
828
927
  const mergeSum = mergeTokens.reduce((s, t) => s + t.state.amount, 0n);
829
928
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
830
929
  const traderTokenOut = addressPresenceOwned(traderPubkey, q.tokenOut + mergeSum);
@@ -842,18 +941,20 @@ function buildPoolV3SwapKasForToken(k, tpl, tokenTpl, params, utxo, poolCovid, t
842
941
  })
843
942
  ];
844
943
  const outputs = [
845
- { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool" },
846
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
847
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderTokenOut)), role: "trader" },
944
+ { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
945
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
946
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderTokenOut)), role: "trader", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
848
947
  { value: q.creatorOut, scriptPublicKey: p2pkSpk2(k, params.creatorFeeOwner), role: "creatorFee" },
849
948
  { value: q.platformOut, scriptPublicKey: p2pkSpk2(k, params.platformFeeOwner), role: "platformFee" }
850
949
  ];
851
- return { kind: "swapKasForToken", inputs, outputs, economics: { kasIn: q.kasIn, tokenOut: q.tokenOut }, covids: { poolCovid: hexOf3(poolCovid), tokenCovid: hexOf3(tokenCovid) } };
950
+ return { kind: "swapKasForToken", inputs, outputs, economics: { kasIn: q.kasIn, tokenOut: q.tokenOut }, covids: { poolCovid: poolCovidHex, tokenCovid: tokenCovidHex } };
852
951
  }
853
952
  function buildPoolV3SwapTokenForKas(k, tpl, tokenTpl, params, utxo, poolCovid, traderPubkey, traderTokens, q, presenceWitnessIdx, opts = {}) {
854
953
  if (traderTokens.length < 1) throw new Error("need at least one trader token");
855
954
  const dust = opts.tokenDust ?? 1000n;
856
955
  const { kasReserve, tokenReserve, tokenCovid, totalShares, lpCovid } = utxo.state;
956
+ const poolCovidHex = hexOf3(poolCovid);
957
+ const tokenCovidHex = hexOf3(tokenCovid);
857
958
  const traderIn = traderTokens.reduce((s, t) => s + t.state.amount, 0n);
858
959
  const change = traderIn - q.tokenIn;
859
960
  if (change < 0n) throw new Error("trader inputs are less than the sell amount");
@@ -874,13 +975,13 @@ function buildPoolV3SwapTokenForKas(k, tpl, tokenTpl, params, utxo, poolCovid, t
874
975
  })
875
976
  ];
876
977
  const outputs = [
877
- { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool" },
878
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
978
+ { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool", binding: { covid: poolCovidHex, authorizingInput: 0 } },
979
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken", binding: { covid: tokenCovidHex, authorizingInput: 1 } },
879
980
  { value: q.creatorOut, scriptPublicKey: p2pkSpk2(k, params.creatorFeeOwner), role: "creatorFee" },
880
981
  { value: q.platformOut, scriptPublicKey: p2pkSpk2(k, params.platformFeeOwner), role: "platformFee" }
881
982
  ];
882
- if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "trader" });
883
- return { kind: "swapTokenForKas", inputs, outputs, economics: { kasOut: q.kasOut, tokenIn: q.tokenIn }, covids: { poolCovid: hexOf3(poolCovid), tokenCovid: hexOf3(tokenCovid) } };
983
+ if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "trader", binding: { covid: tokenCovidHex, authorizingInput: 1 } });
984
+ return { kind: "swapTokenForKas", inputs, outputs, economics: { kasOut: q.kasOut, tokenIn: q.tokenIn }, covids: { poolCovid: poolCovidHex, tokenCovid: tokenCovidHex } };
884
985
  }
885
986
 
886
987
  // src/native/vestingTx.ts
@@ -895,6 +996,7 @@ __export(vestingTx_exports, {
895
996
  vestingSpkForState: () => vestingSpkForState
896
997
  });
897
998
  var VEST_SELECTOR = { claim: 0, claimFinal: 1 };
999
+ var hexOf4 = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
898
1000
  function vestedAmount(total, startScore, durationScore, daaScore) {
899
1001
  if (daaScore <= startScore) return 0n;
900
1002
  const elapsed = BigInt(daaScore - startScore);
@@ -920,6 +1022,8 @@ function buildVestingClaim(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vesti
920
1022
  const remaining = total - claimed;
921
1023
  if (release <= 0n || release >= remaining) throw new Error("partial claim must be > 0 and < remaining (use claimFinal to drain)");
922
1024
  const dust = opts.tokenDust ?? 1000n;
1025
+ const vestingCovidHex = hexOf4(vestingCovid);
1026
+ const tokenBinding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 1 } : void 0;
923
1027
  const newClaimed = claimed + release, newRemaining = remaining - release;
924
1028
  const curRedeem = materializeVestingScript(vestTpl, claimed);
925
1029
  const newRedeem = materializeVestingScript(vestTpl, newClaimed);
@@ -936,20 +1040,22 @@ function buildVestingClaim(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vesti
936
1040
  { transactionId: lockedToken.transactionId, index: lockedToken.index, value: lockedToken.value, scriptPublicKey: kcc20Spk(k, lockedRedeem), signatureScript: transferSigScript(k, lockedRedeem, [relockState, recipientState], [0]), redeem: lockedRedeem, role: "lockedToken" }
937
1041
  ];
938
1042
  const outputs = [
939
- { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting" },
1043
+ { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting", binding: { covid: vestingCovidHex, authorizingInput: 0 } },
940
1044
  // V continuation (claimed bumped)
941
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, relockState)), role: "relock" },
1045
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, relockState)), role: "relock", binding: tokenBinding },
942
1046
  // re-locked (A, V-owned)
943
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient" }
1047
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient", binding: tokenBinding }
944
1048
  // to creator (A, presence)
945
1049
  ];
946
- return { kind: "claim", inputs, outputs, economics: { release, newClaimed } };
1050
+ return { kind: "claim", inputs, outputs, economics: { release, newClaimed }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
947
1051
  }
948
1052
  function buildVestingClaimFinal(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vestingCovid, creatorPubkey, claimed, opts = {}) {
949
1053
  const total = BigInt(vestTpl.params.total);
950
1054
  if (claimed < 0n || claimed >= total) throw new Error("nothing left to claim");
951
1055
  const remaining = total - claimed;
952
1056
  const dust = opts.tokenDust ?? 1000n;
1057
+ const vestingCovidHex = hexOf4(vestingCovid);
1058
+ const tokenBinding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 1 } : void 0;
953
1059
  const curRedeem = materializeVestingScript(vestTpl, claimed);
954
1060
  const newRedeem = materializeVestingScript(vestTpl, total);
955
1061
  const lockedState = covenantIdOwned(vestingCovid, remaining, false);
@@ -963,10 +1069,10 @@ function buildVestingClaimFinal(k, vestTpl, tokenTpl, vestingUtxo, lockedToken,
963
1069
  { transactionId: lockedToken.transactionId, index: lockedToken.index, value: lockedToken.value, scriptPublicKey: kcc20Spk(k, lockedRedeem), signatureScript: transferSigScript(k, lockedRedeem, [recipientState], [0]), redeem: lockedRedeem, role: "lockedToken" }
964
1070
  ];
965
1071
  const outputs = [
966
- { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting" },
967
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient" }
1072
+ { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting", binding: { covid: vestingCovidHex, authorizingInput: 0 } },
1073
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient", binding: tokenBinding }
968
1074
  ];
969
- return { kind: "claimFinal", inputs, outputs, economics: { release: remaining } };
1075
+ return { kind: "claimFinal", inputs, outputs, economics: { release: remaining }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
970
1076
  }
971
1077
 
972
1078
  // src/wallet/index.ts