@wishknish/knishio-client-ts 0.7.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 (141) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +425 -0
  3. package/dist/index.cjs +9961 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.iife.js +32241 -0
  6. package/dist/index.iife.js.map +1 -0
  7. package/dist/index.js +9853 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +113 -0
  10. package/src/AuthToken.ts +214 -0
  11. package/src/KnishIOClient.ts +2020 -0
  12. package/src/constants.ts +398 -0
  13. package/src/core/Atom.ts +646 -0
  14. package/src/core/AtomMeta.ts +278 -0
  15. package/src/core/Meta.ts +428 -0
  16. package/src/core/Molecule.ts +825 -0
  17. package/src/core/PolicyMeta.ts +130 -0
  18. package/src/core/TokenUnit.ts +148 -0
  19. package/src/core/Wallet.ts +467 -0
  20. package/src/exception/AtomIndexException.ts +97 -0
  21. package/src/exception/AtomsMissingException.ts +109 -0
  22. package/src/exception/AuthorizationRejectedException.ts +63 -0
  23. package/src/exception/BalanceInsufficientException.ts +58 -0
  24. package/src/exception/BaseException.ts +275 -0
  25. package/src/exception/BatchIdException.ts +58 -0
  26. package/src/exception/CodeException.ts +119 -0
  27. package/src/exception/DecryptionKeyException.ts +65 -0
  28. package/src/exception/InvalidResponseException.ts +112 -0
  29. package/src/exception/MetaMissingException.ts +58 -0
  30. package/src/exception/MolecularHashMismatchException.ts +115 -0
  31. package/src/exception/MolecularHashMissingException.ts +58 -0
  32. package/src/exception/NegativeAmountException.ts +58 -0
  33. package/src/exception/PolicyInvalidException.ts +58 -0
  34. package/src/exception/SignatureMalformedException.ts +58 -0
  35. package/src/exception/SignatureMismatchException.ts +98 -0
  36. package/src/exception/StackableUnitAmountException.ts +65 -0
  37. package/src/exception/StackableUnitDecimalsException.ts +65 -0
  38. package/src/exception/TransferBalanceException.ts +98 -0
  39. package/src/exception/TransferMalformedException.ts +58 -0
  40. package/src/exception/TransferMismatchedException.ts +58 -0
  41. package/src/exception/TransferRemainderException.ts +58 -0
  42. package/src/exception/TransferToSelfException.ts +58 -0
  43. package/src/exception/TransferUnbalancedException.ts +58 -0
  44. package/src/exception/UnauthenticatedException.ts +147 -0
  45. package/src/exception/WalletCredentialException.ts +108 -0
  46. package/src/exception/WalletShadowException.ts +65 -0
  47. package/src/exception/WrongTokenTypeException.ts +58 -0
  48. package/src/exception/index.ts +272 -0
  49. package/src/index.ts +512 -0
  50. package/src/instance/rules/Callback.ts +257 -0
  51. package/src/instance/rules/Condition.ts +96 -0
  52. package/src/instance/rules/Meta.ts +163 -0
  53. package/src/instance/rules/Rule.ts +29 -0
  54. package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
  55. package/src/libraries/CheckMolecule.ts +581 -0
  56. package/src/libraries/Decimal.ts +94 -0
  57. package/src/libraries/Dot.ts +202 -0
  58. package/src/libraries/GraphQLClient.ts +276 -0
  59. package/src/libraries/Hex.ts +155 -0
  60. package/src/libraries/UrqlClientWrapper.ts +336 -0
  61. package/src/libraries/array.ts +91 -0
  62. package/src/libraries/crypto.ts +816 -0
  63. package/src/libraries/strings.ts +458 -0
  64. package/src/mutation/Mutation.ts +103 -0
  65. package/src/mutation/MutationActiveSession.ts +108 -0
  66. package/src/mutation/MutationClaimShadowWallet.ts +91 -0
  67. package/src/mutation/MutationCreateIdentifier.ts +86 -0
  68. package/src/mutation/MutationCreateMeta.ts +92 -0
  69. package/src/mutation/MutationCreateRule.ts +90 -0
  70. package/src/mutation/MutationCreateToken.ts +92 -0
  71. package/src/mutation/MutationCreateWallet.ts +78 -0
  72. package/src/mutation/MutationDepositBufferToken.ts +72 -0
  73. package/src/mutation/MutationLinkIdentifier.ts +87 -0
  74. package/src/mutation/MutationProposeMolecule.ts +147 -0
  75. package/src/mutation/MutationRequestAuthorization.ts +77 -0
  76. package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
  77. package/src/mutation/MutationRequestTokens.ts +98 -0
  78. package/src/mutation/MutationTransferTokens.ts +87 -0
  79. package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
  80. package/src/query/Query.ts +187 -0
  81. package/src/query/QueryActiveSession.ts +89 -0
  82. package/src/query/QueryAtom.ts +275 -0
  83. package/src/query/QueryBalance.ts +102 -0
  84. package/src/query/QueryBatch.ts +145 -0
  85. package/src/query/QueryBatchHistory.ts +86 -0
  86. package/src/query/QueryContinuId.ts +91 -0
  87. package/src/query/QueryMetaType.ts +177 -0
  88. package/src/query/QueryMetaTypeViaAtom.ts +199 -0
  89. package/src/query/QueryPolicy.ts +91 -0
  90. package/src/query/QueryToken.ts +90 -0
  91. package/src/query/QueryUserActivity.ts +153 -0
  92. package/src/query/QueryWalletBundle.ts +92 -0
  93. package/src/query/QueryWalletList.ts +106 -0
  94. package/src/response/EnhancedResponse.ts +345 -0
  95. package/src/response/Response.ts +254 -0
  96. package/src/response/ResponseActiveSession.ts +72 -0
  97. package/src/response/ResponseAtom.ts +132 -0
  98. package/src/response/ResponseAuthorizationGuest.ts +119 -0
  99. package/src/response/ResponseBalance.ts +153 -0
  100. package/src/response/ResponseClaimShadowWallet.ts +56 -0
  101. package/src/response/ResponseContinuId.ts +101 -0
  102. package/src/response/ResponseCreateIdentifier.ts +56 -0
  103. package/src/response/ResponseCreateMeta.ts +58 -0
  104. package/src/response/ResponseCreateRule.ts +56 -0
  105. package/src/response/ResponseCreateToken.ts +58 -0
  106. package/src/response/ResponseCreateWallet.ts +58 -0
  107. package/src/response/ResponseLinkIdentifier.ts +87 -0
  108. package/src/response/ResponseMetaBatch.ts +72 -0
  109. package/src/response/ResponseMetaType.ts +108 -0
  110. package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
  111. package/src/response/ResponsePolicy.ts +90 -0
  112. package/src/response/ResponseProposeMolecule.ts +155 -0
  113. package/src/response/ResponseQueryActiveSession.ts +105 -0
  114. package/src/response/ResponseQueryUserActivity.ts +89 -0
  115. package/src/response/ResponseRequestAuthorization.ts +100 -0
  116. package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
  117. package/src/response/ResponseRequestTokens.ts +58 -0
  118. package/src/response/ResponseTransferTokens.ts +72 -0
  119. package/src/response/ResponseWalletBundle.ts +95 -0
  120. package/src/response/ResponseWalletList.ts +165 -0
  121. package/src/schemas/index.ts +457 -0
  122. package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
  123. package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
  124. package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
  125. package/src/subscribe/Subscribe.ts +182 -0
  126. package/src/subscribe/WalletStatusSubscribe.ts +70 -0
  127. package/src/subscribe/index.ts +59 -0
  128. package/src/types/assertions.ts +722 -0
  129. package/src/types/client.ts +567 -0
  130. package/src/types/crypto.ts +541 -0
  131. package/src/types/graphql.ts +630 -0
  132. package/src/types/guards.ts +659 -0
  133. package/src/types/index.ts +614 -0
  134. package/src/types/response.ts +133 -0
  135. package/src/types/template-literals.ts +382 -0
  136. package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
  137. package/src/validation/ValidationService.ts +607 -0
  138. package/src/validation/schemas.ts +447 -0
  139. package/src/versions/HashAtom.ts +170 -0
  140. package/src/versions/Version4.ts +120 -0
  141. package/src/versions/index.ts +80 -0
@@ -0,0 +1,825 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ import Atom from './Atom'
50
+ import AtomMeta from './AtomMeta'
51
+ import Wallet from './Wallet'
52
+ import CheckMolecule from '@/libraries/CheckMolecule'
53
+ import { chunkSubstr, hexToBase64 } from '@/libraries/strings'
54
+ import { generateBundleHash, generateOTSSignature } from '@/libraries/crypto'
55
+ import { deepCloning } from '@/libraries/array'
56
+ import {
57
+ AtomsMissingException,
58
+ BalanceInsufficientException,
59
+ NegativeAmountException,
60
+ SignatureMalformedException
61
+ } from '@/exception'
62
+ import type { AtomIsotope } from '@/types'
63
+
64
+ /**
65
+ * Molecule class - Transaction container for KnishIO DLT
66
+ * Essential implementation for basic transactions
67
+ */
68
+ export default class Molecule {
69
+ public status: string | null
70
+ public molecularHash: string | null
71
+ public createdAt: string
72
+ public cellSlugOrigin: string | null
73
+ public cellSlug: string | null
74
+ public secret: string | null
75
+ public bundle: string | null
76
+ public sourceWallet: Wallet | null
77
+ public remainderWallet: Wallet | null
78
+ public atoms: Atom[]
79
+ public version: string | null
80
+ public local?: number
81
+
82
+ /**
83
+ * Create new Molecule instance
84
+ * Matches JavaScript SDK constructor signature
85
+ */
86
+ constructor({
87
+ secret = null,
88
+ bundle = null,
89
+ sourceWallet = null,
90
+ remainderWallet = null,
91
+ cellSlug = null,
92
+ version = null
93
+ }: {
94
+ secret?: string | null
95
+ bundle?: string | null
96
+ sourceWallet?: Wallet | null
97
+ remainderWallet?: Wallet | null
98
+ cellSlug?: string | null
99
+ version?: string | number | null
100
+ } = {}) {
101
+ this.status = null
102
+ this.molecularHash = null
103
+ this.createdAt = String(+new Date())
104
+ this.cellSlugOrigin = this.cellSlug = cellSlug
105
+ this.secret = secret
106
+ this.bundle = bundle
107
+ this.sourceWallet = sourceWallet
108
+ this.atoms = []
109
+
110
+ if (version !== null) {
111
+ this.version = String(version)
112
+ } else {
113
+ this.version = null
114
+ }
115
+
116
+ // Set the remainder wallet for this transaction
117
+ if (remainderWallet || sourceWallet) {
118
+ this.remainderWallet = remainderWallet || Wallet.create({
119
+ secret: secret!,
120
+ bundle,
121
+ token: sourceWallet!.token,
122
+ batchId: sourceWallet!.batchId,
123
+ characters: sourceWallet!.characters
124
+ })
125
+ } else {
126
+ this.remainderWallet = null
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Returns the cell slug delimiter
132
+ */
133
+ get cellSlugDelimiter(): string {
134
+ return '.'
135
+ }
136
+
137
+ /**
138
+ * Filters atoms by isotope(s)
139
+ * Matches JavaScript SDK getIsotopes method
140
+ */
141
+ getIsotopes(isotopes: AtomIsotope | AtomIsotope[]): Atom[] {
142
+ return Molecule.isotopeFilter(isotopes, this.atoms)
143
+ }
144
+
145
+ /**
146
+ * Filters the atoms array by the supplied isotope list
147
+ * Static version matching JavaScript SDK
148
+ */
149
+ static isotopeFilter(isotopes: AtomIsotope | AtomIsotope[], atoms: Atom[]): Atom[] {
150
+ if (!Array.isArray(isotopes)) {
151
+ isotopes = [isotopes]
152
+ }
153
+ return atoms.filter(atom => isotopes.includes(atom.isotope))
154
+ }
155
+
156
+ /**
157
+ * Generates the next atomic index
158
+ */
159
+ static generateNextAtomIndex(atoms: Atom[]): number {
160
+ return atoms.length
161
+ }
162
+
163
+ /**
164
+ * Generates the next atomic index for this molecule
165
+ */
166
+ generateIndex(): number {
167
+ return Molecule.generateNextAtomIndex(this.atoms)
168
+ }
169
+
170
+ /**
171
+ * Add an atom to this molecule
172
+ * Matches JavaScript SDK addAtom method
173
+ */
174
+ addAtom(atom: Atom): Molecule {
175
+ // Reset the molecular hash
176
+ this.molecularHash = null
177
+
178
+ // Set atom's index
179
+ atom.index = this.generateIndex()
180
+ atom.version = this.version ? Number(this.version) : 4
181
+
182
+ // Add atom
183
+ this.atoms.push(atom)
184
+
185
+ // Sort atoms
186
+ this.atoms = Atom.sortAtoms(this.atoms)
187
+
188
+ return this
189
+ }
190
+
191
+ /**
192
+ * Add user remainder atom for ContinuID
193
+ */
194
+ addContinuIdAtom(): Molecule {
195
+ if (!this.remainderWallet) {
196
+ throw new Error('Remainder wallet required for ContinuID atom')
197
+ }
198
+
199
+ this.addAtom(new Atom({
200
+ isotope: 'I',
201
+ position: this.remainderWallet.position!,
202
+ walletAddress: this.remainderWallet.address! as any,
203
+ token: 'USER',
204
+ metaType: 'walletBundle',
205
+ metaId: this.remainderWallet.bundle!
206
+ }))
207
+ return this
208
+ }
209
+
210
+ /**
211
+ * Initialize a V-type molecule to transfer value
212
+ * Essential method for basic transfers
213
+ */
214
+ initValue({
215
+ recipientWallet,
216
+ amount
217
+ }: {
218
+ recipientWallet: Wallet
219
+ amount: number
220
+ }): Molecule {
221
+ if (!this.sourceWallet) {
222
+ throw new Error('Source wallet required for value transfer')
223
+ }
224
+
225
+ if (this.sourceWallet.balance - amount < 0) {
226
+ throw new BalanceInsufficientException()
227
+ }
228
+
229
+ // Initializing a new Atom to remove entire balance from source (UTXO model)
230
+ this.addAtom(new Atom({
231
+ isotope: 'V',
232
+ position: this.sourceWallet.position!,
233
+ walletAddress: this.sourceWallet.address! as any,
234
+ token: this.sourceWallet.token,
235
+ value: -this.sourceWallet.balance
236
+ }))
237
+
238
+ // Initializing a new Atom to add tokens to recipient
239
+ this.addAtom(new Atom({
240
+ isotope: 'V',
241
+ position: recipientWallet.position!,
242
+ walletAddress: recipientWallet.address! as any,
243
+ token: recipientWallet.token,
244
+ value: amount,
245
+ metaType: 'walletBundle',
246
+ metaId: recipientWallet.bundle!
247
+ }))
248
+
249
+ // Initializing a remainder atom (matching JavaScript SDK - uses existing remainderWallet)
250
+ if (!this.remainderWallet) {
251
+ throw new Error('Remainder wallet required for value transfer')
252
+ }
253
+
254
+ this.addAtom(new Atom({
255
+ isotope: 'V',
256
+ position: this.remainderWallet.position!,
257
+ walletAddress: this.remainderWallet.address! as any,
258
+ token: this.remainderWallet.token,
259
+ value: this.sourceWallet.balance - amount,
260
+ metaType: 'walletBundle',
261
+ metaId: this.remainderWallet.bundle!
262
+ }))
263
+
264
+ return this
265
+ }
266
+
267
+ /**
268
+ * Sign the molecule with one-time signature
269
+ * Matches JavaScript SDK sign method
270
+ */
271
+ sign({
272
+ bundle = null,
273
+ anonymous = false,
274
+ compressed = true
275
+ }: {
276
+ bundle?: string | null
277
+ anonymous?: boolean
278
+ compressed?: boolean
279
+ } = {}): string | null {
280
+ // Do we have atoms?
281
+ if (this.atoms.length === 0 || this.atoms.filter(atom => !(atom instanceof Atom)).length !== 0) {
282
+ throw new AtomsMissingException()
283
+ }
284
+
285
+ // Derive the user's bundle
286
+ if (!anonymous && !this.bundle) {
287
+ this.bundle = bundle || generateBundleHash(this.secret!, 'Molecule::sign')
288
+ }
289
+
290
+ // Hash atoms to get molecular hash
291
+ this.molecularHash = Atom.hashAtoms({
292
+ atoms: this.atoms
293
+ })
294
+
295
+ // Signing atom
296
+ const signingAtom = this.atoms[0]
297
+ if (!signingAtom) {
298
+ throw new SignatureMalformedException('No atoms available for signing!')
299
+ }
300
+
301
+ // Set signing position from the first atom
302
+ let signingPosition = signingAtom.position
303
+
304
+ // Signing position is required
305
+ if (!signingPosition) {
306
+ throw new SignatureMalformedException('Signing wallet must have a position!')
307
+ }
308
+
309
+ // Generate the private signing key for this molecule
310
+ const key = Wallet.generateKey({
311
+ secret: this.secret!,
312
+ token: signingAtom.token as string,
313
+ position: signingPosition
314
+ })
315
+
316
+ // Use standardized WOTS+ signature generation from crypto library
317
+ // This ensures 100% compatibility with Implementation Guide requirements
318
+ let signatureFragments = generateOTSSignature(key, this.molecularHash)
319
+
320
+ // Compressing the OTS
321
+ if (compressed) {
322
+ signatureFragments = hexToBase64(signatureFragments)
323
+ }
324
+
325
+ // Chunking the signature across multiple atoms
326
+ const chunkedSignature = chunkSubstr(signatureFragments, Math.ceil(signatureFragments.length / this.atoms.length))
327
+
328
+ let lastPosition: string | null = null
329
+
330
+ for (let chunkCount = 0, condition = chunkedSignature.length; chunkCount < condition; chunkCount++) {
331
+ const atom = this.atoms[chunkCount]
332
+ const signatureChunk = chunkedSignature[chunkCount]
333
+ if (atom && signatureChunk) {
334
+ atom.otsFragment = signatureChunk
335
+ lastPosition = atom.position
336
+ }
337
+ }
338
+
339
+ return lastPosition
340
+ }
341
+
342
+ /**
343
+ * Validate the molecular structure
344
+ * Uses CheckMolecule for validation
345
+ * Matches JavaScript SDK check method exactly
346
+ */
347
+ check(senderWallet: Wallet | null = null): boolean {
348
+ return (new CheckMolecule(this as any)).verify(senderWallet)
349
+ }
350
+
351
+ /**
352
+ * Convert Hm to numeric notation via EnumerateMolecule(Hm)
353
+ * Required by CheckMolecule for OTS validation
354
+ */
355
+ normalizedHash(): number[] {
356
+ if (!this.molecularHash) {
357
+ throw new Error('Molecular hash not set')
358
+ }
359
+ return Molecule.normalize(Molecule.enumerate(this.molecularHash))
360
+ }
361
+
362
+ /**
363
+ * Enumerate a hash string to numeric values
364
+ * Matches JavaScript SDK Molecule.enumerate exactly
365
+ */
366
+ static enumerate(hash: string): number[] {
367
+ const mapped: Record<string, number> = {
368
+ '0': -8,
369
+ '1': -7,
370
+ '2': -6,
371
+ '3': -5,
372
+ '4': -4,
373
+ '5': -3,
374
+ '6': -2,
375
+ '7': -1,
376
+ '8': 0,
377
+ '9': 1,
378
+ 'a': 2,
379
+ 'b': 3,
380
+ 'c': 4,
381
+ 'd': 5,
382
+ 'e': 6,
383
+ 'f': 7,
384
+ 'g': 8
385
+ }
386
+ const target: number[] = []
387
+ const hashList = hash.toLowerCase().split('')
388
+
389
+ for (let index = 0, len = hashList.length; index < len; ++index) {
390
+ const symbol = hashList[index]
391
+
392
+ if (symbol && typeof mapped[symbol] !== 'undefined') {
393
+ target[index] = mapped[symbol]!
394
+ }
395
+ }
396
+
397
+ return target
398
+ }
399
+
400
+ /**
401
+ * Normalize enumerated string to ensure sum equals zero
402
+ * Matches JavaScript SDK Molecule.normalize exactly
403
+ */
404
+ static normalize(mappedHashArray: number[]): number[] {
405
+ let total = mappedHashArray.reduce((total, num) => total + num)
406
+
407
+ const totalCondition = total < 0
408
+
409
+ while (total < 0 || total > 0) {
410
+ for (const index of Object.keys(mappedHashArray)) {
411
+ const idx = Number(index)
412
+ const condition = totalCondition ? (mappedHashArray[idx] ?? 0) < 8 : (mappedHashArray[idx] ?? 0) > -8
413
+
414
+ if (condition) {
415
+ if (totalCondition) {
416
+ mappedHashArray[idx] = (mappedHashArray[idx] ?? 0) + 1
417
+ total++
418
+ } else {
419
+ mappedHashArray[idx] = (mappedHashArray[idx] ?? 0) - 1
420
+ total--
421
+ }
422
+
423
+ if (total === 0) {
424
+ break
425
+ }
426
+ }
427
+ }
428
+ }
429
+
430
+ return mappedHashArray
431
+ }
432
+
433
+ /**
434
+ * Returns JSON-ready object for cross-SDK compatibility (2025 TS best practices)
435
+ *
436
+ * Includes all necessary fields for cross-SDK validation while excluding sensitive data.
437
+ * Follows 2025 TypeScript best practices with proper error handling and type safety.
438
+ *
439
+ * @param options - Serialization options
440
+ * @param options.includeValidationContext - Include sourceWallet/remainderWallet for validation (default: true)
441
+ * @param options.includeOtsFragments - Include OTS signature fragments (default: true)
442
+ * @param options.secureMode - Extra security checks (default: false)
443
+ * @return JSON-serializable object
444
+ * @throws Error if molecule is in invalid state for serialization
445
+ */
446
+ toJSON(options: {
447
+ includeValidationContext?: boolean
448
+ includeOtsFragments?: boolean
449
+ secureMode?: boolean
450
+ } = {}): any {
451
+ const {
452
+ includeValidationContext = true,
453
+ includeOtsFragments = true,
454
+ secureMode = false
455
+ } = options
456
+
457
+ try {
458
+ // Security check in secure mode
459
+ if (secureMode && this.secret) {
460
+ throw new Error('Cannot serialize molecule with secret in secure mode')
461
+ }
462
+
463
+ // Core molecule properties (always included)
464
+ const serialized: any = {
465
+ status: this.status,
466
+ molecularHash: this.molecularHash,
467
+ createdAt: this.createdAt,
468
+ cellSlug: this.cellSlug,
469
+ cellSlugOrigin: this.cellSlugOrigin,
470
+ version: this.version,
471
+ bundle: this.bundle,
472
+
473
+ // Serialized atoms array with optional OTS fragments
474
+ atoms: this.atoms.map(atom => atom.toJSON({
475
+ includeOtsFragments
476
+ }))
477
+ }
478
+
479
+ // Validation context (essential for cross-SDK validation)
480
+ if (includeValidationContext) {
481
+ if (this.sourceWallet) {
482
+ serialized.sourceWallet = {
483
+ address: this.sourceWallet.address,
484
+ position: this.sourceWallet.position,
485
+ token: this.sourceWallet.token,
486
+ balance: this.sourceWallet.balance || 0,
487
+ bundle: this.sourceWallet.bundle,
488
+ batchId: this.sourceWallet.batchId || null,
489
+ characters: this.sourceWallet.characters || 'BASE64',
490
+ // Exclude sensitive fields like secret, key, privkey
491
+ pubkey: this.sourceWallet.pubkey || null,
492
+ tokenUnits: this.sourceWallet.tokenUnits || [],
493
+ tradeRates: this.sourceWallet.tradeRates || {},
494
+ molecules: this.sourceWallet.molecules || {}
495
+ }
496
+ }
497
+
498
+ if (this.remainderWallet) {
499
+ serialized.remainderWallet = {
500
+ address: this.remainderWallet.address,
501
+ position: this.remainderWallet.position,
502
+ token: this.remainderWallet.token,
503
+ balance: this.remainderWallet.balance || 0,
504
+ bundle: this.remainderWallet.bundle,
505
+ batchId: this.remainderWallet.batchId || null,
506
+ characters: this.remainderWallet.characters || 'BASE64',
507
+ // Exclude sensitive fields
508
+ pubkey: this.remainderWallet.pubkey || null,
509
+ tokenUnits: this.remainderWallet.tokenUnits || [],
510
+ tradeRates: this.remainderWallet.tradeRates || {},
511
+ molecules: this.remainderWallet.molecules || {}
512
+ }
513
+ }
514
+ }
515
+
516
+ return serialized
517
+
518
+ } catch (error) {
519
+ throw new Error(`Molecule serialization failed: ${(error as Error).message}`)
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Returns the base cell slug portion
525
+ */
526
+ cellSlugBase(): string {
527
+ return (this.cellSlug || '').split(this.cellSlugDelimiter)[0] || ''
528
+ }
529
+
530
+ // =============================================================================
531
+ // STUB METHODS - Implement as needed (YAGNI)
532
+ // =============================================================================
533
+
534
+ /**
535
+ * Initialize token creation
536
+ */
537
+ initTokenCreation({
538
+ recipientWallet,
539
+ amount,
540
+ meta
541
+ }: {
542
+ recipientWallet: Wallet
543
+ amount: number
544
+ meta: any
545
+ }): Molecule {
546
+ // Create a new token by adding atoms
547
+ this.addAtom(new Atom({
548
+ isotope: 'T',
549
+ position: recipientWallet.position!,
550
+ walletAddress: recipientWallet.address! as any,
551
+ token: recipientWallet.token,
552
+ value: amount,
553
+ metaType: 'token',
554
+ metaId: recipientWallet.token,
555
+ meta: meta ? [{ key: 'tokenMeta', value: JSON.stringify(meta) }] : []
556
+ }))
557
+
558
+ // Add wallet bundle metadata
559
+ this.addAtom(new Atom({
560
+ isotope: 'M',
561
+ position: recipientWallet.position!,
562
+ walletAddress: recipientWallet.address! as any,
563
+ token: recipientWallet.token,
564
+ metaType: 'walletBundle',
565
+ metaId: recipientWallet.bundle!,
566
+ meta: meta ? [{ key: 'tokenMeta', value: JSON.stringify(meta) }] : []
567
+ }))
568
+
569
+ // Add ContinuID atom (matching JavaScript SDK)
570
+ this.addContinuIdAtom()
571
+
572
+ return this
573
+ }
574
+
575
+ /**
576
+ * Burn tokens
577
+ */
578
+ burnToken({
579
+ amount,
580
+ walletBundle = null
581
+ }: {
582
+ amount: number
583
+ walletBundle?: string | null
584
+ }): Molecule {
585
+ if (!this.sourceWallet) {
586
+ throw new Error('Source wallet required for token burning')
587
+ }
588
+
589
+ if (amount <= 0) {
590
+ throw new NegativeAmountException()
591
+ }
592
+
593
+ if (this.sourceWallet.balance - amount < 0) {
594
+ throw new BalanceInsufficientException()
595
+ }
596
+
597
+ // Burn tokens by removing from source
598
+ this.addAtom(new Atom({
599
+ isotope: 'V',
600
+ position: this.sourceWallet.position!,
601
+ walletAddress: this.sourceWallet.address! as any,
602
+ token: this.sourceWallet.token,
603
+ value: -amount,
604
+ metaType: walletBundle ? 'walletBundle' : null,
605
+ metaId: walletBundle || null
606
+ }))
607
+
608
+ return this
609
+ }
610
+
611
+ /**
612
+ * Initialize meta
613
+ */
614
+ initMeta({
615
+ meta,
616
+ metaType,
617
+ metaId,
618
+ policy
619
+ }: {
620
+ meta: any
621
+ metaType: string
622
+ metaId: string
623
+ policy?: any
624
+ }): Molecule {
625
+ if (!this.sourceWallet) {
626
+ throw new Error('Source wallet required for meta creation')
627
+ }
628
+
629
+ const metaArray: Array<{ key: string; value: any }> = []
630
+
631
+ // Convert meta to array format
632
+ if (meta) {
633
+ if (Array.isArray(meta)) {
634
+ metaArray.push(...meta)
635
+ } else if (typeof meta === 'object') {
636
+ for (const [key, value] of Object.entries(meta)) {
637
+ metaArray.push({ key, value })
638
+ }
639
+ } else {
640
+ metaArray.push({ key: 'value', value: meta })
641
+ }
642
+ }
643
+
644
+ // Add policy if provided
645
+ if (policy) {
646
+ metaArray.push({ key: 'policy', value: JSON.stringify(policy) })
647
+ }
648
+
649
+ // Create meta atom
650
+ this.addAtom(new Atom({
651
+ isotope: 'M',
652
+ position: this.sourceWallet.position!,
653
+ walletAddress: this.sourceWallet.address! as any,
654
+ token: this.sourceWallet.token,
655
+ metaType,
656
+ metaId,
657
+ meta: metaArray
658
+ }))
659
+
660
+ // Add ContinuID atom (matching JavaScript SDK)
661
+ this.addContinuIdAtom()
662
+
663
+ return this
664
+ }
665
+
666
+ /**
667
+ * Initialize wallet creation
668
+ */
669
+ initWalletCreation(wallet: Wallet, atomMeta: AtomMeta | null = null): Molecule {
670
+ // Create wallet creation atom
671
+ const atom = new Atom({
672
+ isotope: 'C',
673
+ position: wallet.position!,
674
+ walletAddress: wallet.address! as any,
675
+ token: wallet.token,
676
+ metaType: 'wallet',
677
+ metaId: wallet.address!,
678
+ meta: atomMeta ? atomMeta.get() as any : []
679
+ })
680
+
681
+ // Add wallet metadata
682
+ if (!atomMeta) {
683
+ atom.meta.push(
684
+ { key: 'position', value: wallet.position! },
685
+ { key: 'bundle', value: wallet.bundle! },
686
+ { key: 'token', value: wallet.token },
687
+ { key: 'batchId', value: wallet.batchId || '' },
688
+ { key: 'characters', value: wallet.characters || '' }
689
+ )
690
+
691
+ if (wallet.pubkey) {
692
+ atom.meta.push({ key: 'pubkey', value: wallet.pubkey })
693
+ }
694
+ }
695
+
696
+ this.addAtom(atom)
697
+
698
+ // Add ContinuID atom (matching JavaScript SDK)
699
+ this.addContinuIdAtom()
700
+
701
+ return this
702
+ }
703
+
704
+ /**
705
+ * Creates a Molecule instance from JSON data (2025 TS best practices)
706
+ *
707
+ * Handles cross-SDK deserialization with robust error handling and validation.
708
+ * Essential for cross-platform molecule validation and compatibility testing.
709
+ *
710
+ * @param json - JSON string or object to deserialize
711
+ * @param options - Deserialization options
712
+ * @param options.includeValidationContext - Reconstruct sourceWallet/remainderWallet (default: true)
713
+ * @param options.validateStructure - Validate required fields (default: true)
714
+ * @param options.strictMode - Strict validation mode (default: false)
715
+ * @return Reconstructed molecule instance
716
+ * @throws Error if JSON is invalid or required fields are missing
717
+ */
718
+ static fromJSON(json: string | any, options: {
719
+ includeValidationContext?: boolean
720
+ validateStructure?: boolean
721
+ strictMode?: boolean
722
+ } = {}): Molecule {
723
+ const {
724
+ includeValidationContext = true,
725
+ validateStructure = true,
726
+ strictMode = false
727
+ } = options
728
+
729
+ try {
730
+ // Parse JSON safely
731
+ const data = typeof json === 'string' ? JSON.parse(json) : json
732
+
733
+ // Validate required fields in strict mode
734
+ if (strictMode || validateStructure) {
735
+ if (!data.molecularHash || !Array.isArray(data.atoms)) {
736
+ throw new Error('Invalid molecule data: missing molecularHash or atoms array')
737
+ }
738
+ }
739
+
740
+ // Create minimal molecule instance (never include secret from JSON)
741
+ const molecule = new Molecule({
742
+ secret: null,
743
+ bundle: data.bundle || null,
744
+ cellSlug: data.cellSlug || null,
745
+ version: data.version || null
746
+ })
747
+
748
+ // Populate core properties
749
+ molecule.status = data.status
750
+ molecule.molecularHash = data.molecularHash
751
+ molecule.createdAt = data.createdAt || String(+new Date())
752
+ molecule.cellSlugOrigin = data.cellSlugOrigin
753
+
754
+ // Reconstruct atoms array with proper Atom instances
755
+ if (Array.isArray(data.atoms)) {
756
+ molecule.atoms = data.atoms.map((atomData: any, index: number) => {
757
+ try {
758
+ return Atom.fromJSON(atomData)
759
+ } catch (error) {
760
+ throw new Error(`Failed to reconstruct atom ${index}: ${(error as Error).message}`)
761
+ }
762
+ })
763
+ }
764
+
765
+ // Reconstruct validation context if available and requested
766
+ if (includeValidationContext) {
767
+ if (data.sourceWallet) {
768
+ // Create source wallet for validation (without secret for security)
769
+ molecule.sourceWallet = new Wallet({
770
+ secret: null,
771
+ token: data.sourceWallet.token,
772
+ position: data.sourceWallet.position,
773
+ bundle: data.sourceWallet.bundle,
774
+ batchId: data.sourceWallet.batchId,
775
+ characters: data.sourceWallet.characters
776
+ })
777
+
778
+ // Set additional properties for validation context
779
+ molecule.sourceWallet.balance = data.sourceWallet.balance || 0
780
+ molecule.sourceWallet.address = data.sourceWallet.address as any
781
+ if (data.sourceWallet.pubkey) {
782
+ molecule.sourceWallet.pubkey = data.sourceWallet.pubkey
783
+ }
784
+ molecule.sourceWallet.tokenUnits = data.sourceWallet.tokenUnits || []
785
+ molecule.sourceWallet.tradeRates = data.sourceWallet.tradeRates || {}
786
+ molecule.sourceWallet.molecules = data.sourceWallet.molecules || {}
787
+ }
788
+
789
+ if (data.remainderWallet) {
790
+ // Create remainder wallet for validation (without secret for security)
791
+ molecule.remainderWallet = new Wallet({
792
+ secret: null,
793
+ token: data.remainderWallet.token,
794
+ position: data.remainderWallet.position,
795
+ bundle: data.remainderWallet.bundle,
796
+ batchId: data.remainderWallet.batchId,
797
+ characters: data.remainderWallet.characters
798
+ })
799
+
800
+ // Set additional properties for validation context
801
+ molecule.remainderWallet.balance = data.remainderWallet.balance || 0
802
+ molecule.remainderWallet.address = data.remainderWallet.address as any
803
+ if (data.remainderWallet.pubkey) {
804
+ molecule.remainderWallet.pubkey = data.remainderWallet.pubkey
805
+ }
806
+ molecule.remainderWallet.tokenUnits = data.remainderWallet.tokenUnits || []
807
+ molecule.remainderWallet.tradeRates = data.remainderWallet.tradeRates || {}
808
+ molecule.remainderWallet.molecules = data.remainderWallet.molecules || {}
809
+ }
810
+ }
811
+
812
+ return molecule
813
+
814
+ } catch (error) {
815
+ throw new Error(`Molecule deserialization failed: ${(error as Error).message}`)
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Convert JSON to Molecule instance - legacy method for compatibility
821
+ */
822
+ static jsonToObject(json: string): Molecule {
823
+ return Molecule.fromJSON(json)
824
+ }
825
+ }