@kronsdk/kron-sdk 0.5.0 → 0.6.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 +18 -9
- package/dist/index.d.ts +39 -1
- package/dist/index.js +62 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
7
|
+
> **Status: v0.6.1, 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.
|
|
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`.
|
|
15
|
-
>
|
|
16
|
-
>
|
|
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.
|
|
45
|
+
npm install @kronsdk/kron-sdk@0.6.1 # or pin an exact version for reproducible builds
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
The package follows semver. **0.
|
|
47
|
-
the warning above. The token-list client (`client.RegistryClient.tokenlist()`) and
|
|
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
|
|
|
@@ -117,6 +119,13 @@ verifier can't re-derive the covenant script from params (this package has no co
|
|
|
117
119
|
covenant-id-on-genesis check is the achievable, sufficient anti-spoof proof. Full schema:
|
|
118
120
|
[`docs/INTEGRATION.md`](docs/INTEGRATION.md).
|
|
119
121
|
|
|
122
|
+
`extensions.templateVersion` (0.6.1+) is the token's **covenant version pin** `{ schema, silverc }` —
|
|
123
|
+
KRON pins each token to the covenant source set it was deployed under (template pinning), so future
|
|
124
|
+
covenant upgrades can't strand deployed tokens. An auditor recompiling the covenant from
|
|
125
|
+
`extensions.curveParams` must compile **that** version's sources (archived at
|
|
126
|
+
`covenants/versions/<schema[0..12]>/` in the kron repo), not the newest ones. `null` = pre-pinning legacy
|
|
127
|
+
entry. The on-chain verifier above is version-independent and needs none of this.
|
|
128
|
+
|
|
120
129
|
## What's in the box
|
|
121
130
|
|
|
122
131
|
```
|
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;
|
|
@@ -1084,6 +1093,25 @@ type CpCurveParamsRecord = {
|
|
|
1084
1093
|
poolLockedShares?: number;
|
|
1085
1094
|
vestingCovid?: string;
|
|
1086
1095
|
};
|
|
1096
|
+
/** Covenant version pin (template pinning, KRON ROADMAP 3.5) — stamped by the registry SERVER at
|
|
1097
|
+
* registration, never client-set. `schema` = blake2b-256 over the covenant `.sil` source set the token was
|
|
1098
|
+
* deployed under (the sources live at `covenants/versions/<schema[0..12]>/` in the kron repo); `silverc` =
|
|
1099
|
+
* the pinned compiler commit. A consumer re-deriving the token's covenant templates/addresses from
|
|
1100
|
+
* `curveParams` MUST compile the PINNED source set — compiling newer sources yields different bytes and
|
|
1101
|
+
* wrong addresses. Absent/null = a pre-pinning legacy record (current sources at the time). */
|
|
1102
|
+
type TemplateVersionRecord = {
|
|
1103
|
+
schema: string;
|
|
1104
|
+
silverc?: string | null;
|
|
1105
|
+
};
|
|
1106
|
+
/** Optional dev-allocation vesting record (curve_cp.initVested + vesting.sil) — schedule in tx.locktime units. */
|
|
1107
|
+
type CpVestingRecord = {
|
|
1108
|
+
vestingCovid: string;
|
|
1109
|
+
total: number;
|
|
1110
|
+
startScore: number;
|
|
1111
|
+
durationScore: number;
|
|
1112
|
+
genesisTxid: string;
|
|
1113
|
+
outIndex: number;
|
|
1114
|
+
};
|
|
1087
1115
|
type RegistryToken = {
|
|
1088
1116
|
tick: string;
|
|
1089
1117
|
name: string;
|
|
@@ -1100,12 +1128,19 @@ type RegistryToken = {
|
|
|
1100
1128
|
};
|
|
1101
1129
|
cp: {
|
|
1102
1130
|
curveParams: CpCurveParamsRecord;
|
|
1131
|
+
templateVersion?: TemplateVersionRecord | null;
|
|
1103
1132
|
tokenCovid?: string;
|
|
1104
1133
|
curveCovid?: string;
|
|
1105
1134
|
poolCovid?: string;
|
|
1106
1135
|
genesisTxid?: string;
|
|
1136
|
+
initialInventory?: number;
|
|
1137
|
+
devAmount?: number;
|
|
1138
|
+
vesting?: CpVestingRecord | null;
|
|
1107
1139
|
};
|
|
1140
|
+
creatorPubkey?: string;
|
|
1141
|
+
graduated?: boolean;
|
|
1108
1142
|
chainVerified?: boolean;
|
|
1143
|
+
createdAt?: string;
|
|
1109
1144
|
};
|
|
1110
1145
|
/** One entry of the public token list (GET /api/registry/tokenlist). tokenlists.org-shaped: the EVM-core
|
|
1111
1146
|
* fields (network/covenantId/symbol/name/decimals/logoURI) plus a KRON `extensions` block. `covenantId`
|
|
@@ -1126,6 +1161,9 @@ type TokenListEntry = {
|
|
|
1126
1161
|
creator: string | null;
|
|
1127
1162
|
creatorPubkey: string | null;
|
|
1128
1163
|
curveParams: CpCurveParamsRecord | null;
|
|
1164
|
+
/** Covenant version pin — with `curveParams`, what a covenant-aware auditor needs to re-derive the
|
|
1165
|
+
* curve P2SH (compile the PINNED source set, not the newest). Null = pre-pinning legacy entry. */
|
|
1166
|
+
templateVersion: TemplateVersionRecord | null;
|
|
1129
1167
|
graduated: boolean;
|
|
1130
1168
|
chainVerified: boolean;
|
|
1131
1169
|
};
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|