@kronsdk/kron-sdk 0.3.0 → 0.5.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 +11 -4
- package/dist/index.d.ts +95 -11
- package/dist/index.js +135 -28
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,10 +4,16 @@
|
|
|
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.5.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
|
+
>
|
|
12
|
+
> **⚠️ Upgrade from < 0.5.0.** Every earlier release built **version-0** transactions, which cannot carry
|
|
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`.
|
|
11
17
|
|
|
12
18
|
## Why this exists
|
|
13
19
|
|
|
@@ -34,11 +40,12 @@ ESM only (`"type": "module"`) in v1 — see [Design notes](#design-notes) for wh
|
|
|
34
40
|
|
|
35
41
|
```bash
|
|
36
42
|
npm install @kronsdk/kron-sdk@latest # newest
|
|
37
|
-
npm install @kronsdk/kron-sdk@0.
|
|
43
|
+
npm install @kronsdk/kron-sdk@0.5.0 # or pin an exact version for reproducible builds
|
|
38
44
|
```
|
|
39
45
|
|
|
40
|
-
The package follows semver.
|
|
41
|
-
|
|
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).
|
|
42
49
|
|
|
43
50
|
## Quickstart — quote a curve buy (Node)
|
|
44
51
|
|
package/dist/index.d.ts
CHANGED
|
@@ -140,6 +140,21 @@ declare namespace genesis {
|
|
|
140
140
|
|
|
141
141
|
type K$5 = Kaspa;
|
|
142
142
|
type Spk$4 = any;
|
|
143
|
+
/** Covenant txs are KIP-20 v1 transactions (covenant outputs require tx.version >= 1). */
|
|
144
|
+
declare const TX_VERSION = 1;
|
|
145
|
+
/** Per-input compute budget (v1): a P2PK funding input ≈ one sig op. */
|
|
146
|
+
declare const FUNDING_COMPUTE = 10;
|
|
147
|
+
/** Per-input compute budget (v1): a kcc20 `transfer` input (token balance / inventory / seller piece). */
|
|
148
|
+
declare const TOKEN_COMPUTE = 500;
|
|
149
|
+
/** Per-input compute budget (v1): a curve_cp / amm_pool_cp input (the large redeem scripts). */
|
|
150
|
+
declare const COVENANT_COMPUTE = 2000;
|
|
151
|
+
/** Covenant output min value (KIP-9 storage mass) — the conventional token-UTXO dust, 0.5 KAS. */
|
|
152
|
+
declare const COVENANT_DUST = 50000000n;
|
|
153
|
+
/** A covenant output's KIP-20 lineage: which covenant id it continues and which input authorizes it. */
|
|
154
|
+
type CovBinding = {
|
|
155
|
+
covid: string;
|
|
156
|
+
authorizingInput: number;
|
|
157
|
+
};
|
|
143
158
|
/** A covenant UTXO being spent, already carrying its signature script (no wallet signature needed). */
|
|
144
159
|
type CovInput = {
|
|
145
160
|
transactionId: string;
|
|
@@ -152,16 +167,19 @@ type CovInput = {
|
|
|
152
167
|
redeem: Uint8Array;
|
|
153
168
|
/** what this input is, for assembly/debugging: 'curve' | 'minterBranch' | 'burn' | 'pool' | 'poolToken'. */
|
|
154
169
|
role: string;
|
|
170
|
+
/** v1 compute budget override; defaults by role (curve/pool → COVENANT_COMPUTE, else TOKEN_COMPUTE). */
|
|
171
|
+
computeBudget?: number;
|
|
155
172
|
};
|
|
156
|
-
/** A covenant-required output (value + scriptPublicKey). */
|
|
173
|
+
/** A covenant-required output (value + scriptPublicKey [+ covenant binding]). */
|
|
157
174
|
type CovOutput = {
|
|
158
175
|
value: bigint;
|
|
159
176
|
scriptPublicKey: Spk$4;
|
|
160
177
|
role: string;
|
|
178
|
+
binding?: CovBinding;
|
|
161
179
|
};
|
|
162
180
|
/** A complete covenant action: the inputs it spends + the outputs it must create + computed economics. */
|
|
163
181
|
type CovenantSpend = {
|
|
164
|
-
kind: 'init' | 'initVested' | 'buy' | 'sell' | 'graduate' | 'swapKasForToken' | 'swapTokenForKas' | 'addLiquidity' | 'removeLiquidity' | 'bindLp' | 'claim' | 'claimFinal';
|
|
182
|
+
kind: 'init' | 'initVested' | 'buy' | 'sell' | 'transfer' | 'graduate' | 'swapKasForToken' | 'swapTokenForKas' | 'addLiquidity' | 'removeLiquidity' | 'bindLp' | 'claim' | 'claimFinal';
|
|
165
183
|
inputs: CovInput[];
|
|
166
184
|
outputs: CovOutput[];
|
|
167
185
|
economics: Record<string, bigint>;
|
|
@@ -183,10 +201,12 @@ type AssembledNativeTx = {
|
|
|
183
201
|
change: bigint;
|
|
184
202
|
};
|
|
185
203
|
/**
|
|
186
|
-
* Assemble a complete tx: the spend's covenant inputs (pre-scripted) + the trader's funding
|
|
187
|
-
* change output. `
|
|
188
|
-
*
|
|
189
|
-
*
|
|
204
|
+
* Assemble a complete v1 covenant tx: the spend's covenant inputs (pre-scripted) + the trader's funding
|
|
205
|
+
* inputs + a change output. Covenant outputs whose `binding` is set carry the KIP-20 `CovenantBinding`
|
|
206
|
+
* (REQUIRED for any output the covenant validates — see the module header). `networkFee` is
|
|
207
|
+
* caller-provided; size it with `estimateNativeFee` (v1 fees must cover the per-input compute budget, so a
|
|
208
|
+
* flat legacy fee is usually too low). Covenant inputs default to a role-based compute budget; funding
|
|
209
|
+
* inputs are signed via signFundingInputs (or the signPskt bridge).
|
|
190
210
|
*/
|
|
191
211
|
declare function assembleNativeTx(k: K$5, opts: {
|
|
192
212
|
spend: CovenantSpend;
|
|
@@ -194,6 +214,14 @@ declare function assembleNativeTx(k: K$5, opts: {
|
|
|
194
214
|
changeAddress: string;
|
|
195
215
|
networkFee: bigint;
|
|
196
216
|
}): AssembledNativeTx;
|
|
217
|
+
/**
|
|
218
|
+
* Size `networkFee` for an assembled v1 tx: byte/storage mass (via the WASM mass calculator, with
|
|
219
|
+
* placeholder signatures on the funding inputs so byte mass is realistic) + the per-input compute budget
|
|
220
|
+
* the calculator omits (grams = budget × 100), at `feeRateSompiPerGram` (use the node's feerate estimate;
|
|
221
|
+
* min-relay on TN10 has been ~100 sompi/gram), with a 1.5× over-cover and a 10_000-sompi floor. Assemble
|
|
222
|
+
* with a guess (e.g. 10_000n), call this, then re-assemble with the returned fee.
|
|
223
|
+
*/
|
|
224
|
+
declare function estimateNativeFee(k: K$5, networkId: string, asm: AssembledNativeTx, feeRateSompiPerGram: number): bigint;
|
|
197
225
|
/** Sign the trader's funding inputs (P2PK) in place; covenant inputs are left untouched (pre-scripted). */
|
|
198
226
|
declare function signFundingInputs(k: K$5, tx: any, privKey: any, fundingInputIndexes: number[]): any;
|
|
199
227
|
/**
|
|
@@ -221,16 +249,23 @@ declare function signPsktWithKey(k: K$5, txJsonString: string, signInputs: {
|
|
|
221
249
|
}[], privKey: any): string;
|
|
222
250
|
|
|
223
251
|
type spend_AssembledNativeTx = AssembledNativeTx;
|
|
252
|
+
declare const spend_COVENANT_COMPUTE: typeof COVENANT_COMPUTE;
|
|
253
|
+
declare const spend_COVENANT_DUST: typeof COVENANT_DUST;
|
|
254
|
+
type spend_CovBinding = CovBinding;
|
|
224
255
|
type spend_CovInput = CovInput;
|
|
225
256
|
type spend_CovOutput = CovOutput;
|
|
226
257
|
type spend_CovenantSpend = CovenantSpend;
|
|
258
|
+
declare const spend_FUNDING_COMPUTE: typeof FUNDING_COMPUTE;
|
|
227
259
|
type spend_FundingEntry = FundingEntry;
|
|
260
|
+
declare const spend_TOKEN_COMPUTE: typeof TOKEN_COMPUTE;
|
|
261
|
+
declare const spend_TX_VERSION: typeof TX_VERSION;
|
|
228
262
|
declare const spend_assembleNativeTx: typeof assembleNativeTx;
|
|
263
|
+
declare const spend_estimateNativeFee: typeof estimateNativeFee;
|
|
229
264
|
declare const spend_signFundingInputs: typeof signFundingInputs;
|
|
230
265
|
declare const spend_signPsktWithKey: typeof signPsktWithKey;
|
|
231
266
|
declare const spend_toPsktJson: typeof toPsktJson;
|
|
232
267
|
declare namespace spend {
|
|
233
|
-
export { type spend_AssembledNativeTx as AssembledNativeTx, type spend_CovInput as CovInput, type spend_CovOutput as CovOutput, type spend_CovenantSpend as CovenantSpend, type spend_FundingEntry as FundingEntry, spend_assembleNativeTx as assembleNativeTx, spend_signFundingInputs as signFundingInputs, spend_signPsktWithKey as signPsktWithKey, spend_toPsktJson as toPsktJson };
|
|
268
|
+
export { type spend_AssembledNativeTx as AssembledNativeTx, spend_COVENANT_COMPUTE as COVENANT_COMPUTE, spend_COVENANT_DUST as COVENANT_DUST, type spend_CovBinding as CovBinding, type spend_CovInput as CovInput, type spend_CovOutput as CovOutput, type spend_CovenantSpend as CovenantSpend, spend_FUNDING_COMPUTE as FUNDING_COMPUTE, type spend_FundingEntry as FundingEntry, spend_TOKEN_COMPUTE as TOKEN_COMPUTE, spend_TX_VERSION as TX_VERSION, spend_assembleNativeTx as assembleNativeTx, spend_estimateNativeFee as estimateNativeFee, spend_signFundingInputs as signFundingInputs, spend_signPsktWithKey as signPsktWithKey, spend_toPsktJson as toPsktJson };
|
|
234
269
|
}
|
|
235
270
|
|
|
236
271
|
type K$4 = Kaspa;
|
|
@@ -259,6 +294,23 @@ type Kcc20Template = {
|
|
|
259
294
|
};
|
|
260
295
|
/** Produce the kcc20 redeem script for `state` by splicing the 46-byte region. Byte-identical to silverc. */
|
|
261
296
|
declare function materializeKcc20Script(tpl: Kcc20Template, state: Kcc20State): Uint8Array;
|
|
297
|
+
/**
|
|
298
|
+
* Recover the `{ script, stateStart }` template AND the current balance state from a live token UTXO's
|
|
299
|
+
* redeem script (your indexer's `redeemScriptHex`). Scans for the 46-byte state region's push layout
|
|
300
|
+
* (`0x20 <owner:32> 0x01 <type:1> 0x08 <amount:8> 0x01 <isMinter:1>` with type ≤ 0x03, isMinter ≤ 1) and
|
|
301
|
+
* requires it to match at exactly ONE offset. This is the supported way to build the template for
|
|
302
|
+
* `materializeKcc20Script` — splice the SAME script the chain holds; never re-compile.
|
|
303
|
+
* `maxIns`/`maxOuts` are compiled constants not recoverable from the bytes — pass the token's deploy
|
|
304
|
+
* params if you know them (KRON deploys use 4/4, the default) — they are informational only (the splice
|
|
305
|
+
* uses just `script` + `stateStart`).
|
|
306
|
+
*/
|
|
307
|
+
declare function decodeKcc20Redeem(redeem: Uint8Array, opts?: {
|
|
308
|
+
maxIns?: number;
|
|
309
|
+
maxOuts?: number;
|
|
310
|
+
}): {
|
|
311
|
+
template: Kcc20Template;
|
|
312
|
+
state: Kcc20State;
|
|
313
|
+
};
|
|
262
314
|
/** Token P2SH scriptPublicKey for a redeem script. */
|
|
263
315
|
declare const kcc20Spk: (k: K$4, redeem: Uint8Array) => Spk$3;
|
|
264
316
|
/** Token P2SH scriptPublicKey for a balance state (materialize → P2SH). */
|
|
@@ -288,13 +340,33 @@ declare function pushKcc20States(b: SigScriptBuilder, states: Kcc20State[]): voi
|
|
|
288
340
|
* carrying that covenant id). `sigs` are 65-byte Schnorr sigs (empty for covenant-id-only ownership).
|
|
289
341
|
*/
|
|
290
342
|
declare function transferSigScript(k: K$4, redeem: Uint8Array, newStates: Kcc20State[], witnesses: number[], sigs?: Uint8Array[]): string;
|
|
343
|
+
/**
|
|
344
|
+
* Send `sendAmount` from one or more presence-owned token UTXOs (same owner) to `recipientPubkey32`, with
|
|
345
|
+
* the remainder returned to the sender — the wallet "Send" button. A plain conserving `transfer`: inputs
|
|
346
|
+
* are authorized by ONE co-present P2PK input at tx index `presenceWitnessIdx` (assemble the covenant
|
|
347
|
+
* inputs first, then the sender's funding inputs: with N token inputs, `presenceWitnessIdx = N`).
|
|
348
|
+
*
|
|
349
|
+
* `tokenCovid` (the token's covenant id, hex — `covenantId` from the indexer's token info) is REQUIRED:
|
|
350
|
+
* every covenant output must carry the KIP-20 `CovenantBinding` or the chain rejects the spend with
|
|
351
|
+
* "script ran, but verification failed". Outputs: [recipient, change] (change omitted when exact).
|
|
352
|
+
*/
|
|
353
|
+
declare function buildKcc20Send(k: K$4, tpl: Kcc20Template, senderTokens: {
|
|
354
|
+
transactionId: string;
|
|
355
|
+
index: number;
|
|
356
|
+
value: bigint;
|
|
357
|
+
state: Kcc20State;
|
|
358
|
+
}[], recipientPubkey32: Uint8Array, sendAmount: bigint, presenceWitnessIdx: number, tokenCovid: string, opts?: {
|
|
359
|
+
tokenDust?: bigint;
|
|
360
|
+
}): CovenantSpend;
|
|
291
361
|
|
|
292
362
|
declare const kcc20Tx_IDENTIFIER: typeof IDENTIFIER;
|
|
293
363
|
type kcc20Tx_IdentifierType = IdentifierType;
|
|
294
364
|
type kcc20Tx_Kcc20State = Kcc20State;
|
|
295
365
|
type kcc20Tx_Kcc20Template = Kcc20Template;
|
|
296
366
|
declare const kcc20Tx_addressPresenceOwned: typeof addressPresenceOwned;
|
|
367
|
+
declare const kcc20Tx_buildKcc20Send: typeof buildKcc20Send;
|
|
297
368
|
declare const kcc20Tx_covenantIdOwned: typeof covenantIdOwned;
|
|
369
|
+
declare const kcc20Tx_decodeKcc20Redeem: typeof decodeKcc20Redeem;
|
|
298
370
|
declare const kcc20Tx_kcc20Address: typeof kcc20Address;
|
|
299
371
|
declare const kcc20Tx_kcc20Spk: typeof kcc20Spk;
|
|
300
372
|
declare const kcc20Tx_kcc20SpkForState: typeof kcc20SpkForState;
|
|
@@ -305,7 +377,7 @@ declare const kcc20Tx_pushKcc20States: typeof pushKcc20States;
|
|
|
305
377
|
declare const kcc20Tx_scriptHashOwned: typeof scriptHashOwned;
|
|
306
378
|
declare const kcc20Tx_transferSigScript: typeof transferSigScript;
|
|
307
379
|
declare namespace kcc20Tx {
|
|
308
|
-
export { kcc20Tx_IDENTIFIER as IDENTIFIER, type kcc20Tx_IdentifierType as IdentifierType, type kcc20Tx_Kcc20State as Kcc20State, type kcc20Tx_Kcc20Template as Kcc20Template, kcc20Tx_addressPresenceOwned as addressPresenceOwned, kcc20Tx_covenantIdOwned as covenantIdOwned, kcc20Tx_kcc20Address as kcc20Address, kcc20Tx_kcc20Spk as kcc20Spk, kcc20Tx_kcc20SpkForState as kcc20SpkForState, kcc20Tx_materializeKcc20Script as materializeKcc20Script, kcc20Tx_pubkeyOwned as pubkeyOwned, kcc20Tx_pushKcc20StateScalar as pushKcc20StateScalar, kcc20Tx_pushKcc20States as pushKcc20States, kcc20Tx_scriptHashOwned as scriptHashOwned, kcc20Tx_transferSigScript as transferSigScript };
|
|
380
|
+
export { kcc20Tx_IDENTIFIER as IDENTIFIER, type kcc20Tx_IdentifierType as IdentifierType, type kcc20Tx_Kcc20State as Kcc20State, type kcc20Tx_Kcc20Template as Kcc20Template, kcc20Tx_addressPresenceOwned as addressPresenceOwned, kcc20Tx_buildKcc20Send as buildKcc20Send, kcc20Tx_covenantIdOwned as covenantIdOwned, kcc20Tx_decodeKcc20Redeem as decodeKcc20Redeem, kcc20Tx_kcc20Address as kcc20Address, kcc20Tx_kcc20Spk as kcc20Spk, kcc20Tx_kcc20SpkForState as kcc20SpkForState, kcc20Tx_materializeKcc20Script as materializeKcc20Script, kcc20Tx_pubkeyOwned as pubkeyOwned, kcc20Tx_pushKcc20StateScalar as pushKcc20StateScalar, kcc20Tx_pushKcc20States as pushKcc20States, kcc20Tx_scriptHashOwned as scriptHashOwned, kcc20Tx_transferSigScript as transferSigScript };
|
|
309
381
|
}
|
|
310
382
|
|
|
311
383
|
type K$3 = Kaspa;
|
|
@@ -551,6 +623,7 @@ type CpTemplate = {
|
|
|
551
623
|
type CpCurveState = {
|
|
552
624
|
graduated: boolean;
|
|
553
625
|
tokenCovid: Uint8Array;
|
|
626
|
+
tokenReserve: bigint;
|
|
554
627
|
};
|
|
555
628
|
/** The live curve UTXO. `realKas` (sompi) = its value = KAS raised. */
|
|
556
629
|
type CpCurveUtxo = {
|
|
@@ -572,15 +645,20 @@ declare const cpSpkForState: (k: K$2, tpl: CpTemplate, state: CpCurveState) => S
|
|
|
572
645
|
declare function cpAddress(k: K$2, tpl: CpTemplate, state: CpCurveState, network: string): string;
|
|
573
646
|
/** Fee output scriptPublicKey: P2PK (`<32-byte pubkey> OP_CHECKSIG`). */
|
|
574
647
|
declare function p2pkSpk(k: K$2, pubkey: Uint8Array): Spk$1;
|
|
575
|
-
declare function buildCpBuy(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Template, utxo: CpCurveUtxo, inventory: CpInventoryUtxo, curveCovid: Uint8Array, buyerPubkey: Uint8Array, kasIn: bigint, tokenOut: bigint,
|
|
648
|
+
declare function buildCpBuy(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Template, utxo: CpCurveUtxo, inventory: CpInventoryUtxo, curveCovid: Uint8Array, buyerPubkey: Uint8Array, kasIn: bigint, tokenOut: bigint, mergeTokens?: {
|
|
649
|
+
transactionId: string;
|
|
650
|
+
index: number;
|
|
651
|
+
value: bigint;
|
|
652
|
+
state: Kcc20State;
|
|
653
|
+
}[], presenceWitnessIdx?: number, opts?: {
|
|
576
654
|
tokenDust?: bigint;
|
|
577
655
|
}): CovenantSpend;
|
|
578
|
-
declare function buildCpSell(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Template, utxo: CpCurveUtxo,
|
|
656
|
+
declare function buildCpSell(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Template, utxo: CpCurveUtxo, sellerTokens: {
|
|
579
657
|
transactionId: string;
|
|
580
658
|
index: number;
|
|
581
659
|
value: bigint;
|
|
582
660
|
state: Kcc20State;
|
|
583
|
-
}, inventory: CpInventoryUtxo, curveCovid: Uint8Array, tokenIn: bigint, kasOut: bigint, presenceWitnessIdx: number, opts?: {
|
|
661
|
+
}[], inventory: CpInventoryUtxo, curveCovid: Uint8Array, traderPubkey: Uint8Array, tokenIn: bigint, kasOut: bigint, presenceWitnessIdx: number, opts?: {
|
|
584
662
|
tokenDust?: bigint;
|
|
585
663
|
}): CovenantSpend;
|
|
586
664
|
declare function buildCpGraduate(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Template, poolTemplate: PoolCpTemplate, utxo: CpCurveUtxo, inventory: CpInventoryUtxo, curveCovid: Uint8Array, poolLockedShares: bigint, opts?: {
|
|
@@ -592,6 +670,8 @@ declare function buildCpGraduate(k: K$2, tpl: CpTemplate, tokenTpl: Kcc20Templat
|
|
|
592
670
|
* a plain conserving kcc20 transfer authorized by a co-present P2PK input at `presenceWitnessIdx`. Lets a
|
|
593
671
|
* holder sell an ARBITRARY amount on covenants that require full-UTXO sells (curve/pool): split, then sell the
|
|
594
672
|
* `sellAmount` piece. No curve/pool involved — just the token covenant.
|
|
673
|
+
* Pass `opts.tokenCovid` (the token's covenant id, hex — `covenantId` from the indexer) so both outputs carry
|
|
674
|
+
* the KIP-20 covenant binding the chain requires; without it the assembled tx fails on-chain.
|
|
595
675
|
*/
|
|
596
676
|
declare function buildSplitToken(k: K$2, tokenTpl: Kcc20Template, sellerToken: {
|
|
597
677
|
transactionId: string;
|
|
@@ -600,11 +680,14 @@ declare function buildSplitToken(k: K$2, tokenTpl: Kcc20Template, sellerToken: {
|
|
|
600
680
|
state: Kcc20State;
|
|
601
681
|
}, sellAmount: bigint, presenceWitnessIdx: number, opts?: {
|
|
602
682
|
tokenDust?: bigint;
|
|
683
|
+
tokenCovid?: string;
|
|
603
684
|
}): CovenantSpend;
|
|
604
685
|
/**
|
|
605
686
|
* Consolidate several presence-owned token UTXOs (same owner) into ONE — a conserving kcc20 transfer (N covid-A
|
|
606
687
|
* inputs → 1 output) authorized by a single co-present P2PK input at `presenceWitnessIdx`. Lets a holder merge
|
|
607
688
|
* many small buys into one piece so a later sell needs just one (or two) inputs. No curve/pool involved.
|
|
689
|
+
* Pass `opts.tokenCovid` (the token's covenant id, hex) so the merged output carries the KIP-20 covenant
|
|
690
|
+
* binding the chain requires; without it the assembled tx fails on-chain.
|
|
608
691
|
*/
|
|
609
692
|
declare function buildConsolidate(k: K$2, tokenTpl: Kcc20Template, tokens: {
|
|
610
693
|
transactionId: string;
|
|
@@ -613,6 +696,7 @@ declare function buildConsolidate(k: K$2, tokenTpl: Kcc20Template, tokens: {
|
|
|
613
696
|
state: Kcc20State;
|
|
614
697
|
}[], presenceWitnessIdx: number, opts?: {
|
|
615
698
|
tokenDust?: bigint;
|
|
699
|
+
tokenCovid?: string;
|
|
616
700
|
}): CovenantSpend;
|
|
617
701
|
|
|
618
702
|
type curveCpTx_CpCurveState = CpCurveState;
|
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:
|
|
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(
|
|
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:
|
|
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 = {};
|
|
@@ -592,15 +674,18 @@ var ZERO322 = new Uint8Array(32);
|
|
|
592
674
|
function materializeCpScript(tpl, state) {
|
|
593
675
|
const s = tpl.stateStart;
|
|
594
676
|
const t = tpl.script;
|
|
595
|
-
if (t[s] !== 1 || t[s + 2] !== 32) {
|
|
596
|
-
throw new Error("curve_cp template has an unexpected state layout (expected push1 graduated / push32 tokenCovid)");
|
|
677
|
+
if (t[s] !== 1 || t[s + 2] !== 32 || t[s + 35] !== 8) {
|
|
678
|
+
throw new Error("curve_cp template has an unexpected state layout (expected push1 graduated / push32 tokenCovid / push8 tokenReserve)");
|
|
597
679
|
}
|
|
598
680
|
if (state.tokenCovid.length !== 32) throw new Error("tokenCovid must be 32 bytes");
|
|
681
|
+
if (state.tokenReserve < 0n) throw new Error("tokenReserve must be non-negative");
|
|
599
682
|
const out = t.slice();
|
|
600
683
|
out[s] = 1;
|
|
601
684
|
out[s + 1] = state.graduated ? 1 : 0;
|
|
602
685
|
out[s + 2] = 32;
|
|
603
686
|
out.set(state.tokenCovid, s + 3);
|
|
687
|
+
out[s + 35] = 8;
|
|
688
|
+
out.set(int8LE(state.tokenReserve), s + 36);
|
|
604
689
|
return out;
|
|
605
690
|
}
|
|
606
691
|
var cpSpk = (k, redeem) => k.payToScriptHashScript(redeem);
|
|
@@ -619,9 +704,10 @@ function buySig(k, redeem, kasIn, tokenOut, inventoryOut, buyerOut) {
|
|
|
619
704
|
pushKcc20StateScalar(b, buyerOut);
|
|
620
705
|
return b.selector(SELECTOR.buy).redeem(redeem).drain();
|
|
621
706
|
}
|
|
622
|
-
function sellSig(k, redeem, tokenIn, kasOut, inventoryOut) {
|
|
707
|
+
function sellSig(k, redeem, tokenIn, kasOut, inventoryOut, traderChangeOut) {
|
|
623
708
|
const b = new SigScriptBuilder(k).int(tokenIn).int(kasOut);
|
|
624
709
|
pushKcc20StateScalar(b, inventoryOut);
|
|
710
|
+
pushKcc20StateScalar(b, traderChangeOut);
|
|
625
711
|
return b.selector(SELECTOR.sell).redeem(redeem).drain();
|
|
626
712
|
}
|
|
627
713
|
function graduateSigV2(k, redeem, pool, poolTokens) {
|
|
@@ -629,27 +715,35 @@ function graduateSigV2(k, redeem, pool, poolTokens) {
|
|
|
629
715
|
pushKcc20StateScalar(b, poolTokens);
|
|
630
716
|
return b.selector(SELECTOR.graduate).redeem(redeem).drain();
|
|
631
717
|
}
|
|
632
|
-
function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey, kasIn, tokenOut, opts = {}) {
|
|
718
|
+
function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey, kasIn, tokenOut, mergeTokens = [], presenceWitnessIdx = 0, opts = {}) {
|
|
633
719
|
if (utxo.state.graduated) throw new Error("curve has graduated \u2014 buys are locked");
|
|
634
720
|
if (kasIn <= 0n || kasIn % SCALE2 !== 0n) throw new Error("kasIn must be a positive multiple of SCALE (0.01 KAS)");
|
|
635
721
|
if (tokenOut <= 0n || tokenOut >= inventory.amount) throw new Error("invalid tokenOut");
|
|
722
|
+
if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
|
|
636
723
|
const dust = opts.tokenDust ?? 1000n;
|
|
637
724
|
const newKas = utxo.realKas + kasIn;
|
|
638
725
|
if (newKas > MAX_KAS) throw new Error("buy exceeds the curve max raise (9,000,000 TKAS)");
|
|
639
726
|
const newToken = inventory.amount - tokenOut;
|
|
640
727
|
const creatorFee = kasIn * tpl.params.creatorFeeBps / 10000n;
|
|
641
728
|
const platformFee = kasIn * tpl.params.platformFeeBps / 10000n;
|
|
729
|
+
const mergeSum = mergeTokens.reduce((s, t) => s + t.state.amount, 0n);
|
|
642
730
|
const inventoryOut = covenantIdOwned(curveCovid, newToken, false);
|
|
643
|
-
const buyerOut = addressPresenceOwned(buyerPubkey, tokenOut);
|
|
731
|
+
const buyerOut = addressPresenceOwned(buyerPubkey, tokenOut + mergeSum);
|
|
644
732
|
const curRedeem = materializeCpScript(tpl, utxo.state);
|
|
645
|
-
const newCurveRedeem = materializeCpScript(tpl, { graduated: false, tokenCovid: utxo.state.tokenCovid });
|
|
733
|
+
const newCurveRedeem = materializeCpScript(tpl, { graduated: false, tokenCovid: utxo.state.tokenCovid, tokenReserve: newToken });
|
|
646
734
|
const invRedeem = materializeKcc20Script(tokenTpl, covenantIdOwned(curveCovid, inventory.amount, false));
|
|
647
735
|
const invOutRedeem = materializeKcc20Script(tokenTpl, inventoryOut);
|
|
648
736
|
const buyerRedeem = materializeKcc20Script(tokenTpl, buyerOut);
|
|
737
|
+
const witnesses = [0, ...mergeTokens.map(() => presenceWitnessIdx)];
|
|
738
|
+
const newStates = [inventoryOut, buyerOut];
|
|
649
739
|
const inputs = [
|
|
650
740
|
{ transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: buySig(k, curRedeem, kasIn, tokenOut, inventoryOut, buyerOut), redeem: curRedeem, role: "curve" },
|
|
651
|
-
// inventory (covid A, C-owned) spent via kcc20 transfer; the
|
|
652
|
-
{ transactionId: inventory.transactionId, index: inventory.index, value: inventory.value, scriptPublicKey: kcc20Spk(k, invRedeem), signatureScript: transferSigScript(k, invRedeem,
|
|
741
|
+
// inventory (covid A, C-owned) spent via kcc20 transfer; the C-owned input is authorized by the curve (input 0)
|
|
742
|
+
{ transactionId: inventory.transactionId, index: inventory.index, value: inventory.value, scriptPublicKey: kcc20Spk(k, invRedeem), signatureScript: transferSigScript(k, invRedeem, newStates, witnesses), redeem: invRedeem, role: "inventory" },
|
|
743
|
+
...mergeTokens.map((mt) => {
|
|
744
|
+
const r = materializeKcc20Script(tokenTpl, mt.state);
|
|
745
|
+
return { transactionId: mt.transactionId, index: mt.index, value: mt.value, scriptPublicKey: kcc20Spk(k, r), signatureScript: transferSigScript(k, r, newStates, witnesses), redeem: r, role: "buyerToken" };
|
|
746
|
+
})
|
|
653
747
|
];
|
|
654
748
|
const outputs = [
|
|
655
749
|
{ value: newKas, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
|
|
@@ -658,28 +752,37 @@ function buildCpBuy(k, tpl, tokenTpl, utxo, inventory, curveCovid, buyerPubkey,
|
|
|
658
752
|
{ value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
|
|
659
753
|
{ value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
|
|
660
754
|
];
|
|
661
|
-
return { kind: "buy", inputs, outputs, economics: { kasIn, tokenOut, creatorFee, platformFee, newRealKas: newKas, newTokenReserve: newToken }, covids: { tokenCovid: hexOf2(utxo.state.tokenCovid) } };
|
|
755
|
+
return { kind: "buy", inputs, outputs, economics: { kasIn, tokenOut, creatorFee, platformFee, newRealKas: newKas, newTokenReserve: newToken, merged: mergeSum }, covids: { tokenCovid: hexOf2(utxo.state.tokenCovid) } };
|
|
662
756
|
}
|
|
663
|
-
function buildCpSell(k, tpl, tokenTpl, utxo,
|
|
757
|
+
function buildCpSell(k, tpl, tokenTpl, utxo, sellerTokens, inventory, curveCovid, traderPubkey, tokenIn, kasOut, presenceWitnessIdx, opts = {}) {
|
|
664
758
|
if (utxo.state.graduated) throw new Error("curve has graduated \u2014 sells are locked");
|
|
665
|
-
if (
|
|
759
|
+
if (sellerTokens.length < 1) throw new Error("need at least one seller token");
|
|
760
|
+
if (tokenIn <= 0n) throw new Error("tokenIn must be positive");
|
|
666
761
|
if (kasOut <= 0n || kasOut % SCALE2 !== 0n || kasOut > utxo.realKas) throw new Error("invalid kasOut");
|
|
762
|
+
if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
|
|
667
763
|
const dust = opts.tokenDust ?? 1000n;
|
|
764
|
+
const sellerIn = sellerTokens.reduce((s, t) => s + t.state.amount, 0n);
|
|
765
|
+
const change = sellerIn - tokenIn;
|
|
766
|
+
if (change < 0n) throw new Error("seller inputs are less than the sell amount");
|
|
767
|
+
const hasChange = change > 0n;
|
|
668
768
|
const newToken = inventory.amount + tokenIn;
|
|
669
769
|
const creatorFee = kasOut * tpl.params.creatorFeeBps / 10000n;
|
|
670
770
|
const platformFee = kasOut * tpl.params.platformFeeBps / 10000n;
|
|
671
771
|
const inventoryOut = covenantIdOwned(curveCovid, newToken, false);
|
|
772
|
+
const traderChangeOut = addressPresenceOwned(traderPubkey, hasChange ? change : 1n);
|
|
672
773
|
const curRedeem = materializeCpScript(tpl, utxo.state);
|
|
673
|
-
const newCurveRedeem = materializeCpScript(tpl, { graduated: false, tokenCovid: utxo.state.tokenCovid });
|
|
674
|
-
const sellerRedeem = materializeKcc20Script(tokenTpl, sellerToken.state);
|
|
774
|
+
const newCurveRedeem = materializeCpScript(tpl, { graduated: false, tokenCovid: utxo.state.tokenCovid, tokenReserve: newToken });
|
|
675
775
|
const invRedeem = materializeKcc20Script(tokenTpl, covenantIdOwned(curveCovid, inventory.amount, false));
|
|
676
776
|
const invOutRedeem = materializeKcc20Script(tokenTpl, inventoryOut);
|
|
677
|
-
const witnesses = [
|
|
678
|
-
const newStates = [inventoryOut];
|
|
777
|
+
const witnesses = [0, ...sellerTokens.map(() => presenceWitnessIdx)];
|
|
778
|
+
const newStates = hasChange ? [inventoryOut, traderChangeOut] : [inventoryOut];
|
|
679
779
|
const inputs = [
|
|
680
|
-
{ transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: sellSig(k, curRedeem, tokenIn, kasOut, inventoryOut), redeem: curRedeem, role: "curve" },
|
|
681
|
-
{ transactionId:
|
|
682
|
-
|
|
780
|
+
{ transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: sellSig(k, curRedeem, tokenIn, kasOut, inventoryOut, traderChangeOut), redeem: curRedeem, role: "curve" },
|
|
781
|
+
{ transactionId: inventory.transactionId, index: inventory.index, value: inventory.value, scriptPublicKey: kcc20Spk(k, invRedeem), signatureScript: transferSigScript(k, invRedeem, newStates, witnesses), redeem: invRedeem, role: "inventory" },
|
|
782
|
+
...sellerTokens.map((st) => {
|
|
783
|
+
const r = materializeKcc20Script(tokenTpl, st.state);
|
|
784
|
+
return { transactionId: st.transactionId, index: st.index, value: st.value, scriptPublicKey: kcc20Spk(k, r), signatureScript: transferSigScript(k, r, newStates, witnesses), redeem: r, role: "sellerToken" };
|
|
785
|
+
})
|
|
683
786
|
];
|
|
684
787
|
const outputs = [
|
|
685
788
|
{ value: utxo.realKas - kasOut, scriptPublicKey: cpSpk(k, newCurveRedeem), role: "curve" },
|
|
@@ -687,12 +790,14 @@ function buildCpSell(k, tpl, tokenTpl, utxo, sellerToken, inventory, curveCovid,
|
|
|
687
790
|
{ value: padFee3(creatorFee), scriptPublicKey: p2pkSpk(k, tpl.params.creatorFeeOwner), role: "creatorFee" },
|
|
688
791
|
{ value: padFee3(platformFee), scriptPublicKey: p2pkSpk(k, tpl.params.platformFeeOwner), role: "platformFee" }
|
|
689
792
|
];
|
|
690
|
-
|
|
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) } };
|
|
691
795
|
}
|
|
692
796
|
function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveCovid, poolLockedShares, opts = {}) {
|
|
693
797
|
if (utxo.state.graduated) throw new Error("already graduated");
|
|
694
798
|
if (utxo.realKas < tpl.params.graduationKas) throw new Error("reserve has not reached the graduation target");
|
|
695
799
|
if (poolLockedShares < 1n) throw new Error("poolLockedShares must be >= 1");
|
|
800
|
+
if (inventory.amount !== utxo.state.tokenReserve) throw new Error("inventory.amount must equal the curve's committed tokenReserve");
|
|
696
801
|
const lockedValue = opts.lockedCurveValue ?? 1000n;
|
|
697
802
|
const dust = opts.tokenDust ?? 1000n;
|
|
698
803
|
const targetPoolKas = utxo.realKas * (10000n - tpl.params.graduationFeeBps) / 10000n;
|
|
@@ -711,7 +816,7 @@ function buildCpGraduate(k, tpl, tokenTpl, poolTemplate, utxo, inventory, curveC
|
|
|
711
816
|
const poolTokens = covenantIdOwned(poolCovid, leftover, false);
|
|
712
817
|
const poolTokenRedeem = materializeKcc20Script(tokenTpl, poolTokens);
|
|
713
818
|
const curRedeem = materializeCpScript(tpl, utxo.state);
|
|
714
|
-
const lockedRedeem = materializeCpScript(tpl, { graduated: true, tokenCovid: A });
|
|
819
|
+
const lockedRedeem = materializeCpScript(tpl, { graduated: true, tokenCovid: A, tokenReserve: inventory.amount });
|
|
715
820
|
const invRedeem = materializeKcc20Script(tokenTpl, covenantIdOwned(curveCovid, inventory.amount, false));
|
|
716
821
|
const inputs = [
|
|
717
822
|
{ transactionId: utxo.transactionId, index: utxo.index, value: utxo.realKas, scriptPublicKey: cpSpk(k, curRedeem), signatureScript: graduateSigV2(k, curRedeem, poolState, poolTokens), redeem: curRedeem, role: "curve" },
|
|
@@ -733,14 +838,15 @@ function buildSplitToken(k, tokenTpl, sellerToken, sellAmount, presenceWitnessId
|
|
|
733
838
|
const out1 = addressPresenceOwned(owner, sellAmount);
|
|
734
839
|
const out2 = addressPresenceOwned(owner, change);
|
|
735
840
|
const redeem = materializeKcc20Script(tokenTpl, sellerToken.state);
|
|
841
|
+
const binding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 0 } : void 0;
|
|
736
842
|
const inputs = [
|
|
737
843
|
{ transactionId: sellerToken.transactionId, index: sellerToken.index, value: sellerToken.value, scriptPublicKey: kcc20Spk(k, redeem), signatureScript: transferSigScript(k, redeem, [out1, out2], [presenceWitnessIdx]), redeem, role: "sellerToken" }
|
|
738
844
|
];
|
|
739
845
|
const outputs = [
|
|
740
|
-
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out1)), role: "split" },
|
|
741
|
-
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out2)), role: "change" }
|
|
846
|
+
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out1)), role: "split", binding },
|
|
847
|
+
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, out2)), role: "change", binding }
|
|
742
848
|
];
|
|
743
|
-
return { kind: "sell", inputs, outputs, economics: { sellAmount, change }, covids: { tokenCovid:
|
|
849
|
+
return { kind: "sell", inputs, outputs, economics: { sellAmount, change }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
|
|
744
850
|
}
|
|
745
851
|
function buildConsolidate(k, tokenTpl, tokens, presenceWitnessIdx, opts = {}) {
|
|
746
852
|
if (tokens.length < 2) throw new Error("consolidate needs at least 2 UTXOs");
|
|
@@ -754,10 +860,11 @@ function buildConsolidate(k, tokenTpl, tokens, presenceWitnessIdx, opts = {}) {
|
|
|
754
860
|
const r = materializeKcc20Script(tokenTpl, t.state);
|
|
755
861
|
return { transactionId: t.transactionId, index: t.index, value: t.value, scriptPublicKey: kcc20Spk(k, r), signatureScript: transferSigScript(k, r, newStates, witnesses), redeem: r, role: "token" };
|
|
756
862
|
});
|
|
863
|
+
const binding = opts.tokenCovid ? { covid: opts.tokenCovid, authorizingInput: 0 } : void 0;
|
|
757
864
|
const outputs = [
|
|
758
|
-
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, merged)), role: "merged" }
|
|
865
|
+
{ value: dust, scriptPublicKey: kcc20Spk(k, materializeKcc20Script(tokenTpl, merged)), role: "merged", binding }
|
|
759
866
|
];
|
|
760
|
-
return { kind: "sell", inputs, outputs, economics: { total }, covids: { tokenCovid:
|
|
867
|
+
return { kind: "sell", inputs, outputs, economics: { total }, covids: opts.tokenCovid ? { tokenCovid: opts.tokenCovid } : {} };
|
|
761
868
|
}
|
|
762
869
|
var hexOf2 = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
763
870
|
|