@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.
- package/dist/client.cjs.js +29 -102
- package/dist/client.cjs.js.map +1 -1
- package/dist/client.es.mjs +823 -766
- package/dist/client.es.mjs.map +1 -1
- package/dist/client.iife.js +29 -102
- package/dist/client.iife.js.map +1 -1
- package/package.json +1 -1
- package/src/Atom.js +18 -20
- package/src/KnishIOClient.js +115 -49
- package/src/Molecule.js +78 -68
- package/src/Wallet.js +89 -6
- package/src/index.js +0 -4
- package/src/instance/Rules/Callback.js +1 -0
- package/src/instance/Rules/Meta.js +1 -3
- package/src/instance/Rules/Rule.js +1 -0
- package/src/libraries/strings.js +1 -0
- package/src/libraries/urql/UrqlClientWrapper.js +129 -10
- package/src/mutation/MutationTransferTokens.js +18 -0
- package/src/query/QueryPolicy.js +1 -1
- package/src/response/Response.js +3 -3
- package/src/response/ResponseActiveSession.js +0 -1
- package/src/response/ResponseAtom.js +0 -1
- package/src/response/ResponseAuthorizationGuest.js +0 -1
- package/src/response/ResponseBalance.js +0 -1
- package/src/response/ResponseContinuId.js +0 -1
- package/src/response/ResponseLinkIdentifier.js +0 -1
- package/src/response/ResponseMetaType.js +0 -1
- package/src/response/ResponseMetaTypeViaAtom.js +0 -1
- package/src/response/ResponseMetaTypeViaMolecule.js +0 -1
- package/src/response/ResponsePolicy.js +0 -1
- package/src/response/ResponseQueryActiveSession.js +0 -1
- package/src/response/ResponseWalletBundle.js +0 -1
- package/src/response/ResponseWalletList.js +0 -1
- package/src/query/QueryUserActivity.js +0 -151
- package/src/response/ResponseQueryUserActivity.js +0 -80
package/package.json
CHANGED
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
|
|
package/src/KnishIOClient.js
CHANGED
|
@@ -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 ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
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
|
-
*
|
|
610
|
-
*
|
|
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
|
-
|
|
614
|
-
|
|
615
|
-
|
|
630
|
+
initValues ({
|
|
631
|
+
recipientWallets,
|
|
632
|
+
amounts
|
|
616
633
|
}) {
|
|
617
|
-
|
|
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 -
|
|
636
|
+
if (this.sourceWallet.balance - total < 0) {
|
|
622
637
|
throw new BalanceInsufficientException()
|
|
623
638
|
}
|
|
624
639
|
|
|
625
|
-
|
|
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: -
|
|
632
|
-
batchId
|
|
644
|
+
value: -this.sourceWallet.balance
|
|
633
645
|
}))
|
|
634
646
|
|
|
635
|
-
//
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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 -
|
|
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
|
|
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
|
|