@kronsdk/kron-sdk 0.5.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/README.md CHANGED
@@ -4,16 +4,18 @@
4
4
  — a bonding-curve launchpad + AMM DEX — from any JS/TS environment.** Browser or Node. No custody, ever:
5
5
  this package only *builds* transactions; a wallet (yours, or your user's) signs them.
6
6
 
7
- > **Status: v0.5.0, testnet (TN10).** Read paths and the covenant builders are proven byte-identical to
7
+ > **Status: v0.6.0, testnet (TN10).** Read paths and the covenant builders are proven byte-identical to
8
8
  > KRON's own production code (see "Verification" below). Wallet signing is a documented interface plus a
9
9
  > generic reference implementation — see [`docs/WALLETS.md`](docs/WALLETS.md) for the contract and how to
10
10
  > adapt it to a specific wallet's injected provider.
11
11
  >
12
- > **⚠️ Upgrade from < 0.5.0.** Every earlier release built **version-0** transactions, which cannot carry
12
+ > **⚠️ Upgrade from < 0.6.0.** Every earlier release built **version-0** transactions, which cannot carry
13
13
  > the covenant bindings Kaspa's covenant layer (KIP-20) requires on output — every assembled spend was
14
- > rejected on-chain with `script ran, but verification failed`. Fixed in 0.5.0: `assembleNativeTx` now
15
- > builds v1 transactions with bindings + compute budgets attached. See the [CHANGELOG](CHANGELOG.md) for
16
- > the migration note if you assembled transactions by hand instead of via `spend.assembleNativeTx`.
14
+ > rejected on-chain with `script ran, but verification failed`. 0.5.0 fixed `assembleNativeTx` (v1 txs +
15
+ > compute budgets) and the kcc20 builders; 0.6.0 finishes the job for the curve, pool/LP, and vesting
16
+ > builders (buy/sell/graduate, swap, add/removeLiquidity, bindLp, claim) they now attach the required
17
+ > `CovOutput.binding` automatically. See the [CHANGELOG](CHANGELOG.md) for the migration notes if you
18
+ > assembled transactions by hand instead of via `spend.assembleNativeTx`.
17
19
 
18
20
  ## Why this exists
19
21
 
@@ -40,12 +42,12 @@ ESM only (`"type": "module"`) in v1 — see [Design notes](#design-notes) for wh
40
42
 
41
43
  ```bash
42
44
  npm install @kronsdk/kron-sdk@latest # newest
43
- npm install @kronsdk/kron-sdk@0.5.0 # or pin an exact version for reproducible builds
45
+ npm install @kronsdk/kron-sdk@0.6.0 # or pin an exact version for reproducible builds
44
46
  ```
45
47
 
46
- The package follows semver. **0.5.0 is a required upgrade for anyone using `spend.assembleNativeTx`** — see
47
- the warning above. The token-list client (`client.RegistryClient.tokenlist()`) and on-chain verifier
48
- (`verify.verifyTokenListEntry`) landed in **0.2.0** — see [Discover & verify tokens](#discover--verify-tokens-token-list).
48
+ The package follows semver. **0.6.0 is a required upgrade for anyone building curve/pool/LP/vesting
49
+ transactions** — see the warning above. The token-list client (`client.RegistryClient.tokenlist()`) and
50
+ on-chain verifier (`verify.verifyTokenListEntry`) landed in **0.2.0** — see [Discover & verify tokens](#discover--verify-tokens-token-list).
49
51
 
50
52
  ## Quickstart — quote a curve buy (Node)
51
53
 
package/dist/index.d.ts CHANGED
@@ -814,14 +814,23 @@ type VestUtxo = {
814
814
  * Partial claim: release `release` (0 < release < remaining) to the creator's presence, re-lock the rest under
815
815
  * V. inputs [vesting(0), lockedToken(1)]; outputs [vesting cont(0), relock(1, A/V-owned), recipient(2, A/creator)].
816
816
  * The flow must set tx.lockTime = current DAA score (consensus blocks the tx until then; the covenant reads it).
817
+ *
818
+ * Pass `opts.tokenCovid` (the vested token's covenant id, hex — `covenantId` from the indexer) so the relock +
819
+ * recipient outputs carry the KIP-20 covenant binding the chain requires; without it the assembled tx fails
820
+ * on-chain. The vesting-continuation output is always bound to `vestingCovid` (already a required param).
817
821
  */
818
822
  declare function buildVestingClaim(k: K, vestTpl: VestingTemplate, tokenTpl: Kcc20Template, vestingUtxo: VestUtxo, lockedToken: VestUtxo, vestingCovid: Uint8Array, creatorPubkey: Uint8Array, claimed: bigint, release: bigint, opts?: {
819
823
  tokenDust?: bigint;
824
+ tokenCovid?: string;
820
825
  }): CovenantSpend;
821
826
  /** Final claim: once fully vested, pay ALL remaining to the creator and continue V with claimed=total (husk).
822
- * inputs [vesting(0), lockedToken(1)]; outputs [vesting cont(0), recipient(1, A/creator)]. */
827
+ * inputs [vesting(0), lockedToken(1)]; outputs [vesting cont(0), recipient(1, A/creator)].
828
+ *
829
+ * Pass `opts.tokenCovid` (the vested token's covenant id, hex) so the recipient output carries the KIP-20
830
+ * covenant binding the chain requires; without it the assembled tx fails on-chain. */
823
831
  declare function buildVestingClaimFinal(k: K, vestTpl: VestingTemplate, tokenTpl: Kcc20Template, vestingUtxo: VestUtxo, lockedToken: VestUtxo, vestingCovid: Uint8Array, creatorPubkey: Uint8Array, claimed: bigint, opts?: {
824
832
  tokenDust?: bigint;
833
+ tokenCovid?: string;
825
834
  }): CovenantSpend;
826
835
 
827
836
  declare const vestingTx_VEST_SELECTOR: typeof VEST_SELECTOR;
package/dist/index.js CHANGED
@@ -578,6 +578,9 @@ function buildAddLiquidity(k, tpl, tokenTpl, utxo, lpInventory, poolCovid, lpDep
578
578
  if (lpDepositToken.state.amount !== q.dToken) throw new Error("LP deposit token UTXO must equal dToken exactly (split first)");
579
579
  const dust = opts.tokenDust ?? 1000n;
580
580
  const { kasReserve, tokenReserve, tokenCovid, lpCovid } = utxo.state;
581
+ const poolCovidHex = hexOf(poolCovid);
582
+ const tokenCovidHex = hexOf(tokenCovid);
583
+ const lpCovidHex = hexOf(lpCovid);
581
584
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
582
585
  const poolLpOut = covenantIdOwned(poolCovid, lpInventory.amount - q.dShares, false);
583
586
  const lpSharesOut = addressPresenceOwned(lpPubkey, q.dShares);
@@ -597,17 +600,20 @@ function buildAddLiquidity(k, tpl, tokenTpl, utxo, lpInventory, poolCovid, lpDep
597
600
  { transactionId: lpInventory.transactionId, index: lpInventory.index, value: lpInventory.value, scriptPublicKey: kcc20Spk(k, poolLpInvRedeem), signatureScript: transferSigScript(k, poolLpInvRedeem, lStates, lWitnesses), redeem: poolLpInvRedeem, role: "poolLpInventory" }
598
601
  ];
599
602
  const outputs = [
600
- { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool" },
601
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
602
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolLpOut)), role: "poolLpInventory" },
603
- { 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 } }
604
607
  ];
605
- 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 } };
606
609
  }
607
610
  function buildRemoveLiquidity(k, tpl, tokenTpl, utxo, lpShares, poolCovid, lpPubkey, q, presenceWitnessIdx, opts = {}) {
608
611
  if (lpShares.state.amount !== q.dShares) throw new Error("LP shares UTXO must equal dShares exactly (split first)");
609
612
  const dust = opts.tokenDust ?? 1000n;
610
613
  const { kasReserve, tokenReserve, tokenCovid, lpCovid } = utxo.state;
614
+ const poolCovidHex = hexOf(poolCovid);
615
+ const tokenCovidHex = hexOf(tokenCovid);
616
+ const lpCovidHex = hexOf(lpCovid);
611
617
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
612
618
  const lpTokenOut = addressPresenceOwned(lpPubkey, q.dToken);
613
619
  const poolLpOut = covenantIdOwned(poolCovid, q.dShares, false);
@@ -625,12 +631,12 @@ function buildRemoveLiquidity(k, tpl, tokenTpl, utxo, lpShares, poolCovid, lpPub
625
631
  { transactionId: lpShares.transactionId, index: lpShares.index, value: lpShares.value, scriptPublicKey: kcc20Spk(k, lpSharesRedeem), signatureScript: transferSigScript(k, lpSharesRedeem, lStates, lWitnesses), redeem: lpSharesRedeem, role: "lpShares" }
626
632
  ];
627
633
  const outputs = [
628
- { value: q.newKas * SCALE, scriptPublicKey: poolCpSpk(k, newRedeem), role: "pool" },
629
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
630
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, lpTokenOut)), role: "lpToken" },
631
- { 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 } }
632
638
  ];
633
- 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 } };
634
640
  }
635
641
  function buildBindLp(k, tpl, tokenTpl, utxo, poolCovid, lockedShares, opts = {}) {
636
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");
@@ -658,12 +664,13 @@ function buildBindLp(k, tpl, tokenTpl, utxo, poolCovid, lockedShares, opts = {})
658
664
  const inputs = [
659
665
  { transactionId: utxo.transactionId, index: utxo.index, value: poolValue, scriptPublicKey: poolCpSpk(k, curRedeem), signatureScript: bindSig, redeem: curRedeem, role: "pool" }
660
666
  ];
667
+ const poolCovidHex = hexOf(poolCovid);
661
668
  const outputs = [
662
- { value: poolValue, scriptPublicKey: poolCpSpk(k, boundRedeem), role: "pool" },
663
- { value: dust, scriptPublicKey: floorSpk, role: "lpFloor" },
664
- { 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 } }
665
672
  ];
666
- 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 };
667
674
  }
668
675
 
669
676
  // src/native/curveCpTx.ts
@@ -721,6 +728,8 @@ function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey,
721
728
  if (tokenOut <= 0n || tokenOut >= inventory.amount) throw new Error("invalid tokenOut");
722
729
  if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
723
730
  const dust = opts.tokenDust ?? 1000n;
731
+ const curveCovidHex = hexOf2(curveCovid);
732
+ const tokenCovidHex = hexOf2(utxo.state.tokenCovid);
724
733
  const newKas = utxo.realKas + kasIn;
725
734
  if (newKas > MAX_KAS) throw new Error("buy exceeds the curve max raise (9,000,000 TKAS)");
726
735
  const newToken = inventory.amount - tokenOut;
@@ -746,13 +755,13 @@ function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey,
746
755
  })
747
756
  ];
748
757
  const outputs = [
749
- { value: newKas, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
750
- { value: dust, scriptPublicKey: kcc20Spk(k, invOutRedeem), role: "inventory" },
751
- { 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 } },
752
761
  { value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
753
762
  { value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
754
763
  ];
755
- 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 } };
756
765
  }
757
766
  function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid, traderPubkey, tokenIn, kasOut, presenceWitnessIdx, opts = {}) {
758
767
  if (utxo.state.graduated) throw new Error("curve has graduated \u2014 sells are locked");
@@ -761,6 +770,8 @@ function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid
761
770
  if (kasOut <= 0n || kasOut % SCALE2 !== 0n || kasOut > utxo.realKas) throw new Error("invalid kasOut");
762
771
  if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
763
772
  const dust = opts.tokenDust ?? 1000n;
773
+ const curveCovidHex = hexOf2(curveCovid);
774
+ const tokenCovidHex = hexOf2(utxo.state.tokenCovid);
764
775
  const sellerIn = sellerTokens.reduce((s, t) => s + t.state.amount, 0n);
765
776
  const change = sellerIn - tokenIn;
766
777
  if (change < 0n) throw new Error("seller inputs are less than the sell amount");
@@ -785,13 +796,13 @@ function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid
785
796
  })
786
797
  ];
787
798
  const outputs = [
788
- { value: utxo.realKas - kasOut, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
789
- { 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 } },
790
801
  { value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
791
802
  { value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
792
803
  ];
793
- if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "seller" });
794
- 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 } };
795
806
  }
796
807
  function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveCovid, poolLockedShares, opts = {}) {
797
808
  if (utxo.state.graduated) throw new Error("already graduated");
@@ -822,10 +833,12 @@ function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveC
822
833
  { transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: graduateSigV2(k, curRedeem, poolState, poolTokens), redeem: curRedeem, role: "curve" },
823
834
  { transactionId: inventory.transactionId, index: inventory.index, value: inventory.value, scriptPublicKey: kcc20Spk(k, invRedeem), signatureScript: transferSigScript(k, invRedeem, [poolTokens], [0]), redeem: invRedeem, role: "inventory" }
824
835
  ];
836
+ const curveCovidHex = hexOf2(curveCovid);
837
+ const tokenCovidHex = hexOf2(A);
825
838
  const outputs = [
826
- { value: lockedValue, scriptPublicKey: cpSpk(k, lockedRedeem), role: "curve" },
827
- { value: poolKas, scriptPublicKey: poolSpkV, role: "pool" },
828
- { 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 } },
829
842
  { value: padFee3(gradFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "gradFee" }
830
843
  ];
831
844
  return { kind: "graduate", inputs, outputs, economics: { poolKas, gradFee, leftover, poolLockedShares }, covids: { tokenCovid: hexOf2(A), poolCovid: poolCovidHex } };
@@ -909,6 +922,8 @@ function v3SwapSellSig(k, redeem, kasOutUnits, poolTokenOut, traderChangeOut) {
909
922
  function buildPoolV3SwapKasForToken(k, tpl, tokenTpl, params, utxo, poolCovid, traderPubkey, q, mergeTokens = [], presenceWitnessIdx = 0, opts = {}) {
910
923
  const dust = opts.tokenDust ?? 1000n;
911
924
  const { kasReserve, tokenReserve, tokenCovid, totalShares, lpCovid } = utxo.state;
925
+ const poolCovidHex = hexOf3(poolCovid);
926
+ const tokenCovidHex = hexOf3(tokenCovid);
912
927
  const mergeSum = mergeTokens.reduce((s, t) => s + t.state.amount, 0n);
913
928
  const poolTokenOut = covenantIdOwned(poolCovid, q.newToken, false);
914
929
  const traderTokenOut = addressPresenceOwned(traderPubkey, q.tokenOut + mergeSum);
@@ -926,18 +941,20 @@ function buildPoolV3SwapKasForToken(k, tpl, tokenTpl, params, utxo, poolCovid, t
926
941
  })
927
942
  ];
928
943
  const outputs = [
929
- { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool" },
930
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, poolTokenOut)), role: "poolToken" },
931
- { 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 } },
932
947
  { value: q.creatorOut, scriptPublicKey: p2pkSpk2(k, params.creatorFeeOwner), role: "creatorFee" },
933
948
  { value: q.platformOut, scriptPublicKey: p2pkSpk2(k, params.platformFeeOwner), role: "platformFee" }
934
949
  ];
935
- 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 } };
936
951
  }
937
952
  function buildPoolV3SwapTokenForKas(k, tpl, tokenTpl, params, utxo, poolCovid, traderPubkey, traderTokens, q, presenceWitnessIdx, opts = {}) {
938
953
  if (traderTokens.length < 1) throw new Error("need at least one trader token");
939
954
  const dust = opts.tokenDust ?? 1000n;
940
955
  const { kasReserve, tokenReserve, tokenCovid, totalShares, lpCovid } = utxo.state;
956
+ const poolCovidHex = hexOf3(poolCovid);
957
+ const tokenCovidHex = hexOf3(tokenCovid);
941
958
  const traderIn = traderTokens.reduce((s, t) => s + t.state.amount, 0n);
942
959
  const change = traderIn - q.tokenIn;
943
960
  if (change < 0n) throw new Error("trader inputs are less than the sell amount");
@@ -958,13 +975,13 @@ function buildPoolV3SwapTokenForKas(k, tpl, tokenTpl, params, utxo, poolCovid, t
958
975
  })
959
976
  ];
960
977
  const outputs = [
961
- { value: q.newKas * SCALE, scriptPublicKey: poolCpV3Spk(k, newRedeem), role: "pool" },
962
- { 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 } },
963
980
  { value: q.creatorOut, scriptPublicKey: p2pkSpk2(k, params.creatorFeeOwner), role: "creatorFee" },
964
981
  { value: q.platformOut, scriptPublicKey: p2pkSpk2(k, params.platformFeeOwner), role: "platformFee" }
965
982
  ];
966
- if (hasChange) outputs.push({ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, traderChangeOut)), role: "trader" });
967
- 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 } };
968
985
  }
969
986
 
970
987
  // src/native/vestingTx.ts
@@ -979,6 +996,7 @@ __export(vestingTx_exports, {
979
996
  vestingSpkForState: () => vestingSpkForState
980
997
  });
981
998
  var VEST_SELECTOR = { claim: 0, claimFinal: 1 };
999
+ var hexOf4 = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
982
1000
  function vestedAmount(total, startScore, durationScore, daaScore) {
983
1001
  if (daaScore <= startScore) return 0n;
984
1002
  const elapsed = BigInt(daaScore - startScore);
@@ -1004,6 +1022,8 @@ function buildVestingClaim(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vesti
1004
1022
  const remaining = total - claimed;
1005
1023
  if (release <= 0n || release >= remaining) throw new Error("partial claim must be > 0 and < remaining (use claimFinal to drain)");
1006
1024
  const dust = opts.tokenDust ?? 1000n;
1025
+ const vestingCovidHex = hexOf4(vestingCovid);
1026
+ const tokenBinding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 1 } : void 0;
1007
1027
  const newClaimed = claimed + release, newRemaining = remaining - release;
1008
1028
  const curRedeem = materializeVestingScript(vestTpl, claimed);
1009
1029
  const newRedeem = materializeVestingScript(vestTpl, newClaimed);
@@ -1020,20 +1040,22 @@ function buildVestingClaim(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vesti
1020
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" }
1021
1041
  ];
1022
1042
  const outputs = [
1023
- { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting" },
1043
+ { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting", binding: { covid: vestingCovidHex, authorizingInput: 0 } },
1024
1044
  // V continuation (claimed bumped)
1025
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, relockState)), role: "relock" },
1045
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, relockState)), role: "relock", binding: tokenBinding },
1026
1046
  // re-locked (A, V-owned)
1027
- { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient" }
1047
+ { value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, recipientState)), role: "recipient", binding: tokenBinding }
1028
1048
  // to creator (A, presence)
1029
1049
  ];
1030
- return { kind: "claim", inputs, outputs, economics: { release, newClaimed } };
1050
+ return { kind: "claim", inputs, outputs, economics: { release, newClaimed }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
1031
1051
  }
1032
1052
  function buildVestingClaimFinal(k, vestTpl, tokenTpl, vestingUtxo, lockedToken, vestingCovid, creatorPubkey, claimed, opts = {}) {
1033
1053
  const total = BigInt(vestTpl.params.total);
1034
1054
  if (claimed < 0n || claimed >= total) throw new Error("nothing left to claim");
1035
1055
  const remaining = total - claimed;
1036
1056
  const dust = opts.tokenDust ?? 1000n;
1057
+ const vestingCovidHex = hexOf4(vestingCovid);
1058
+ const tokenBinding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 1 } : void 0;
1037
1059
  const curRedeem = materializeVestingScript(vestTpl, claimed);
1038
1060
  const newRedeem = materializeVestingScript(vestTpl, total);
1039
1061
  const lockedState = covenantIdOwned(vestingCovid, remaining, false);
@@ -1047,10 +1069,10 @@ function buildVestingClaimFinal(k, vestTpl, tokenTpl, vestingUtxo, lockedToken,
1047
1069
  { transactionId: lockedToken.transactionId, index: lockedToken.index, value: lockedToken.value, scriptPublicKey: kcc20Spk(k, lockedRedeem), signatureScript: transferSigScript(k, lockedRedeem, [recipientState], [0]), redeem: lockedRedeem, role: "lockedToken" }
1048
1070
  ];
1049
1071
  const outputs = [
1050
- { value: vestingUtxo.value, scriptPublicKey: vestingSpk(k, newRedeem), role: "vesting" },
1051
- { 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 }
1052
1074
  ];
1053
- return { kind: "claimFinal", inputs, outputs, economics: { release: remaining } };
1075
+ return { kind: "claimFinal", inputs, outputs, economics: { release: remaining }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
1054
1076
  }
1055
1077
 
1056
1078
  // src/wallet/index.ts