@wishknish/knishio-client-js 0.8.1 → 0.9.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 (35) hide show
  1. package/dist/client.cjs.js +29 -102
  2. package/dist/client.cjs.js.map +1 -1
  3. package/dist/client.es.mjs +823 -766
  4. package/dist/client.es.mjs.map +1 -1
  5. package/dist/client.iife.js +29 -102
  6. package/dist/client.iife.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/Atom.js +18 -20
  9. package/src/KnishIOClient.js +115 -49
  10. package/src/Molecule.js +78 -68
  11. package/src/Wallet.js +89 -6
  12. package/src/index.js +0 -4
  13. package/src/instance/Rules/Callback.js +1 -0
  14. package/src/instance/Rules/Meta.js +1 -3
  15. package/src/instance/Rules/Rule.js +1 -0
  16. package/src/libraries/strings.js +1 -0
  17. package/src/libraries/urql/UrqlClientWrapper.js +129 -10
  18. package/src/mutation/MutationTransferTokens.js +18 -0
  19. package/src/query/QueryPolicy.js +1 -1
  20. package/src/response/Response.js +3 -3
  21. package/src/response/ResponseActiveSession.js +0 -1
  22. package/src/response/ResponseAtom.js +0 -1
  23. package/src/response/ResponseAuthorizationGuest.js +0 -1
  24. package/src/response/ResponseBalance.js +0 -1
  25. package/src/response/ResponseContinuId.js +0 -1
  26. package/src/response/ResponseLinkIdentifier.js +0 -1
  27. package/src/response/ResponseMetaType.js +0 -1
  28. package/src/response/ResponseMetaTypeViaAtom.js +0 -1
  29. package/src/response/ResponseMetaTypeViaMolecule.js +0 -1
  30. package/src/response/ResponsePolicy.js +0 -1
  31. package/src/response/ResponseQueryActiveSession.js +0 -1
  32. package/src/response/ResponseWalletBundle.js +0 -1
  33. package/src/response/ResponseWalletList.js +0 -1
  34. package/src/query/QueryUserActivity.js +0 -151
  35. package/src/response/ResponseQueryUserActivity.js +0 -80
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishknish/knishio-client-js",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "productName": "Knish.IO Javascript SDK Client",
6
6
  "description": "JavaScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs.",
package/src/Atom.js CHANGED
@@ -212,7 +212,7 @@ export default class Atom {
212
212
 
213
213
  /**
214
214
  * Returns JSON-ready object for cross-SDK compatibility (2025 JS best practices)
215
- *
215
+ *
216
216
  * Provides clean serialization of atomic operations with optional OTS fragments.
217
217
  * Follows 2025 JavaScript best practices with proper type safety and validation.
218
218
  *
@@ -226,15 +226,15 @@ export default class Atom {
226
226
  const {
227
227
  includeOtsFragments = true,
228
228
  validateFields = false
229
- } = options;
229
+ } = options
230
230
 
231
231
  try {
232
232
  // Validate required fields if requested
233
233
  if (validateFields) {
234
- const requiredFields = ['position', 'walletAddress', 'isotope', 'token'];
234
+ const requiredFields = ['position', 'walletAddress', 'isotope', 'token']
235
235
  for (const field of requiredFields) {
236
236
  if (!this[field]) {
237
- throw new Error(`Required field '${field}' is missing or empty`);
237
+ throw new Error(`Required field '${field}' is missing or empty`)
238
238
  }
239
239
  }
240
240
  }
@@ -258,23 +258,22 @@ export default class Atom {
258
258
  index: this.index,
259
259
  createdAt: this.createdAt,
260
260
  version: this.version
261
- };
261
+ }
262
262
 
263
263
  // Optional OTS fragments (can be large, so optional)
264
264
  if (includeOtsFragments && this.otsFragment) {
265
- serialized.otsFragment = this.otsFragment;
265
+ serialized.otsFragment = this.otsFragment
266
266
  }
267
267
 
268
- return serialized;
269
-
268
+ return serialized
270
269
  } catch (error) {
271
- throw new Error(`Atom serialization failed: ${error.message}`);
270
+ throw new Error(`Atom serialization failed: ${error.message}`)
272
271
  }
273
272
  }
274
273
 
275
274
  /**
276
275
  * Creates an Atom instance from JSON data (2025 JS best practices)
277
- *
276
+ *
278
277
  * Handles cross-SDK atom deserialization with robust error handling.
279
278
  * Essential for reconstructing atoms from other SDK implementations.
280
279
  *
@@ -289,18 +288,18 @@ export default class Atom {
289
288
  const {
290
289
  validateStructure = true,
291
290
  strictMode = false
292
- } = options;
291
+ } = options
293
292
 
294
293
  try {
295
294
  // Parse JSON safely
296
- const data = typeof json === 'string' ? JSON.parse(json) : json;
295
+ const data = typeof json === 'string' ? JSON.parse(json) : json
297
296
 
298
297
  // Validate required fields in strict mode
299
298
  if (strictMode || validateStructure) {
300
- const requiredFields = ['position', 'walletAddress', 'isotope', 'token'];
299
+ const requiredFields = ['position', 'walletAddress', 'isotope', 'token']
301
300
  for (const field of requiredFields) {
302
301
  if (!data[field]) {
303
- throw new Error(`Required field '${field}' is missing or empty`);
302
+ throw new Error(`Required field '${field}' is missing or empty`)
304
303
  }
305
304
  }
306
305
  }
@@ -318,20 +317,19 @@ export default class Atom {
318
317
  meta: data.meta,
319
318
  index: data.index,
320
319
  version: data.version
321
- });
320
+ })
322
321
 
323
322
  // Set additional properties that may not be in constructor
324
323
  if (data.otsFragment) {
325
- atom.otsFragment = data.otsFragment;
324
+ atom.otsFragment = data.otsFragment
326
325
  }
327
326
  if (data.createdAt) {
328
- atom.createdAt = data.createdAt;
327
+ atom.createdAt = data.createdAt
329
328
  }
330
329
 
331
- return atom;
332
-
330
+ return atom
333
331
  } catch (error) {
334
- throw new Error(`Atom deserialization failed: ${error.message}`);
332
+ throw new Error(`Atom deserialization failed: ${error.message}`)
335
333
  }
336
334
  }
337
335
 
@@ -88,7 +88,6 @@ import ActiveWalletSubscribe from './subscribe/ActiveWalletSubscribe.js'
88
88
  import ActiveSessionSubscribe from './subscribe/ActiveSessionSubscribe.js'
89
89
  import MutationActiveSession from './mutation/MutationActiveSession.js'
90
90
  import QueryActiveSession from './query/QueryActiveSession.js'
91
- import QueryUserActivity from './query/QueryUserActivity.js'
92
91
  import QueryToken from './query/QueryToken.js'
93
92
  import BatchIdException from './exception/BatchIdException.js'
94
93
  import AuthorizationRejectedException from './exception/AuthorizationRejectedException.js'
@@ -582,15 +581,15 @@ export default class KnishIOClient {
582
581
  * @param molecule
583
582
  */
584
583
  async createMoleculeMutation ({
585
- mutationClass,
584
+ mutationClass: MutationClass,
586
585
  molecule = null
587
586
  }) {
588
- this.log('info', `KnishIOClient::createMoleculeQuery() - Creating a new ${ mutationClass.name } query...`)
587
+ this.log('info', `KnishIOClient::createMoleculeQuery() - Creating a new ${ MutationClass.name } query...`)
589
588
 
590
589
  // If you don't supply the molecule, we'll generate one for you
591
590
  const _molecule = molecule || await this.createMolecule({})
592
591
 
593
- const mutation = new mutationClass(this.client(), this, _molecule)
592
+ const mutation = new MutationClass(this.client(), this, _molecule)
594
593
 
595
594
  if (!(mutation instanceof MutationProposeMolecule)) {
596
595
  throw new CodeException(`${ this.constructor.name }::createMoleculeMutation() - This method only accepts MutationProposeMolecule!`)
@@ -1216,50 +1215,6 @@ export default class KnishIOClient {
1216
1215
  })
1217
1216
  }
1218
1217
 
1219
- /**
1220
- * Queries user activity based on the provided parameters.
1221
- *
1222
- * @param {string} bundleHash - The bundle hash.
1223
- * @param {string} metaType - The meta type.
1224
- * @param {string} metaId - The meta ID.
1225
- * @param {string} ipAddress - The IP address.
1226
- * @param {string} browser - The browser.
1227
- * @param {string} osCpu - The operating system and CPU.
1228
- * @param {string} resolution - The screen resolution.
1229
- * @param {string} timeZone - The time zone.
1230
- * @param {string} countBy - The count by parameter.
1231
- * @param {string} interval - The interval parameter.
1232
- *
1233
- * @returns {Promise<ResponseQueryUserActivity>} The result of the query.
1234
- */
1235
- async queryUserActivity ({
1236
- bundleHash,
1237
- metaType,
1238
- metaId,
1239
- ipAddress,
1240
- browser,
1241
- osCpu,
1242
- resolution,
1243
- timeZone,
1244
- countBy,
1245
- interval
1246
- }) {
1247
- const query = this.createQuery(QueryUserActivity)
1248
-
1249
- return await this.executeQuery(query, {
1250
- bundleHash,
1251
- metaType,
1252
- metaId,
1253
- ipAddress,
1254
- browser,
1255
- osCpu,
1256
- resolution,
1257
- timeZone,
1258
- countBy,
1259
- interval
1260
- })
1261
- }
1262
-
1263
1218
  /**
1264
1219
  * Builds and executes a molecule to declare an active session for the given MetaType
1265
1220
  *
@@ -1966,6 +1921,109 @@ export default class KnishIOClient {
1966
1921
  return await this.executeQuery(query)
1967
1922
  }
1968
1923
 
1924
+ /**
1925
+ * Transfers tokens from one source wallet to MULTIPLE recipients in a single molecule.
1926
+ *
1927
+ * One molecule funds N recipients (WP line 544: "utilizing the sender's Wallet to fund multiple
1928
+ * recipients with one transaction"). Each recipient receives its own amount and, for stackable
1929
+ * tokens, its own subset of token units. The source is fully drained and the unsent change
1930
+ * returns via the remainder atom (UTXO), so the V-atoms conserve. Single-recipient transfers
1931
+ * should keep using transferToken(); this is its N-recipient generalization.
1932
+ *
1933
+ * @param {Object} options - The transfer options.
1934
+ * @param {string} options.token - The token to transfer.
1935
+ * @param {Array} options.recipients - [{ bundleHash, amount?, units?, batchId? }] per recipient.
1936
+ * @param {Object} [options.sourceWallet=null] - The source wallet; queried if not provided.
1937
+ *
1938
+ * @returns {Promise} - A Promise that resolves to the transaction result.
1939
+ *
1940
+ * @throws {StackableUnitAmountException} - If a recipient provides both amount and units.
1941
+ * @throws {TransferBalanceException} - If the source wallet does not have enough balance.
1942
+ */
1943
+ async transferTokens ({
1944
+ token,
1945
+ recipients,
1946
+ sourceWallet = null
1947
+ }) {
1948
+ // Per-recipient amount: units.length for stackable, else the explicit amount
1949
+ const amounts = recipients.map(recipient => {
1950
+ const units = recipient.units || []
1951
+ if (units.length > 0) {
1952
+ // Can't move stackable units AND provide amount
1953
+ if (recipient.amount > 0) {
1954
+ throw new StackableUnitAmountException()
1955
+ }
1956
+ return units.length
1957
+ }
1958
+ return recipient.amount || 0
1959
+ })
1960
+ const total = amounts.reduce((sum, value) => sum + value, 0)
1961
+
1962
+ // Get a source wallet (loads its token units)
1963
+ if (sourceWallet === null) {
1964
+ sourceWallet = await this.querySourceWallet({
1965
+ token,
1966
+ amount: total
1967
+ })
1968
+ }
1969
+
1970
+ // Do you have enough tokens?
1971
+ if (sourceWallet === null || Decimal.cmp(sourceWallet.balance, total) < 0) {
1972
+ throw new TransferBalanceException()
1973
+ }
1974
+
1975
+ // Build a shadow recipient wallet per recipient, each with its own batch ID
1976
+ const recipientWallets = recipients.map(recipient => {
1977
+ const recipientWallet = Wallet.create({
1978
+ bundle: recipient.bundleHash,
1979
+ token
1980
+ })
1981
+
1982
+ // Compute the batch ID for the recipient (typically used by stackable tokens)
1983
+ if (recipient.batchId !== undefined && recipient.batchId !== null) {
1984
+ recipientWallet.batchId = recipient.batchId
1985
+ } else {
1986
+ recipientWallet.initBatchId({
1987
+ sourceWallet
1988
+ })
1989
+ }
1990
+
1991
+ return recipientWallet
1992
+ })
1993
+
1994
+ // Create a remainder from the source wallet
1995
+ const remainderWallet = sourceWallet.createRemainder(this.getSecret())
1996
+
1997
+ // --- Token units splitting (N-way): source keeps the union, each recipient its subset,
1998
+ // remainder the kept units
1999
+ sourceWallet.splitUnitsMulti(
2000
+ recipients.map(recipient => recipient.units || []),
2001
+ recipientWallets,
2002
+ remainderWallet
2003
+ )
2004
+ // ---
2005
+
2006
+ // Build the molecule itself
2007
+ const molecule = await this.createMolecule({
2008
+ sourceWallet,
2009
+ remainderWallet
2010
+ })
2011
+ /**
2012
+ * @type {MutationTransferTokens}
2013
+ */
2014
+ const query = await this.createMoleculeMutation({
2015
+ mutationClass: MutationTransferTokens,
2016
+ molecule
2017
+ })
2018
+
2019
+ query.fillMoleculeMulti({
2020
+ recipientWallets,
2021
+ amounts
2022
+ })
2023
+
2024
+ return await this.executeQuery(query)
2025
+ }
2026
+
1969
2027
  /**
1970
2028
  * Deposits buffer token into the source wallet.
1971
2029
  *
@@ -2343,7 +2401,15 @@ export default class KnishIOClient {
2343
2401
  molecule
2344
2402
  })
2345
2403
 
2346
- query.fillMolecule({ meta: { encrypt: (encrypt ? 'true' : 'false') } })
2404
+ // PQ-transport Phase E (cycle 163): convey the AUTH source wallet's ML-KEM768 public key as a
2405
+ // SIGNED `walletPubkey` meta on the U-atom (fillMolecule → initAuthorization → sign), so the
2406
+ // validator can encrypt CipherHash responses back to THIS wallet (the one that decrypts them).
2407
+ // Signed → tamper-proof. Only when present (PQ-capable wallet).
2408
+ const authMeta = { encrypt: (encrypt ? 'true' : 'false') }
2409
+ if (wallet.pubkey) {
2410
+ authMeta.walletPubkey = wallet.pubkey
2411
+ }
2412
+ query.fillMolecule({ meta: authMeta })
2347
2413
  /**
2348
2414
  * @type {ResponseRequestAuthorization}
2349
2415
  */
package/src/Molecule.js CHANGED
@@ -264,7 +264,13 @@ export default class Molecule {
264
264
  const condition = totalCondition ? mappedHashArray[index] < 8 : mappedHashArray[index] > -8
265
265
 
266
266
  if (condition) {
267
- const process = totalCondition ? [++mappedHashArray[index], ++total] : [--mappedHashArray[index], --total]
267
+ if (totalCondition) {
268
+ ++mappedHashArray[index]
269
+ ++total
270
+ } else {
271
+ --mappedHashArray[index]
272
+ --total
273
+ }
268
274
 
269
275
  if (total === 0) {
270
276
  break
@@ -571,6 +577,11 @@ export default class Molecule {
571
577
  recipientWallet,
572
578
  amount
573
579
  }) {
580
+ // Canonical token transfer (incl. stackable): a UTXO-style FULL-balance debit + remainder.
581
+ // The source wallet is fully drained (-balance) and the change returns via the remainder atom,
582
+ // so the 3 V-atoms conserve (sum == 0) and the validator's stackable full-drain guard accepts.
583
+ // Per-unit (stackable) movement is layered by calling Wallet.splitUnits(units, remainder,
584
+ // recipient) BEFORE this — there is no separate partial-debit transfer mode.
574
585
  if (this.sourceWallet.balance - amount < 0) {
575
586
  throw new BalanceInsufficientException()
576
587
  }
@@ -602,54 +613,55 @@ export default class Molecule {
602
613
  }
603
614
 
604
615
  /**
605
- * Creates a stackable V-isotope transfer with 3 atoms:
606
- * source debit, recipient credit, remainder.
607
- * Propagates batchId from source wallet.
616
+ * Multi-recipient value transfer (WP line 544: one molecule funds N recipients).
617
+ *
618
+ * Builds 1 source atom (full-balance debit, carrying the SENT union of all units) + one atom
619
+ * per recipient (its amount + its own units, walletBundle-bonded) + 1 remainder atom (the KEPT
620
+ * change). Conservation: -balance + Σ amounts + (balance - Σ amounts) == 0.
608
621
  *
609
- * @param {Wallet} recipientWallet - wallet receiving the tokens
610
- * @param {number} amount - amount to transfer
622
+ * Per-unit (stackable) routing is layered by calling Wallet.splitUnitsMulti(...) BEFORE this —
623
+ * it sets the source's tokenUnits to the union, each recipient's to its subset, and the
624
+ * remainder's to the kept units. The single-recipient initValue() is the N=1 special case.
625
+ *
626
+ * @param {Wallet[]} recipientWallets
627
+ * @param {number[]} amounts - parallel to recipientWallets
611
628
  * @return {Molecule}
612
629
  */
613
- addStackableTransfer ({
614
- recipientWallet,
615
- amount
630
+ initValues ({
631
+ recipientWallets,
632
+ amounts
616
633
  }) {
617
- if (amount <= 0) {
618
- throw new NegativeAmountException('Molecule::addStackableTransfer() - Amount must be positive!')
619
- }
634
+ const total = amounts.reduce((sum, value) => sum + Number(value), 0)
620
635
 
621
- if (this.sourceWallet.balance - amount < 0) {
636
+ if (this.sourceWallet.balance - total < 0) {
622
637
  throw new BalanceInsufficientException()
623
638
  }
624
639
 
625
- const batchId = this.sourceWallet.batchId || generateBatchId({})
626
-
627
- // Source debit atom
640
+ // Source atom: debit the full balance (UTXO); carries the SENT union of all units
628
641
  this.addAtom(Atom.create({
629
642
  isotope: 'V',
630
643
  wallet: this.sourceWallet,
631
- value: -amount,
632
- batchId
644
+ value: -this.sourceWallet.balance
633
645
  }))
634
646
 
635
- // Recipient credit atom
636
- this.addAtom(Atom.create({
637
- isotope: 'V',
638
- wallet: recipientWallet,
639
- value: amount,
640
- metaType: 'walletBundle',
641
- metaId: recipientWallet.bundle,
642
- batchId: generateBatchId({})
643
- }))
647
+ // One atom per recipient: its amount + its own units, bonded to the recipient bundle
648
+ recipientWallets.forEach((recipientWallet, index) => {
649
+ this.addAtom(Atom.create({
650
+ isotope: 'V',
651
+ wallet: recipientWallet,
652
+ value: amounts[index],
653
+ metaType: 'walletBundle',
654
+ metaId: recipientWallet.bundle
655
+ }))
656
+ })
644
657
 
645
- // Remainder atom
658
+ // Remainder atom: the change back to the sender; carries the KEPT units
646
659
  this.addAtom(Atom.create({
647
660
  isotope: 'V',
648
661
  wallet: this.remainderWallet,
649
- value: this.sourceWallet.balance - amount,
662
+ value: this.sourceWallet.balance - total,
650
663
  metaType: 'walletBundle',
651
- metaId: this.remainderWallet.bundle,
652
- batchId
664
+ metaId: this.remainderWallet.bundle
653
665
  }))
654
666
 
655
667
  return this
@@ -716,7 +728,7 @@ export default class Molecule {
716
728
  }) {
717
729
  // Calculate final amount from all recipients
718
730
  let amount = 0
719
- for (const [recipientBundle, recipientAmount] of Object.entries(recipients || {})) {
731
+ for (const recipientAmount of Object.values(recipients || {})) {
720
732
  amount += recipientAmount
721
733
  }
722
734
  if (this.sourceWallet.balance - amount < 0) {
@@ -1184,7 +1196,7 @@ export default class Molecule {
1184
1196
  const {
1185
1197
  includeValidationContext = false,
1186
1198
  includeOtsFragments = true
1187
- } = options;
1199
+ } = options
1188
1200
 
1189
1201
  try {
1190
1202
  // Core molecule properties (server-compatible fields only)
@@ -1199,7 +1211,7 @@ export default class Molecule {
1199
1211
  atoms: this.atoms.map(atom => atom.toJSON({
1200
1212
  includeOtsFragments
1201
1213
  }))
1202
- };
1214
+ }
1203
1215
 
1204
1216
  // Parent molecular hashes for DAG linkage (only include when non-empty
1205
1217
  // to maintain backward compatibility with servers that don't support it)
@@ -1225,7 +1237,7 @@ export default class Molecule {
1225
1237
  tokenUnits: this.sourceWallet.tokenUnits || [],
1226
1238
  tradeRates: this.sourceWallet.tradeRates || {},
1227
1239
  molecules: this.sourceWallet.molecules || {}
1228
- };
1240
+ }
1229
1241
  }
1230
1242
 
1231
1243
  if (this.remainderWallet) {
@@ -1241,14 +1253,13 @@ export default class Molecule {
1241
1253
  tokenUnits: this.remainderWallet.tokenUnits || [],
1242
1254
  tradeRates: this.remainderWallet.tradeRates || {},
1243
1255
  molecules: this.remainderWallet.molecules || {}
1244
- };
1256
+ }
1245
1257
  }
1246
1258
  }
1247
1259
 
1248
- return serialized;
1249
-
1260
+ return serialized
1250
1261
  } catch (error) {
1251
- throw new Error(`Molecule serialization failed: ${error.message}`);
1262
+ throw new Error(`Molecule serialization failed: ${error.message}`)
1252
1263
  }
1253
1264
  }
1254
1265
 
@@ -1269,16 +1280,16 @@ export default class Molecule {
1269
1280
  const {
1270
1281
  includeValidationContext = false,
1271
1282
  validateStructure = true
1272
- } = options;
1283
+ } = options
1273
1284
 
1274
1285
  try {
1275
1286
  // Parse JSON safely
1276
- const data = typeof json === 'string' ? JSON.parse(json) : json;
1287
+ const data = typeof json === 'string' ? JSON.parse(json) : json
1277
1288
 
1278
1289
  // Validate required fields in strict mode
1279
1290
  if (validateStructure) {
1280
1291
  if (!data.molecularHash || !Array.isArray(data.atoms)) {
1281
- throw new Error('Invalid molecule data: missing molecularHash or atoms array');
1292
+ throw new Error('Invalid molecule data: missing molecularHash or atoms array')
1282
1293
  }
1283
1294
  }
1284
1295
 
@@ -1288,24 +1299,24 @@ export default class Molecule {
1288
1299
  bundle: data.bundle || null,
1289
1300
  cellSlug: data.cellSlug || null,
1290
1301
  version: data.version || null
1291
- });
1302
+ })
1292
1303
 
1293
1304
  // Populate core properties
1294
- molecule.status = data.status;
1295
- molecule.molecularHash = data.molecularHash;
1296
- molecule.createdAt = data.createdAt || String(+new Date());
1297
- molecule.cellSlugOrigin = data.cellSlugOrigin;
1298
- molecule.parentHashes = Array.isArray(data.parentHashes) ? [...data.parentHashes] : [];
1305
+ molecule.status = data.status
1306
+ molecule.molecularHash = data.molecularHash
1307
+ molecule.createdAt = data.createdAt || String(+new Date())
1308
+ molecule.cellSlugOrigin = data.cellSlugOrigin
1309
+ molecule.parentHashes = Array.isArray(data.parentHashes) ? [...data.parentHashes] : []
1299
1310
 
1300
1311
  // Reconstruct atoms array with proper Atom instances
1301
1312
  if (Array.isArray(data.atoms)) {
1302
1313
  molecule.atoms = data.atoms.map((atomData, index) => {
1303
1314
  try {
1304
- return Atom.fromJSON(atomData);
1315
+ return Atom.fromJSON(atomData)
1305
1316
  } catch (error) {
1306
- throw new Error(`Failed to reconstruct atom ${index}: ${error.message}`);
1317
+ throw new Error(`Failed to reconstruct atom ${index}: ${error.message}`)
1307
1318
  }
1308
- });
1319
+ })
1309
1320
  }
1310
1321
 
1311
1322
  // Reconstruct validation context if available and requested
@@ -1319,17 +1330,17 @@ export default class Molecule {
1319
1330
  bundle: data.sourceWallet.bundle,
1320
1331
  batchId: data.sourceWallet.batchId,
1321
1332
  characters: data.sourceWallet.characters
1322
- });
1333
+ })
1323
1334
 
1324
1335
  // Set additional properties for validation context
1325
- molecule.sourceWallet.balance = String(data.sourceWallet.balance != null ? data.sourceWallet.balance : 0);
1326
- molecule.sourceWallet.address = data.sourceWallet.address;
1336
+ molecule.sourceWallet.balance = String(data.sourceWallet.balance != null ? data.sourceWallet.balance : 0)
1337
+ molecule.sourceWallet.address = data.sourceWallet.address
1327
1338
  if (data.sourceWallet.pubkey) {
1328
- molecule.sourceWallet.pubkey = data.sourceWallet.pubkey;
1339
+ molecule.sourceWallet.pubkey = data.sourceWallet.pubkey
1329
1340
  }
1330
- molecule.sourceWallet.tokenUnits = data.sourceWallet.tokenUnits || [];
1331
- molecule.sourceWallet.tradeRates = data.sourceWallet.tradeRates || {};
1332
- molecule.sourceWallet.molecules = data.sourceWallet.molecules || {};
1341
+ molecule.sourceWallet.tokenUnits = data.sourceWallet.tokenUnits || []
1342
+ molecule.sourceWallet.tradeRates = data.sourceWallet.tradeRates || {}
1343
+ molecule.sourceWallet.molecules = data.sourceWallet.molecules || {}
1333
1344
  }
1334
1345
 
1335
1346
  if (data.remainderWallet) {
@@ -1341,24 +1352,23 @@ export default class Molecule {
1341
1352
  bundle: data.remainderWallet.bundle,
1342
1353
  batchId: data.remainderWallet.batchId,
1343
1354
  characters: data.remainderWallet.characters
1344
- });
1355
+ })
1345
1356
 
1346
1357
  // Set additional properties for validation context
1347
- molecule.remainderWallet.balance = String(data.remainderWallet.balance != null ? data.remainderWallet.balance : 0);
1348
- molecule.remainderWallet.address = data.remainderWallet.address;
1358
+ molecule.remainderWallet.balance = String(data.remainderWallet.balance != null ? data.remainderWallet.balance : 0)
1359
+ molecule.remainderWallet.address = data.remainderWallet.address
1349
1360
  if (data.remainderWallet.pubkey) {
1350
- molecule.remainderWallet.pubkey = data.remainderWallet.pubkey;
1361
+ molecule.remainderWallet.pubkey = data.remainderWallet.pubkey
1351
1362
  }
1352
- molecule.remainderWallet.tokenUnits = data.remainderWallet.tokenUnits || [];
1353
- molecule.remainderWallet.tradeRates = data.remainderWallet.tradeRates || {};
1354
- molecule.remainderWallet.molecules = data.remainderWallet.molecules || {};
1363
+ molecule.remainderWallet.tokenUnits = data.remainderWallet.tokenUnits || []
1364
+ molecule.remainderWallet.tradeRates = data.remainderWallet.tradeRates || {}
1365
+ molecule.remainderWallet.molecules = data.remainderWallet.molecules || {}
1355
1366
  }
1356
1367
  }
1357
1368
 
1358
- return molecule;
1359
-
1369
+ return molecule
1360
1370
  } catch (error) {
1361
- throw new Error(`Molecule deserialization failed: ${error.message}`);
1371
+ throw new Error(`Molecule deserialization failed: ${error.message}`)
1362
1372
  }
1363
1373
  }
1364
1374