@kaspacom/kcc20-tx-builder 0.1.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.
Files changed (234) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +104 -0
  3. package/artifacts/KCC20.placeholder.json +77479 -0
  4. package/artifacts/KCC20FeeTicket.placeholder.json +16846 -0
  5. package/artifacts/KCC20Orderbook.placeholder.json +78001 -0
  6. package/artifacts/KCC20Vesting.placeholder.json +4298 -0
  7. package/artifacts/KCC20Wrapper.placeholder.json +12246 -0
  8. package/dist/abi.d.ts +44 -0
  9. package/dist/abi.d.ts.map +1 -0
  10. package/dist/abi.js +336 -0
  11. package/dist/abi.js.map +1 -0
  12. package/dist/action-source-resolver.d.ts +80 -0
  13. package/dist/action-source-resolver.d.ts.map +1 -0
  14. package/dist/action-source-resolver.js +412 -0
  15. package/dist/action-source-resolver.js.map +1 -0
  16. package/dist/artifacts.d.ts +14 -0
  17. package/dist/artifacts.d.ts.map +1 -0
  18. package/dist/artifacts.js +44 -0
  19. package/dist/artifacts.js.map +1 -0
  20. package/dist/builder.d.ts +12 -0
  21. package/dist/builder.d.ts.map +1 -0
  22. package/dist/builder.js +18 -0
  23. package/dist/builder.js.map +1 -0
  24. package/dist/cjs/abi.d.ts +43 -0
  25. package/dist/cjs/abi.js +361 -0
  26. package/dist/cjs/abi.js.map +1 -0
  27. package/dist/cjs/action-source-resolver.d.ts +79 -0
  28. package/dist/cjs/action-source-resolver.js +435 -0
  29. package/dist/cjs/action-source-resolver.js.map +1 -0
  30. package/dist/cjs/artifacts.d.ts +13 -0
  31. package/dist/cjs/artifacts.js +51 -0
  32. package/dist/cjs/artifacts.js.map +1 -0
  33. package/dist/cjs/builder.d.ts +11 -0
  34. package/dist/cjs/builder.js +22 -0
  35. package/dist/cjs/builder.js.map +1 -0
  36. package/dist/cjs/deploy-operation.d.ts +103 -0
  37. package/dist/cjs/deploy-operation.js +335 -0
  38. package/dist/cjs/deploy-operation.js.map +1 -0
  39. package/dist/cjs/encoding.d.ts +5 -0
  40. package/dist/cjs/encoding.js +51 -0
  41. package/dist/cjs/encoding.js.map +1 -0
  42. package/dist/cjs/engine.d.ts +40 -0
  43. package/dist/cjs/engine.js +8972 -0
  44. package/dist/cjs/engine.js.map +1 -0
  45. package/dist/cjs/errors.d.ts +7 -0
  46. package/dist/cjs/errors.js +23 -0
  47. package/dist/cjs/errors.js.map +1 -0
  48. package/dist/cjs/fee-ticket-operation.d.ts +204 -0
  49. package/dist/cjs/fee-ticket-operation.js +327 -0
  50. package/dist/cjs/fee-ticket-operation.js.map +1 -0
  51. package/dist/cjs/index.d.ts +27 -0
  52. package/dist/cjs/index.js +45 -0
  53. package/dist/cjs/index.js.map +1 -0
  54. package/dist/cjs/ladder-limit-orders.d.ts +41 -0
  55. package/dist/cjs/ladder-limit-orders.js +129 -0
  56. package/dist/cjs/ladder-limit-orders.js.map +1 -0
  57. package/dist/cjs/mint-availability-operation.d.ts +56 -0
  58. package/dist/cjs/mint-availability-operation.js +95 -0
  59. package/dist/cjs/mint-availability-operation.js.map +1 -0
  60. package/dist/cjs/models.d.ts +90 -0
  61. package/dist/cjs/models.js +6 -0
  62. package/dist/cjs/models.js.map +1 -0
  63. package/dist/cjs/operations.d.ts +6 -0
  64. package/dist/cjs/operations.js +41 -0
  65. package/dist/cjs/operations.js.map +1 -0
  66. package/dist/cjs/package.json +1 -0
  67. package/dist/cjs/paid-mint-amount.d.ts +69 -0
  68. package/dist/cjs/paid-mint-amount.js +281 -0
  69. package/dist/cjs/paid-mint-amount.js.map +1 -0
  70. package/dist/cjs/protocol.d.ts +21 -0
  71. package/dist/cjs/protocol.js +25 -0
  72. package/dist/cjs/protocol.js.map +1 -0
  73. package/dist/cjs/runtime.d.ts +27 -0
  74. package/dist/cjs/runtime.js +3 -0
  75. package/dist/cjs/runtime.js.map +1 -0
  76. package/dist/cjs/sale-common.d.ts +287 -0
  77. package/dist/cjs/sale-common.js +928 -0
  78. package/dist/cjs/sale-common.js.map +1 -0
  79. package/dist/cjs/source-provider.d.ts +11 -0
  80. package/dist/cjs/source-provider.js +3 -0
  81. package/dist/cjs/source-provider.js.map +1 -0
  82. package/dist/cjs/source-validation.d.ts +9 -0
  83. package/dist/cjs/source-validation.js +33 -0
  84. package/dist/cjs/source-validation.js.map +1 -0
  85. package/dist/cjs/token-amount.d.ts +31 -0
  86. package/dist/cjs/token-amount.js +252 -0
  87. package/dist/cjs/token-amount.js.map +1 -0
  88. package/dist/cjs/token-operation.d.ts +135 -0
  89. package/dist/cjs/token-operation.js +236 -0
  90. package/dist/cjs/token-operation.js.map +1 -0
  91. package/dist/cjs/trade-fee.d.ts +60 -0
  92. package/dist/cjs/trade-fee.js +185 -0
  93. package/dist/cjs/trade-fee.js.map +1 -0
  94. package/dist/cjs/trading-operation.d.ts +212 -0
  95. package/dist/cjs/trading-operation.js +587 -0
  96. package/dist/cjs/trading-operation.js.map +1 -0
  97. package/dist/cjs/verify-operation.d.ts +79 -0
  98. package/dist/cjs/verify-operation.js +246 -0
  99. package/dist/cjs/verify-operation.js.map +1 -0
  100. package/dist/cjs/wallet-operation.d.ts +52 -0
  101. package/dist/cjs/wallet-operation.js +58 -0
  102. package/dist/cjs/wallet-operation.js.map +1 -0
  103. package/dist/cjs/wasm-provider.d.ts +15 -0
  104. package/dist/cjs/wasm-provider.js +20 -0
  105. package/dist/cjs/wasm-provider.js.map +1 -0
  106. package/dist/cjs/wrapper-operation.d.ts +196 -0
  107. package/dist/cjs/wrapper-operation.js +317 -0
  108. package/dist/cjs/wrapper-operation.js.map +1 -0
  109. package/dist/deploy-operation.d.ts +104 -0
  110. package/dist/deploy-operation.d.ts.map +1 -0
  111. package/dist/deploy-operation.js +327 -0
  112. package/dist/deploy-operation.js.map +1 -0
  113. package/dist/encoding.d.ts +6 -0
  114. package/dist/encoding.d.ts.map +1 -0
  115. package/dist/encoding.js +44 -0
  116. package/dist/encoding.js.map +1 -0
  117. package/dist/engine.d.ts +41 -0
  118. package/dist/engine.d.ts.map +1 -0
  119. package/dist/engine.js +8969 -0
  120. package/dist/engine.js.map +1 -0
  121. package/dist/errors.d.ts +8 -0
  122. package/dist/errors.d.ts.map +1 -0
  123. package/dist/errors.js +18 -0
  124. package/dist/errors.js.map +1 -0
  125. package/dist/fee-ticket-operation.d.ts +205 -0
  126. package/dist/fee-ticket-operation.d.ts.map +1 -0
  127. package/dist/fee-ticket-operation.js +320 -0
  128. package/dist/fee-ticket-operation.js.map +1 -0
  129. package/dist/index.d.ts +28 -0
  130. package/dist/index.d.ts.map +1 -0
  131. package/dist/index.js +28 -0
  132. package/dist/index.js.map +1 -0
  133. package/dist/ladder-limit-orders.d.ts +42 -0
  134. package/dist/ladder-limit-orders.d.ts.map +1 -0
  135. package/dist/ladder-limit-orders.js +121 -0
  136. package/dist/ladder-limit-orders.js.map +1 -0
  137. package/dist/mint-availability-operation.d.ts +57 -0
  138. package/dist/mint-availability-operation.d.ts.map +1 -0
  139. package/dist/mint-availability-operation.js +91 -0
  140. package/dist/mint-availability-operation.js.map +1 -0
  141. package/dist/models.d.ts +91 -0
  142. package/dist/models.d.ts.map +1 -0
  143. package/dist/models.js +3 -0
  144. package/dist/models.js.map +1 -0
  145. package/dist/operations.d.ts +7 -0
  146. package/dist/operations.d.ts.map +1 -0
  147. package/dist/operations.js +36 -0
  148. package/dist/operations.js.map +1 -0
  149. package/dist/paid-mint-amount.d.ts +70 -0
  150. package/dist/paid-mint-amount.d.ts.map +1 -0
  151. package/dist/paid-mint-amount.js +268 -0
  152. package/dist/paid-mint-amount.js.map +1 -0
  153. package/dist/protocol.d.ts +22 -0
  154. package/dist/protocol.d.ts.map +1 -0
  155. package/dist/protocol.js +22 -0
  156. package/dist/protocol.js.map +1 -0
  157. package/dist/runtime.d.ts +28 -0
  158. package/dist/runtime.d.ts.map +1 -0
  159. package/dist/runtime.js +2 -0
  160. package/dist/runtime.js.map +1 -0
  161. package/dist/sale-common.d.ts +288 -0
  162. package/dist/sale-common.d.ts.map +1 -0
  163. package/dist/sale-common.js +844 -0
  164. package/dist/sale-common.js.map +1 -0
  165. package/dist/source-provider.d.ts +12 -0
  166. package/dist/source-provider.d.ts.map +1 -0
  167. package/dist/source-provider.js +2 -0
  168. package/dist/source-provider.js.map +1 -0
  169. package/dist/source-validation.d.ts +10 -0
  170. package/dist/source-validation.d.ts.map +1 -0
  171. package/dist/source-validation.js +28 -0
  172. package/dist/source-validation.js.map +1 -0
  173. package/dist/token-amount.d.ts +32 -0
  174. package/dist/token-amount.d.ts.map +1 -0
  175. package/dist/token-amount.js +237 -0
  176. package/dist/token-amount.js.map +1 -0
  177. package/dist/token-operation.d.ts +136 -0
  178. package/dist/token-operation.d.ts.map +1 -0
  179. package/dist/token-operation.js +231 -0
  180. package/dist/token-operation.js.map +1 -0
  181. package/dist/trade-fee.d.ts +61 -0
  182. package/dist/trade-fee.d.ts.map +1 -0
  183. package/dist/trade-fee.js +173 -0
  184. package/dist/trade-fee.js.map +1 -0
  185. package/dist/trading-operation.d.ts +213 -0
  186. package/dist/trading-operation.d.ts.map +1 -0
  187. package/dist/trading-operation.js +580 -0
  188. package/dist/trading-operation.js.map +1 -0
  189. package/dist/verify-operation.d.ts +80 -0
  190. package/dist/verify-operation.d.ts.map +1 -0
  191. package/dist/verify-operation.js +243 -0
  192. package/dist/verify-operation.js.map +1 -0
  193. package/dist/wallet-operation.d.ts +53 -0
  194. package/dist/wallet-operation.d.ts.map +1 -0
  195. package/dist/wallet-operation.js +55 -0
  196. package/dist/wallet-operation.js.map +1 -0
  197. package/dist/wasm-provider.d.ts +16 -0
  198. package/dist/wasm-provider.d.ts.map +1 -0
  199. package/dist/wasm-provider.js +17 -0
  200. package/dist/wasm-provider.js.map +1 -0
  201. package/dist/wrapper-operation.d.ts +197 -0
  202. package/dist/wrapper-operation.d.ts.map +1 -0
  203. package/dist/wrapper-operation.js +311 -0
  204. package/dist/wrapper-operation.js.map +1 -0
  205. package/index.d.ts +1 -0
  206. package/package.json +84 -0
  207. package/src/abi.ts +493 -0
  208. package/src/action-source-resolver.ts +660 -0
  209. package/src/artifacts.ts +87 -0
  210. package/src/builder.ts +37 -0
  211. package/src/deploy-operation.ts +458 -0
  212. package/src/encoding.ts +58 -0
  213. package/src/engine.ts +13226 -0
  214. package/src/errors.ts +35 -0
  215. package/src/fee-ticket-operation.ts +518 -0
  216. package/src/index.ts +27 -0
  217. package/src/ladder-limit-orders.ts +184 -0
  218. package/src/mint-availability-operation.ts +146 -0
  219. package/src/models.ts +103 -0
  220. package/src/operations.ts +46 -0
  221. package/src/paid-mint-amount.ts +402 -0
  222. package/src/protocol.ts +28 -0
  223. package/src/runtime.ts +29 -0
  224. package/src/sale-common.ts +1238 -0
  225. package/src/source-provider.ts +12 -0
  226. package/src/source-validation.ts +47 -0
  227. package/src/token-amount.ts +302 -0
  228. package/src/token-operation.ts +384 -0
  229. package/src/trade-fee.ts +264 -0
  230. package/src/trading-operation.ts +842 -0
  231. package/src/verify-operation.ts +400 -0
  232. package/src/wallet-operation.ts +86 -0
  233. package/src/wasm-provider.ts +32 -0
  234. package/src/wrapper-operation.ts +531 -0
@@ -0,0 +1,87 @@
1
+ import { Kcc20BuilderError } from "./errors.js";
2
+ import type { ArtifactDescriptor, BuildContext } from "./models.js";
3
+ import { sha256 } from "@noble/hashes/sha256";
4
+
5
+ export const KCC20_ARTIFACT_SCRIPT_SHA256 = Object.freeze({
6
+ "KCC20.placeholder.json":
7
+ "4633b082f26adbf14600b767d6e2146e36ce67d63d8202c0281857922c05ac41",
8
+ "KCC20FeeTicket.placeholder.json":
9
+ "ed81365466ee4c44db1210b3360783af8145f74e49e847848195e09c71ad6930",
10
+ "KCC20Orderbook.placeholder.json":
11
+ "ef9fe81ff7aa2dc2df118d8d7cc126aca6f2f9c181b9c2d15727a538b23c6f71",
12
+ "KCC20Wrapper.placeholder.json":
13
+ "f3bdf09be9aa258708a306283a4278a93cfb64f35fc50ed7d316b9b189ef9030",
14
+ "KCC20Vesting.placeholder.json":
15
+ "1b84d74aa01b6a50edbff5bf1f9e988a5a6a00ec645458dfd3912ac532b77c33",
16
+ });
17
+
18
+ export type Kcc20ArtifactKey = keyof typeof KCC20_ARTIFACT_SCRIPT_SHA256;
19
+
20
+ export function isKcc20ArtifactKey(value: string): value is Kcc20ArtifactKey {
21
+ return Object.prototype.hasOwnProperty.call(
22
+ KCC20_ARTIFACT_SCRIPT_SHA256,
23
+ value,
24
+ );
25
+ }
26
+
27
+ export function assertKcc20ArtifactScriptHash(
28
+ key: Kcc20ArtifactKey,
29
+ artifact: unknown,
30
+ ): void {
31
+ const script = (artifact as { script?: unknown } | null)?.script;
32
+ const bytes =
33
+ script instanceof Uint8Array
34
+ ? script
35
+ : Array.isArray(script) &&
36
+ script.every(
37
+ (byte) => Number.isInteger(byte) && byte >= 0 && byte <= 0xff,
38
+ )
39
+ ? Uint8Array.from(script)
40
+ : null;
41
+ if (!bytes) {
42
+ throw new Kcc20BuilderError(
43
+ "ARTIFACT_MISMATCH",
44
+ `contract artifact script is invalid for ${key}`,
45
+ { artifactKey: key },
46
+ );
47
+ }
48
+ const actualHash = [...sha256(bytes)]
49
+ .map((byte) => byte.toString(16).padStart(2, "0"))
50
+ .join("");
51
+ const expectedHash = KCC20_ARTIFACT_SCRIPT_SHA256[key];
52
+ if (actualHash !== expectedHash) {
53
+ throw new Kcc20BuilderError(
54
+ "ARTIFACT_MISMATCH",
55
+ `contract artifact hash mismatch for ${key}`,
56
+ { artifactKey: key, expectedHash, actualHash },
57
+ );
58
+ }
59
+ }
60
+
61
+ export function requireArtifact(
62
+ context: BuildContext,
63
+ key: string,
64
+ ): ArtifactDescriptor {
65
+ const artifact = context.artifacts[key];
66
+ if (!artifact) {
67
+ throw new Kcc20BuilderError(
68
+ "ARTIFACT_MISMATCH",
69
+ `required contract artifact is missing: ${key}`,
70
+ { artifactKey: key },
71
+ );
72
+ }
73
+ return artifact;
74
+ }
75
+
76
+ export function assertArtifactHash(
77
+ artifact: ArtifactDescriptor,
78
+ expectedHash: string,
79
+ ): void {
80
+ if (artifact.hash.toLowerCase() !== expectedHash.toLowerCase()) {
81
+ throw new Kcc20BuilderError(
82
+ "ARTIFACT_MISMATCH",
83
+ `contract artifact hash mismatch for ${artifact.key}`,
84
+ { artifactKey: artifact.key, expectedHash, actualHash: artifact.hash },
85
+ );
86
+ }
87
+ }
package/src/builder.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { Kcc20BuilderError } from "./errors.js";
2
+ import { createKcc20PsktBuilderEngine } from "./engine.js";
3
+ import type { Kcc20PsktBuilderDependencies } from "./models.js";
4
+
5
+ export const KCC20_PSKT_BUILDER_INPUT_SCHEMA =
6
+ "kcc20-in-process-pskt-builder-input/v1" as const;
7
+
8
+ export async function buildKcc20Pskt(
9
+ dependencies: Kcc20PsktBuilderDependencies,
10
+ request: Kcc20PsktBuilderInput,
11
+ ): Promise<Record<string, unknown>> {
12
+ if (request.schema !== KCC20_PSKT_BUILDER_INPUT_SCHEMA) {
13
+ throw new Kcc20BuilderError(
14
+ "INVALID_INPUT",
15
+ `unsupported KCC20 builder input schema: ${String(request.schema || "missing")}`,
16
+ );
17
+ }
18
+ const engine = createKcc20PsktBuilderEngine({
19
+ wasm: dependencies.wasm,
20
+ artifacts: dependencies.artifacts ?? {},
21
+ artifactProvider: dependencies.artifactProvider,
22
+ config: dependencies.config,
23
+ sourceProvider: dependencies.sourceProvider,
24
+ rpcClientFactory: dependencies.rpcClientFactory,
25
+ });
26
+ return engine.build(request);
27
+ }
28
+
29
+ export interface Kcc20PsktBuilderInput {
30
+ schema: typeof KCC20_PSKT_BUILDER_INPUT_SCHEMA;
31
+ schemaVersion?: number;
32
+ builder: Record<string, unknown>;
33
+ request: Record<string, unknown>;
34
+ }
35
+
36
+ /** @deprecated Use Kcc20PsktBuilderInput. */
37
+ export type Kcc20LegacyPsktBuilderInput = Kcc20PsktBuilderInput;
@@ -0,0 +1,458 @@
1
+ import {
2
+ DEFAULT_KCC20_FEE_BPS,
3
+ DEFAULT_KCC20_MIN_PROTOCOL_FEE_SOMPI,
4
+ DEFAULT_KCC20_TOKEN_DECIMALS,
5
+ KCC20_TICKER_MAX_LENGTH,
6
+ KCC20_TOKEN_NAME_MAX_LENGTH,
7
+ KCC20_PLATFORM_PROTOCOL_FEE_RECIPIENT,
8
+ } from "./protocol.js";
9
+ import {
10
+ KCC20_U64_MAX,
11
+ kcc20DisplayScaleForDecimals,
12
+ parseKcc20DisplayAmountToBaseUnits,
13
+ } from "./token-amount.js";
14
+ import { kcc20PaidMintCapacityMeetsMinimumGross } from "./paid-mint-amount.js";
15
+ import { buildKcc20WalletOperationFromPlan } from "./wallet-operation.js";
16
+
17
+ const HEX_64_RE = /^[a-fA-F0-9]{64}$/;
18
+ const KASPA_ADDRESS_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
19
+ const KASPA_ADDRESS_GENERATORS = [
20
+ 0x98f2bc8e61n,
21
+ 0x79b76d99e2n,
22
+ 0xf33e5fb3c4n,
23
+ 0xae2eabe2a8n,
24
+ 0x1e4f43e470n,
25
+ ];
26
+
27
+ export interface Kcc20DeployTokenDraft {
28
+ ticker?: string;
29
+ tokenName?: string;
30
+ maxSupply: string;
31
+ premintSupply: string;
32
+ decimals?: number;
33
+ premintRecipient?: string;
34
+ mintPolicy: "fixed" | "controlled" | "public";
35
+ mintLaneCount?: number;
36
+ mintPricePerTokenSompi: string;
37
+ treasuryRecipient?: string;
38
+ }
39
+
40
+ export interface Kcc20DeployWalletInfo {
41
+ walletAddress: string;
42
+ kcc20Owner: string;
43
+ }
44
+
45
+ export interface Kcc20DeployOperationOptions {
46
+ network?: string;
47
+ protocolFeeRecipient?: string;
48
+ protocolFeeBps?: number | string | bigint;
49
+ createdAt?: string;
50
+ requestId?: string;
51
+ }
52
+
53
+ export interface Kcc20ValidatedDeployToken {
54
+ ticker: string;
55
+ tokenName: string;
56
+ maxSupply: bigint;
57
+ premintSupply: bigint;
58
+ decimals: number;
59
+ premintRecipient: string;
60
+ mintPricePerTokenSompi: bigint;
61
+ mintPolicy: number;
62
+ treasuryRecipient: string;
63
+ protocolFeeRecipient: string;
64
+ protocolFeeBps: number;
65
+ mintLaneCount?: number;
66
+ }
67
+
68
+ export function buildKcc20DeployTokenOperation(
69
+ walletInfo: Kcc20DeployWalletInfo,
70
+ draft: Kcc20DeployTokenDraft,
71
+ options: Kcc20DeployOperationOptions = {},
72
+ ) {
73
+ const validated = validateKcc20DeployToken(walletInfo, draft, options);
74
+ return buildKcc20DeployTokenOperationFromValidated(
75
+ walletInfo,
76
+ draft,
77
+ validated,
78
+ options,
79
+ );
80
+ }
81
+
82
+ export function buildKcc20DeployTokenOperationFromValidated(
83
+ walletInfo: Kcc20DeployWalletInfo,
84
+ draft: Kcc20DeployTokenDraft,
85
+ validated: Kcc20ValidatedDeployToken,
86
+ options: Kcc20DeployOperationOptions = {},
87
+ ) {
88
+ const requestId =
89
+ options.requestId ?? `kcc20-deploy-token-${randomOperationId()}`;
90
+ const network = options.network ?? "testnet-10";
91
+ const builderKey = "kcc20.deploy-token";
92
+ const params = {
93
+ ticker: validated.ticker,
94
+ tokenName: validated.tokenName,
95
+ maxSupply: validated.maxSupply.toString(),
96
+ premintSupply: validated.premintSupply.toString(),
97
+ decimals: validated.decimals,
98
+ displayMaxSupply: draft.maxSupply,
99
+ displayPremintSupply: draft.premintSupply,
100
+ mintPolicy: draft.mintPolicy,
101
+ mintPolicyValue: validated.mintPolicy,
102
+ mintPricePerTokenSompi: validated.mintPricePerTokenSompi.toString(),
103
+ creator: walletInfo.kcc20Owner,
104
+ premintRecipient: validated.premintRecipient,
105
+ premintOwnerScheme: 0,
106
+ treasuryRecipient: validated.treasuryRecipient,
107
+ protocolFeeRecipient: validated.protocolFeeRecipient,
108
+ protocolFeeBps: validated.protocolFeeBps,
109
+ ...(validated.mintLaneCount == null
110
+ ? {}
111
+ : { mintLaneCount: validated.mintLaneCount }),
112
+ deployTemplate: deployTemplate(walletInfo, validated),
113
+ };
114
+ const sourceRequirements = [
115
+ {
116
+ type: "kaspa-funding-utxo",
117
+ ownerAddress: walletInfo.walletAddress,
118
+ purpose: "fund deploy outputs and network fee",
119
+ },
120
+ ];
121
+ const signerRequirements = [
122
+ {
123
+ type: "wallet-owner",
124
+ owner: walletInfo.kcc20Owner,
125
+ address: walletInfo.walletAddress,
126
+ },
127
+ ];
128
+
129
+ return buildKcc20WalletOperationFromPlan(
130
+ walletInfo,
131
+ {
132
+ builderKey,
133
+ operation: "deploy-token",
134
+ contract: "KCC20",
135
+ action: "deploy",
136
+ description: "Build a KCC20 deploy transaction request.",
137
+ params,
138
+ sourceRequirements,
139
+ signerRequirements,
140
+ warnings: [
141
+ "The wallet must build the standard KCC20 transaction, attach genesis covenant bindings, sign, and broadcast.",
142
+ "Live submission remains wallet-side.",
143
+ ],
144
+ },
145
+ { ...options, requestId, network },
146
+ );
147
+ }
148
+
149
+ export function validateKcc20DeployToken(
150
+ walletInfo: Kcc20DeployWalletInfo,
151
+ draft: Kcc20DeployTokenDraft,
152
+ options: Kcc20DeployOperationOptions = {},
153
+ ): Kcc20ValidatedDeployToken {
154
+ const decimals = draft.decimals ?? DEFAULT_KCC20_TOKEN_DECIMALS;
155
+ const displayScale = kcc20DisplayScaleForDecimals(decimals);
156
+ const ticker = normalizeKcc20Ticker(draft.ticker);
157
+ const tokenName = normalizeKcc20TokenName(draft.tokenName);
158
+ const maxSupply = BigInt(
159
+ parseKcc20DisplayAmountToBaseUnits(draft.maxSupply, decimals, "maxSupply"),
160
+ );
161
+ const premintSupply = BigInt(
162
+ parseKcc20DisplayAmountToBaseUnits(
163
+ draft.premintSupply,
164
+ decimals,
165
+ "premintSupply",
166
+ {
167
+ allowZero: true,
168
+ },
169
+ ),
170
+ );
171
+ const mintPricePerTokenSompi = parseKcc20U64(
172
+ draft.mintPricePerTokenSompi,
173
+ "mintPricePerTokenSompi",
174
+ );
175
+ const mintPolicy = { fixed: 0, controlled: 1, public: 2 }[draft.mintPolicy];
176
+ if (mintPolicy === undefined) {
177
+ throw new Error("mintPolicy is invalid");
178
+ }
179
+ const mintLaneCount = draft.mintLaneCount ?? 10;
180
+ if (
181
+ !Number.isInteger(mintLaneCount) ||
182
+ mintLaneCount < 1 ||
183
+ mintLaneCount > 10
184
+ ) {
185
+ throw new Error("mintLaneCount must be 1..10");
186
+ }
187
+ if (!HEX_64_RE.test(walletInfo.kcc20Owner)) {
188
+ throw new Error("wallet owner must be 64 hex");
189
+ }
190
+ if (maxSupply <= 0n) {
191
+ throw new Error("maxSupply must be greater than zero");
192
+ }
193
+ if (premintSupply > maxSupply) {
194
+ throw new Error("premintSupply cannot exceed maxSupply");
195
+ }
196
+ if (mintPolicy === 0 && premintSupply !== maxSupply) {
197
+ throw new Error("fixed deploys must premint the full maxSupply");
198
+ }
199
+ if (mintPolicy !== 0 && premintSupply === maxSupply) {
200
+ throw new Error(
201
+ "controlled/public mint deploys must leave remaining mint supply",
202
+ );
203
+ }
204
+ if (mintPolicy !== 2 && mintPricePerTokenSompi > 0n) {
205
+ throw new Error("mintPricePerTokenSompi is only valid for public deploys");
206
+ }
207
+ if (
208
+ mintPolicy === 2 &&
209
+ mintPricePerTokenSompi > 0n &&
210
+ !kcc20PaidMintCapacityMeetsMinimumGross({
211
+ remainingTokenAmount: maxSupply - premintSupply,
212
+ unitPriceSompi: mintPricePerTokenSompi,
213
+ priceScale: displayScale,
214
+ minimumGrossSompi: DEFAULT_KCC20_MIN_PROTOCOL_FEE_SOMPI,
215
+ })
216
+ ) {
217
+ throw new Error(
218
+ "public deploys with a mint price must leave enough mintable supply to satisfy the minimum protocol fee",
219
+ );
220
+ }
221
+ if (mintPolicy !== 0 && maxSupply - premintSupply < BigInt(mintLaneCount)) {
222
+ throw new Error("remaining mint supply must cover every mint lane");
223
+ }
224
+ const premintRecipientInput = draft.premintRecipient?.trim();
225
+ if (premintRecipientInput && (mintPolicy === 0 || premintSupply === 0n)) {
226
+ throw new Error(
227
+ "premintRecipient is only valid when controlled/public mint deploys create a premint holder output",
228
+ );
229
+ }
230
+
231
+ const protocolFeeRecipient =
232
+ normalizeOptionalOwner(
233
+ options.protocolFeeRecipient,
234
+ "protocolFeeRecipient",
235
+ ) ?? KCC20_PLATFORM_PROTOCOL_FEE_RECIPIENT;
236
+ const protocolFeeBps = Number(
237
+ options.protocolFeeBps ?? DEFAULT_KCC20_FEE_BPS,
238
+ );
239
+ if (
240
+ !Number.isSafeInteger(protocolFeeBps) ||
241
+ protocolFeeBps < 0 ||
242
+ protocolFeeBps > 10_000
243
+ ) {
244
+ throw new Error("protocolFeeBps must be an integer between 0 and 10000");
245
+ }
246
+
247
+ return {
248
+ ticker,
249
+ tokenName,
250
+ maxSupply,
251
+ premintSupply,
252
+ decimals,
253
+ mintPolicy,
254
+ mintPricePerTokenSompi,
255
+ premintRecipient:
256
+ normalizeOptionalOwner(premintRecipientInput, "premintRecipient") ??
257
+ walletInfo.kcc20Owner,
258
+ treasuryRecipient:
259
+ normalizeOptionalOwner(draft.treasuryRecipient, "treasuryRecipient") ??
260
+ walletInfo.kcc20Owner,
261
+ protocolFeeRecipient,
262
+ protocolFeeBps,
263
+ mintLaneCount: mintPolicy === 0 ? 1 : mintLaneCount,
264
+ };
265
+ }
266
+
267
+ function deployTemplate(
268
+ walletInfo: Kcc20DeployWalletInfo,
269
+ validated: Kcc20ValidatedDeployToken,
270
+ ) {
271
+ return {
272
+ tmpl: "KCC20",
273
+ v: 1,
274
+ args: [
275
+ { name: "creator", type: "byte[32]", value: walletInfo.kcc20Owner },
276
+ { name: "maxSupply", type: "u64", value: validated.maxSupply.toString() },
277
+ {
278
+ name: "premintSupply",
279
+ type: "u64",
280
+ value: validated.premintSupply.toString(),
281
+ },
282
+ {
283
+ name: "premintRecipient",
284
+ type: "byte[32]",
285
+ value: validated.premintRecipient,
286
+ },
287
+ { name: "premintOwnerScheme", type: "u64", value: "0" },
288
+ { name: "mintPolicy", type: "u64", value: String(validated.mintPolicy) },
289
+ ...(validated.mintLaneCount == null
290
+ ? []
291
+ : [
292
+ {
293
+ name: "mintLaneCount",
294
+ type: "u64",
295
+ value: String(validated.mintLaneCount),
296
+ },
297
+ ]),
298
+ {
299
+ name: "displayScale",
300
+ type: "u64",
301
+ value: kcc20DisplayScaleForDecimals(validated.decimals).toString(),
302
+ },
303
+ {
304
+ name: "mintPriceSompi",
305
+ type: "u64",
306
+ value: validated.mintPricePerTokenSompi.toString(),
307
+ },
308
+ {
309
+ name: "treasury",
310
+ type: "byte[32]",
311
+ value: validated.treasuryRecipient,
312
+ },
313
+ {
314
+ name: "protocolFeeRecipient",
315
+ type: "byte[32]",
316
+ value: validated.protocolFeeRecipient,
317
+ },
318
+ {
319
+ name: "protocolFeeBps",
320
+ type: "u64",
321
+ value: String(validated.protocolFeeBps),
322
+ },
323
+ { name: "ticker", type: "string", value: validated.ticker },
324
+ { name: "name", type: "string", value: validated.tokenName },
325
+ ],
326
+ };
327
+ }
328
+
329
+ export function normalizeKcc20Ticker(value: string | undefined): string {
330
+ const normalized = String(value ?? "")
331
+ .trim()
332
+ .toUpperCase();
333
+ if (!normalized) return "";
334
+ if (
335
+ !new RegExp(`^[A-Z0-9]{1,${KCC20_TICKER_MAX_LENGTH}}$`).test(normalized)
336
+ ) {
337
+ throw new Error(
338
+ `ticker must be 1-${KCC20_TICKER_MAX_LENGTH} uppercase alphanumeric characters`,
339
+ );
340
+ }
341
+ return normalized;
342
+ }
343
+
344
+ export function normalizeKcc20TokenName(value: string | undefined): string {
345
+ const normalized = String(value ?? "")
346
+ .trim()
347
+ .replace(/\s+/g, " ");
348
+ if (!normalized) return "";
349
+ if (normalized.length > KCC20_TOKEN_NAME_MAX_LENGTH) {
350
+ throw new Error(
351
+ `tokenName must be ${KCC20_TOKEN_NAME_MAX_LENGTH} characters or fewer`,
352
+ );
353
+ }
354
+ if (!/^[\x20-\x7E]+$/.test(normalized)) {
355
+ throw new Error("tokenName must be printable ASCII");
356
+ }
357
+ return normalized;
358
+ }
359
+
360
+ export function parseKcc20U64(value: string, field: string): bigint {
361
+ if (!/^\d+$/.test(value)) {
362
+ throw new Error(`${field} must be an unsigned integer`);
363
+ }
364
+ const parsed = BigInt(value);
365
+ if (parsed > KCC20_U64_MAX) {
366
+ throw new Error(`${field} exceeds u64 max`);
367
+ }
368
+ return parsed;
369
+ }
370
+
371
+ function normalizeOptionalOwner(
372
+ value: string | undefined,
373
+ field: string,
374
+ ): string | undefined {
375
+ const normalized = value?.trim().toLowerCase();
376
+ if (!normalized) return undefined;
377
+ if (HEX_64_RE.test(normalized)) return normalized;
378
+ const owner = ownerFromKaspaAddress(normalized);
379
+ if (owner) return owner;
380
+ throw new Error(`${field} must be a 64 hex owner or Kaspa P2PK address`);
381
+ }
382
+
383
+ function ownerFromKaspaAddress(value: string): string | undefined {
384
+ const [prefix, payload, extra] = value.split(":");
385
+ if (!prefix || !payload || extra !== undefined) return undefined;
386
+ const data: number[] = [];
387
+ for (const char of payload) {
388
+ const index = KASPA_ADDRESS_CHARSET.indexOf(char);
389
+ if (index === -1) return undefined;
390
+ data.push(index);
391
+ }
392
+ if (
393
+ data.length <= 8 ||
394
+ kaspaAddressPolymod(kaspaAddressPrefixExpand(prefix).concat(data)) !== 1n
395
+ ) {
396
+ return undefined;
397
+ }
398
+ const bytes = convertBits(data.slice(0, -8), 5, 8, false);
399
+ if (!bytes || bytes.length !== 33) return undefined;
400
+ const [version, ...ownerBytes] = bytes;
401
+ if (version !== 0 && version !== 1) return undefined;
402
+ const owner = bytesToHex(ownerBytes);
403
+ return HEX_64_RE.test(owner) ? owner : undefined;
404
+ }
405
+
406
+ function kaspaAddressPrefixExpand(prefix: string): number[] {
407
+ return [...prefix].map((char) => char.charCodeAt(0) & 0x1f).concat([0]);
408
+ }
409
+
410
+ function kaspaAddressPolymod(values: number[]): bigint {
411
+ let checksum = 1n;
412
+ for (const value of values) {
413
+ const top = checksum >> 35n;
414
+ checksum = ((checksum & 0x07ffffffffn) << 5n) ^ BigInt(value);
415
+ for (let index = 0; index < KASPA_ADDRESS_GENERATORS.length; index += 1) {
416
+ if (((top >> BigInt(index)) & 1n) === 1n) {
417
+ checksum ^= KASPA_ADDRESS_GENERATORS[index];
418
+ }
419
+ }
420
+ }
421
+ return checksum;
422
+ }
423
+
424
+ function convertBits(
425
+ data: number[],
426
+ fromBits: number,
427
+ toBits: number,
428
+ pad: boolean,
429
+ ): number[] | undefined {
430
+ let accumulator = 0;
431
+ let bits = 0;
432
+ const result: number[] = [];
433
+ const maxValue = (1 << toBits) - 1;
434
+ const maxAccumulator = (1 << (fromBits + toBits - 1)) - 1;
435
+ for (const value of data) {
436
+ if (value < 0 || value >> fromBits) return undefined;
437
+ accumulator = ((accumulator << fromBits) | value) & maxAccumulator;
438
+ bits += fromBits;
439
+ while (bits >= toBits) {
440
+ bits -= toBits;
441
+ result.push((accumulator >> bits) & maxValue);
442
+ }
443
+ }
444
+ if (pad) {
445
+ if (bits > 0) result.push((accumulator << (toBits - bits)) & maxValue);
446
+ } else if (bits >= fromBits || (accumulator << (toBits - bits)) & maxValue) {
447
+ return undefined;
448
+ }
449
+ return result;
450
+ }
451
+
452
+ function bytesToHex(bytes: number[]): string {
453
+ return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
454
+ }
455
+
456
+ function randomOperationId(): string {
457
+ return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}`;
458
+ }
@@ -0,0 +1,58 @@
1
+ import { Kcc20BuilderError } from "./errors.js";
2
+
3
+ export function hexToBytes(value: string, label = "hex value"): Uint8Array {
4
+ const hex = value.trim().replace(/^0x/i, "");
5
+ if (!/^(?:[0-9a-f]{2})*$/i.test(hex)) {
6
+ throw new Kcc20BuilderError(
7
+ "INVALID_INPUT",
8
+ `${label} must be an even-length hexadecimal string`,
9
+ );
10
+ }
11
+ const bytes = new Uint8Array(hex.length / 2);
12
+ for (let index = 0; index < bytes.length; index += 1) {
13
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
14
+ }
15
+ return bytes;
16
+ }
17
+
18
+ export function bytesToHex(bytes: Uint8Array): string {
19
+ let result = "";
20
+ for (const byte of bytes) result += byte.toString(16).padStart(2, "0");
21
+ return result;
22
+ }
23
+
24
+ export function requireHex(value: string, byteLength: number, label: string) {
25
+ const bytes = hexToBytes(value, label);
26
+ if (bytes.length !== byteLength) {
27
+ throw new Kcc20BuilderError(
28
+ "INVALID_INPUT",
29
+ `${label} must be ${byteLength} bytes`,
30
+ );
31
+ }
32
+ return bytesToHex(bytes);
33
+ }
34
+
35
+ export function asciiToBytes32(value: string, label = "value"): Uint8Array {
36
+ const encoded = new TextEncoder().encode(value);
37
+ if (encoded.length > 32) {
38
+ throw new Kcc20BuilderError(
39
+ "INVALID_INPUT",
40
+ `${label} must be at most 32 UTF-8 bytes`,
41
+ );
42
+ }
43
+ const result = new Uint8Array(32);
44
+ result.set(encoded);
45
+ return result;
46
+ }
47
+
48
+ export function concatByteArrays(...parts: Uint8Array[]): Uint8Array {
49
+ const result = new Uint8Array(
50
+ parts.reduce((length, part) => length + part.length, 0),
51
+ );
52
+ let offset = 0;
53
+ for (const part of parts) {
54
+ result.set(part, offset);
55
+ offset += part.length;
56
+ }
57
+ return result;
58
+ }