@kaspacom/kcc20-tx-builder 0.8.0-fee.1 → 0.8.0-fee.3

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/RELEASING.md +8 -7
  3. package/dist/cjs/engine.js +252 -0
  4. package/dist/cjs/engine.js.map +1 -1
  5. package/dist/cjs/escrow-account-setup.d.ts +53 -0
  6. package/dist/cjs/escrow-account-setup.js +103 -0
  7. package/dist/cjs/escrow-account-setup.js.map +1 -0
  8. package/dist/cjs/escrow-claim-plan.d.ts +74 -0
  9. package/dist/cjs/escrow-claim-plan.js +98 -0
  10. package/dist/cjs/escrow-claim-plan.js.map +1 -0
  11. package/dist/cjs/escrow-receipt.d.ts +103 -0
  12. package/dist/cjs/escrow-receipt.js +293 -0
  13. package/dist/cjs/escrow-receipt.js.map +1 -0
  14. package/dist/cjs/index.d.ts +3 -0
  15. package/dist/cjs/index.js +3 -0
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/operations.d.ts +1 -1
  18. package/dist/cjs/operations.js +2 -0
  19. package/dist/cjs/operations.js.map +1 -1
  20. package/dist/engine.d.ts.map +1 -1
  21. package/dist/engine.js +252 -0
  22. package/dist/engine.js.map +1 -1
  23. package/dist/escrow-account-setup.d.ts +54 -0
  24. package/dist/escrow-account-setup.d.ts.map +1 -0
  25. package/dist/escrow-account-setup.js +99 -0
  26. package/dist/escrow-account-setup.js.map +1 -0
  27. package/dist/escrow-claim-plan.d.ts +75 -0
  28. package/dist/escrow-claim-plan.d.ts.map +1 -0
  29. package/dist/escrow-claim-plan.js +95 -0
  30. package/dist/escrow-claim-plan.js.map +1 -0
  31. package/dist/escrow-receipt.d.ts +104 -0
  32. package/dist/escrow-receipt.d.ts.map +1 -0
  33. package/dist/escrow-receipt.js +287 -0
  34. package/dist/escrow-receipt.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/operations.d.ts +1 -1
  40. package/dist/operations.d.ts.map +1 -1
  41. package/dist/operations.js +2 -0
  42. package/dist/operations.js.map +1 -1
  43. package/docs/COMPATIBILITY.json +1 -1
  44. package/docs/escrow-fees.md +47 -3
  45. package/package.json +3 -3
  46. package/src/engine.ts +292 -0
  47. package/src/escrow-account-setup.ts +124 -0
  48. package/src/escrow-claim-plan.ts +130 -0
  49. package/src/escrow-receipt.ts +333 -0
  50. package/src/index.ts +3 -0
  51. package/src/operations.ts +2 -0
@@ -0,0 +1,333 @@
1
+ import { blake2b } from "@noble/hashes/blake2b";
2
+ import { sha256 } from "@noble/hashes/sha256";
3
+ import { assertKcc20ArtifactScriptHash } from "./artifacts.js";
4
+ import { normalizeSilverScriptArtifact, templateHash } from "./abi.js";
5
+ import { KCC20_ESCROW_MAX_INT, KCC20_ESCROW_MIN_FEE_SOMPI, type Kcc20FeeReceiptAmounts } from "./escrow-fee.js";
6
+ import type { Kcc20FeeAccountState, Kcc20FeeClaimAccount } from "./escrow-claim-plan.js";
7
+
8
+ const ARTIFACT_KEY = "KCC20FeeEscrowV1.placeholder.json";
9
+ // SHA-256 of JSON.stringify(JSON.parse(the packaged artifact)). This pins the
10
+ // ABI and runtime-state layout as well as both copies of the compiled script.
11
+ const ARTIFACT_CANONICAL_SHA256 = "2ff79d6e8ca7176d6346ab7523c72b789f7238865cd9d3b056c83a8af1b5b2cd";
12
+ const STATE_START = 1;
13
+ const STATE_LENGTH = 362;
14
+ const ZERO = "00".repeat(32);
15
+ const FIELDS = [
16
+ ["role", "int"], ["token", "bytes32"], ["market", "bytes32"],
17
+ ["policy", "bytes32"], ["protocol_account", "bytes32"],
18
+ ["creator_account", "bytes32"], ["protocol_amount", "int"],
19
+ ["creator_amount", "int"], ["owner", "bytes32"],
20
+ ["owner_scheme", "byte"], ["guard_template", "bytes32"],
21
+ ["guard_prefix", "int"], ["guard_suffix", "int"],
22
+ ["action_hash", "bytes32"], ["self_template", "bytes32"],
23
+ ["self_prefix", "int"], ["self_suffix", "int"],
24
+ ] as const;
25
+
26
+ export interface Kcc20IndexedFeeReceipt {
27
+ txidHex: string;
28
+ vout: number;
29
+ covenantIdHex: string;
30
+ scriptHashHex: string;
31
+ amountSompiExact: string;
32
+ status: string;
33
+ spentByTxidHex?: string | null;
34
+ state: Record<string, unknown> | null;
35
+ }
36
+
37
+ /** Obtain this UTXO from the canonical node immediately before construction. */
38
+ export interface Kcc20LiveFeeReceiptUtxo {
39
+ outpoint: { transactionId: string; index: number };
40
+ amountSompi: bigint;
41
+ covenantIdHex: string;
42
+ scriptPublicKey: { version: number; scriptHex: string };
43
+ }
44
+
45
+ export interface Kcc20FeeReceiptIdentity {
46
+ tokenId: string;
47
+ marketId: string;
48
+ policyId: string;
49
+ protocolAccountId: string;
50
+ creatorAccountId: string;
51
+ }
52
+
53
+ function hex32(value: unknown, name: string): string {
54
+ if (typeof value !== "string" || !/^[0-9a-f]{64}$/i.test(value)) throw new Error(`${name} must be 32-byte hex`);
55
+ return value.toLowerCase();
56
+ }
57
+
58
+ function natural(value: unknown, name: string): bigint {
59
+ if (typeof value !== "string" || !/^(0|[1-9]\d*)$/.test(value)) throw new Error(`${name} must be a canonical decimal string`);
60
+ const result = BigInt(value);
61
+ if (result > KCC20_ESCROW_MAX_INT) throw new Error(`${name} exceeds the contract range`);
62
+ return result;
63
+ }
64
+
65
+ function bytes(value: unknown, name: string): Uint8Array {
66
+ const text = hex32(value, name);
67
+ return Uint8Array.from(text.match(/../g)!.map(part => Number.parseInt(part, 16)));
68
+ }
69
+
70
+ function encodeState(state: Record<string, unknown>): Uint8Array {
71
+ const encoded: number[] = [];
72
+ for (const [name, kind] of FIELDS) {
73
+ let field: Uint8Array;
74
+ if (kind === "bytes32") field = bytes(state[name], name);
75
+ else if (kind === "byte") {
76
+ const value = state[name];
77
+ if (!Number.isInteger(value) || (value as number) < 0 || (value as number) > 255) throw new Error(`${name} must be a byte`);
78
+ field = Uint8Array.of(value as number);
79
+ } else {
80
+ const value = natural(state[name], name);
81
+ field = new Uint8Array(8);
82
+ new DataView(field.buffer).setBigUint64(0, value, true);
83
+ }
84
+ encoded.push(field.length, ...field);
85
+ }
86
+ if (encoded.length !== STATE_LENGTH) throw new Error("fee receipt state width mismatch");
87
+ return Uint8Array.from(encoded);
88
+ }
89
+
90
+ /** Render an escrow state using only the packaged, pinned v1 program. */
91
+ export function buildKcc20FeeEscrowScript(artifactInput: unknown, state: Record<string, unknown>) {
92
+ const artifactHash = Array.from(sha256(new TextEncoder().encode(JSON.stringify(artifactInput))),
93
+ byte => byte.toString(16).padStart(2, "0")).join("");
94
+ if (artifactHash !== ARTIFACT_CANONICAL_SHA256) throw new Error("fee escrow artifact metadata mismatch");
95
+ const artifact = normalizeSilverScriptArtifact(artifactInput);
96
+ assertKcc20ArtifactScriptHash(ARTIFACT_KEY, artifact);
97
+ if (artifact.contract_name !== "KCC20FeeEscrowV1" ||
98
+ artifact.state_layout?.start !== STATE_START || artifact.state_layout?.len !== STATE_LENGTH) {
99
+ throw new Error("unsupported fee escrow artifact layout");
100
+ }
101
+ const base = Uint8Array.from(artifact.script);
102
+ const prefix = base.slice(0, STATE_START);
103
+ const suffix = base.slice(STATE_START + STATE_LENGTH);
104
+ const hash = Array.from(templateHash(prefix, suffix), byte => byte.toString(16).padStart(2, "0")).join("");
105
+ if (hex32(state.self_template, "self_template") !== hash ||
106
+ natural(state.self_prefix, "self_prefix") !== BigInt(prefix.length) ||
107
+ natural(state.self_suffix, "self_suffix") !== BigInt(suffix.length)) {
108
+ throw new Error("fee escrow self-template mismatch");
109
+ }
110
+ const redeemScript = new Uint8Array(prefix.length + STATE_LENGTH + suffix.length);
111
+ redeemScript.set(prefix);
112
+ redeemScript.set(encodeState(state), prefix.length);
113
+ redeemScript.set(suffix, prefix.length + STATE_LENGTH);
114
+ const scriptHash = Array.from(blake2b(redeemScript, { dkLen: 32 }), byte => byte.toString(16).padStart(2, "0")).join("");
115
+ return {
116
+ redeemScript,
117
+ scriptHash,
118
+ scriptPublicKey: { version: 0 as const, scriptHex: `aa20${scriptHash}87` },
119
+ escrowTemplate: { hash, prefixLength: prefix.length, suffixLength: suffix.length },
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Check a receipt hint against the pinned program and a fresh node UTXO.
125
+ * The caller must fetch `live` from a qualified canonical node, authenticate
126
+ * the market root/policy, and refresh the UTXO again before signing.
127
+ */
128
+ export function verifyKcc20FeeReceiptForClaim(input: {
129
+ artifact: unknown;
130
+ indexed: Kcc20IndexedFeeReceipt;
131
+ live: Kcc20LiveFeeReceiptUtxo;
132
+ expected: Kcc20FeeReceiptIdentity;
133
+ }): { receipt: Kcc20FeeReceiptAmounts; redeemScript: Uint8Array;
134
+ escrowTemplate: { hash: string; prefixLength: number; suffixLength: number } } {
135
+ const artifactHash = Array.from(sha256(new TextEncoder().encode(JSON.stringify(input.artifact))),
136
+ byte => byte.toString(16).padStart(2, "0")).join("");
137
+ if (artifactHash !== ARTIFACT_CANONICAL_SHA256) throw new Error("fee receipt artifact metadata mismatch");
138
+ const artifact = normalizeSilverScriptArtifact(input.artifact);
139
+ assertKcc20ArtifactScriptHash(ARTIFACT_KEY, artifact);
140
+ if (artifact.contract_name !== "KCC20FeeEscrowV1" ||
141
+ artifact.state_layout?.start !== STATE_START || artifact.state_layout?.len !== STATE_LENGTH) {
142
+ throw new Error("unsupported fee receipt artifact layout");
143
+ }
144
+ const base = Uint8Array.from(artifact.script);
145
+ const prefix = base.slice(0, STATE_START);
146
+ const suffix = base.slice(STATE_START + STATE_LENGTH);
147
+ const state = input.indexed.state;
148
+ if (!state || Array.isArray(state) || typeof state !== "object") throw new Error("missing indexed fee receipt state");
149
+ const expected = input.expected;
150
+ const tokenId = hex32(expected.tokenId, "expected token");
151
+ const marketId = hex32(expected.marketId, "expected market");
152
+ const policyId = hex32(expected.policyId, "expected policy");
153
+ const protocolAccountId = hex32(expected.protocolAccountId, "expected protocol account");
154
+ const creatorAccountId = hex32(expected.creatorAccountId, "expected creator account");
155
+ if ([tokenId, marketId, policyId, protocolAccountId].includes(ZERO) ||
156
+ protocolAccountId === creatorAccountId) throw new Error("invalid fee receipt identity");
157
+ for (const [name, value] of [
158
+ ["token", tokenId], ["market", marketId], ["policy", policyId],
159
+ ["protocol_account", protocolAccountId], ["creator_account", creatorAccountId],
160
+ ] as const) {
161
+ if (hex32(state[name], name) !== hex32(value, `expected ${name}`)) throw new Error(`fee receipt ${name} mismatch`);
162
+ }
163
+ if (state.role !== "0" || state.roleName !== "receipt" || state.owner_scheme !== 0 ||
164
+ state.owner !== ZERO || state.guard_template !== ZERO ||
165
+ state.guard_prefix !== "0" || state.guard_suffix !== "0") {
166
+ throw new Error("invalid fee receipt role or owner state");
167
+ }
168
+ const selfHash = Array.from(templateHash(prefix, suffix), byte => byte.toString(16).padStart(2, "0")).join("");
169
+ if (state.self_template !== selfHash || state.self_prefix !== String(prefix.length) ||
170
+ state.self_suffix !== String(suffix.length)) {
171
+ throw new Error("fee receipt self-template mismatch");
172
+ }
173
+ const protocol = natural(state.protocol_amount, "protocol_amount");
174
+ const creator = natural(state.creator_amount, "creator_amount");
175
+ const total = protocol + creator;
176
+ if (total === 0n || total > KCC20_ESCROW_MAX_INT ||
177
+ (protocol > 0n && protocol < KCC20_ESCROW_MIN_FEE_SOMPI) ||
178
+ (creator > 0n && creator < KCC20_ESCROW_MIN_FEE_SOMPI) ||
179
+ (creatorAccountId === ZERO && creator !== 0n)) {
180
+ throw new Error("invalid fee receipt entitlements");
181
+ }
182
+ if (input.indexed.status !== "active" || input.indexed.spentByTxidHex) throw new Error("fee receipt is not active");
183
+ const txid = hex32(input.indexed.txidHex, "indexed txid");
184
+ if (!Number.isInteger(input.indexed.vout) || input.indexed.vout < 0 || input.indexed.vout > 0xffffffff ||
185
+ txid !== hex32(input.live.outpoint.transactionId, "live txid") || input.indexed.vout !== input.live.outpoint.index) {
186
+ throw new Error("fee receipt outpoint mismatch");
187
+ }
188
+ if (hex32(input.indexed.covenantIdHex, "indexed covenant") !== hex32(expected.marketId, "expected market") ||
189
+ hex32(input.live.covenantIdHex, "live covenant") !== hex32(expected.marketId, "expected market")) {
190
+ throw new Error("fee receipt covenant lineage mismatch");
191
+ }
192
+ if (natural(input.indexed.amountSompiExact, "indexed amount") !== total || input.live.amountSompi !== total) {
193
+ throw new Error("fee receipt amount mismatch");
194
+ }
195
+ const stateBytes = encodeState(state);
196
+ const redeemScript = new Uint8Array(prefix.length + stateBytes.length + suffix.length);
197
+ redeemScript.set(prefix);
198
+ redeemScript.set(stateBytes, prefix.length);
199
+ redeemScript.set(suffix, prefix.length + stateBytes.length);
200
+ const scriptHash = Array.from(blake2b(redeemScript, { dkLen: 32 }), byte => byte.toString(16).padStart(2, "0")).join("");
201
+ if (hex32(input.indexed.scriptHashHex, "indexed script hash") !== scriptHash ||
202
+ input.live.scriptPublicKey.version !== 0 ||
203
+ input.live.scriptPublicKey.scriptHex.toLowerCase() !== `aa20${scriptHash}87`) {
204
+ throw new Error("fee receipt script hash mismatch");
205
+ }
206
+ return {
207
+ receipt: {
208
+ outpoint: { transactionId: txid, index: input.indexed.vout },
209
+ tokenId, marketId, policyId, protocolAccountId, creatorAccountId,
210
+ valueSompi: total, protocolSompi: protocol, creatorSompi: creator,
211
+ },
212
+ redeemScript,
213
+ escrowTemplate: { hash: selfHash, prefixLength: prefix.length, suffixLength: suffix.length },
214
+ };
215
+ }
216
+
217
+ /** Authenticate a fee-account hint against the pinned escrow artifact and a fresh node UTXO. */
218
+ export function verifyKcc20FeeAccountForClaim(input: {
219
+ artifact: unknown;
220
+ indexed: Kcc20IndexedFeeReceipt;
221
+ live: Kcc20LiveFeeReceiptUtxo;
222
+ expected: { tokenId: string; marketId: string; policyId: string; accountId: string };
223
+ }): { account: Kcc20FeeClaimAccount; redeemScript: Uint8Array;
224
+ escrowTemplate: { hash: string; prefixLength: number; suffixLength: number } } {
225
+ return verifyFeeAccount(input, false);
226
+ }
227
+
228
+ /** Verify an unbound account before its one-time market binding. */
229
+ export function verifyKcc20UnboundFeeAccountForBinding(input: {
230
+ artifact: unknown;
231
+ indexed: Kcc20IndexedFeeReceipt;
232
+ live: Kcc20LiveFeeReceiptUtxo;
233
+ expected: { tokenId: string; accountId: string };
234
+ }): { account: Kcc20FeeClaimAccount; redeemScript: Uint8Array;
235
+ escrowTemplate: { hash: string; prefixLength: number; suffixLength: number } } {
236
+ return verifyFeeAccount({
237
+ ...input,
238
+ expected: { ...input.expected, marketId: ZERO, policyId: ZERO },
239
+ }, true);
240
+ }
241
+
242
+ function verifyFeeAccount(input: {
243
+ artifact: unknown;
244
+ indexed: Kcc20IndexedFeeReceipt;
245
+ live: Kcc20LiveFeeReceiptUtxo;
246
+ expected: { tokenId: string; marketId: string; policyId: string; accountId: string };
247
+ }, requireUnbound: boolean): { account: Kcc20FeeClaimAccount; redeemScript: Uint8Array;
248
+ escrowTemplate: { hash: string; prefixLength: number; suffixLength: number } } {
249
+ const artifactHash = Array.from(sha256(new TextEncoder().encode(JSON.stringify(input.artifact))),
250
+ byte => byte.toString(16).padStart(2, "0")).join("");
251
+ if (artifactHash !== ARTIFACT_CANONICAL_SHA256) throw new Error("fee account artifact metadata mismatch");
252
+ const artifact = normalizeSilverScriptArtifact(input.artifact);
253
+ assertKcc20ArtifactScriptHash(ARTIFACT_KEY, artifact);
254
+ if (artifact.contract_name !== "KCC20FeeEscrowV1" ||
255
+ artifact.state_layout?.start !== STATE_START || artifact.state_layout?.len !== STATE_LENGTH) {
256
+ throw new Error("unsupported fee account artifact layout");
257
+ }
258
+ const base = Uint8Array.from(artifact.script);
259
+ const prefix = base.slice(0, STATE_START);
260
+ const suffix = base.slice(STATE_START + STATE_LENGTH);
261
+ const state = input.indexed.state;
262
+ if (!state || Array.isArray(state) || typeof state !== "object") throw new Error("missing indexed fee account state");
263
+ const tokenId = hex32(input.expected.tokenId, "expected token");
264
+ const marketId = hex32(input.expected.marketId, "expected market");
265
+ const policyId = hex32(input.expected.policyId, "expected policy");
266
+ const accountId = hex32(input.expected.accountId, "expected account");
267
+ if (tokenId === ZERO || accountId === ZERO ||
268
+ (requireUnbound ? (marketId !== ZERO || policyId !== ZERO) : (marketId === ZERO || policyId === ZERO))) {
269
+ throw new Error("invalid fee account identity");
270
+ }
271
+ for (const [name, expected] of [["token", tokenId], ["market", marketId], ["policy", policyId]] as const) {
272
+ if (hex32(state[name], name) !== expected) throw new Error(`fee account ${name} mismatch`);
273
+ }
274
+ if (state.role !== "1" || state.roleName !== "account" ||
275
+ hex32(state.protocol_account, "protocol_account") !== ZERO ||
276
+ hex32(state.creator_account, "creator_account") !== ZERO ||
277
+ natural(state.protocol_amount, "protocol_amount") !== 0n ||
278
+ natural(state.creator_amount, "creator_amount") !== 0n ||
279
+ hex32(state.owner, "owner") === ZERO ||
280
+ !Number.isInteger(state.owner_scheme) || (state.owner_scheme as number) < 0 ||
281
+ (state.owner_scheme as number) > 4) {
282
+ throw new Error("invalid fee account role or owner state");
283
+ }
284
+ const guard = hex32(state.guard_template, "guard_template");
285
+ const guardPrefix = natural(state.guard_prefix, "guard_prefix");
286
+ const guardSuffix = natural(state.guard_suffix, "guard_suffix");
287
+ const scheme = state.owner_scheme as number;
288
+ if ((scheme <= 2 && (guard !== ZERO || guardPrefix !== 0n || guardSuffix !== 0n)) ||
289
+ (scheme === 3 && (guard === ZERO || guardPrefix !== 0n || guardSuffix !== 0n)) ||
290
+ (scheme === 4 && (guard === ZERO || guardPrefix === 0n || guardSuffix === 0n))) {
291
+ throw new Error("invalid fee account guard state");
292
+ }
293
+ hex32(state.action_hash, "action_hash");
294
+ if (requireUnbound && (scheme !== 0 || state.action_hash !== ZERO)) {
295
+ throw new Error("unbound fee account must have its initial owner and action state");
296
+ }
297
+ const selfHash = Array.from(templateHash(prefix, suffix), byte => byte.toString(16).padStart(2, "0")).join("");
298
+ if (hex32(state.self_template, "self_template") !== selfHash ||
299
+ natural(state.self_prefix, "self_prefix") !== BigInt(prefix.length) ||
300
+ natural(state.self_suffix, "self_suffix") !== BigInt(suffix.length)) {
301
+ throw new Error("fee account self-template mismatch");
302
+ }
303
+ if (input.indexed.status !== "active" || input.indexed.spentByTxidHex) throw new Error("fee account is not active");
304
+ const txid = hex32(input.indexed.txidHex, "indexed txid");
305
+ if (!Number.isInteger(input.indexed.vout) || input.indexed.vout < 0 || input.indexed.vout > 0xffffffff ||
306
+ txid !== hex32(input.live.outpoint.transactionId, "live txid") || input.indexed.vout !== input.live.outpoint.index ||
307
+ hex32(input.indexed.covenantIdHex, "indexed covenant") !== accountId ||
308
+ hex32(input.live.covenantIdHex, "live covenant") !== accountId) {
309
+ throw new Error("fee account outpoint or lineage mismatch");
310
+ }
311
+ const principal = natural(input.indexed.amountSompiExact, "account principal");
312
+ if (principal === 0n || input.live.amountSompi !== principal) throw new Error("fee account principal mismatch");
313
+ const stateBytes = encodeState(state);
314
+ const redeemScript = new Uint8Array(prefix.length + stateBytes.length + suffix.length);
315
+ redeemScript.set(prefix);
316
+ redeemScript.set(stateBytes, prefix.length);
317
+ redeemScript.set(suffix, prefix.length + stateBytes.length);
318
+ const scriptHash = Array.from(blake2b(redeemScript, { dkLen: 32 }), byte => byte.toString(16).padStart(2, "0")).join("");
319
+ if (hex32(input.indexed.scriptHashHex, "indexed script hash") !== scriptHash ||
320
+ input.live.scriptPublicKey.version !== 0 ||
321
+ input.live.scriptPublicKey.scriptHex.toLowerCase() !== `aa20${scriptHash}87`) {
322
+ throw new Error("fee account script hash mismatch");
323
+ }
324
+ return {
325
+ account: {
326
+ outpoint: { transactionId: txid, index: input.indexed.vout },
327
+ covenantId: accountId, principalSompi: principal,
328
+ state: state as unknown as Kcc20FeeAccountState,
329
+ },
330
+ redeemScript,
331
+ escrowTemplate: { hash: selfHash, prefixLength: prefix.length, suffixLength: suffix.length },
332
+ };
333
+ }
package/src/index.ts CHANGED
@@ -21,6 +21,9 @@ export * from "./trade-fee.js";
21
21
  export * from "./escrow-fee.js";
22
22
  export * from "./escrow-policy.js";
23
23
  export * from "./escrow-group.js";
24
+ export * from "./escrow-receipt.js";
25
+ export * from "./escrow-claim-plan.js";
26
+ export * from "./escrow-account-setup.js";
24
27
  export * from "./trading-operation.js";
25
28
  export * from "./protocol.js";
26
29
  export * from "./runtime.js";
package/src/operations.ts CHANGED
@@ -22,6 +22,8 @@ export const KCC20_BUILDER_KEYS = [
22
22
  "fee-ticket.create-from-utility-burn",
23
23
  "fee-ticket.burn",
24
24
  "fee-ticket.transfer",
25
+ "kcc20fee.create-account",
26
+ "kcc20fee.bind-account",
25
27
  "kcc20-snapshot.deploy-bootstrap",
26
28
  "kcc20-snapshot.deploy-controller",
27
29
  "kcc20-snapshot.start",