@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.
- package/LICENSE +674 -0
- package/README.md +425 -0
- package/dist/index.cjs +9961 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.iife.js +32241 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.js +9853 -0
- package/dist/index.js.map +1 -0
- package/package.json +113 -0
- package/src/AuthToken.ts +214 -0
- package/src/KnishIOClient.ts +2020 -0
- package/src/constants.ts +398 -0
- package/src/core/Atom.ts +646 -0
- package/src/core/AtomMeta.ts +278 -0
- package/src/core/Meta.ts +428 -0
- package/src/core/Molecule.ts +825 -0
- package/src/core/PolicyMeta.ts +130 -0
- package/src/core/TokenUnit.ts +148 -0
- package/src/core/Wallet.ts +467 -0
- package/src/exception/AtomIndexException.ts +97 -0
- package/src/exception/AtomsMissingException.ts +109 -0
- package/src/exception/AuthorizationRejectedException.ts +63 -0
- package/src/exception/BalanceInsufficientException.ts +58 -0
- package/src/exception/BaseException.ts +275 -0
- package/src/exception/BatchIdException.ts +58 -0
- package/src/exception/CodeException.ts +119 -0
- package/src/exception/DecryptionKeyException.ts +65 -0
- package/src/exception/InvalidResponseException.ts +112 -0
- package/src/exception/MetaMissingException.ts +58 -0
- package/src/exception/MolecularHashMismatchException.ts +115 -0
- package/src/exception/MolecularHashMissingException.ts +58 -0
- package/src/exception/NegativeAmountException.ts +58 -0
- package/src/exception/PolicyInvalidException.ts +58 -0
- package/src/exception/SignatureMalformedException.ts +58 -0
- package/src/exception/SignatureMismatchException.ts +98 -0
- package/src/exception/StackableUnitAmountException.ts +65 -0
- package/src/exception/StackableUnitDecimalsException.ts +65 -0
- package/src/exception/TransferBalanceException.ts +98 -0
- package/src/exception/TransferMalformedException.ts +58 -0
- package/src/exception/TransferMismatchedException.ts +58 -0
- package/src/exception/TransferRemainderException.ts +58 -0
- package/src/exception/TransferToSelfException.ts +58 -0
- package/src/exception/TransferUnbalancedException.ts +58 -0
- package/src/exception/UnauthenticatedException.ts +147 -0
- package/src/exception/WalletCredentialException.ts +108 -0
- package/src/exception/WalletShadowException.ts +65 -0
- package/src/exception/WrongTokenTypeException.ts +58 -0
- package/src/exception/index.ts +272 -0
- package/src/index.ts +512 -0
- package/src/instance/rules/Callback.ts +257 -0
- package/src/instance/rules/Condition.ts +96 -0
- package/src/instance/rules/Meta.ts +163 -0
- package/src/instance/rules/Rule.ts +29 -0
- package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
- package/src/libraries/CheckMolecule.ts +581 -0
- package/src/libraries/Decimal.ts +94 -0
- package/src/libraries/Dot.ts +202 -0
- package/src/libraries/GraphQLClient.ts +276 -0
- package/src/libraries/Hex.ts +155 -0
- package/src/libraries/UrqlClientWrapper.ts +336 -0
- package/src/libraries/array.ts +91 -0
- package/src/libraries/crypto.ts +816 -0
- package/src/libraries/strings.ts +458 -0
- package/src/mutation/Mutation.ts +103 -0
- package/src/mutation/MutationActiveSession.ts +108 -0
- package/src/mutation/MutationClaimShadowWallet.ts +91 -0
- package/src/mutation/MutationCreateIdentifier.ts +86 -0
- package/src/mutation/MutationCreateMeta.ts +92 -0
- package/src/mutation/MutationCreateRule.ts +90 -0
- package/src/mutation/MutationCreateToken.ts +92 -0
- package/src/mutation/MutationCreateWallet.ts +78 -0
- package/src/mutation/MutationDepositBufferToken.ts +72 -0
- package/src/mutation/MutationLinkIdentifier.ts +87 -0
- package/src/mutation/MutationProposeMolecule.ts +147 -0
- package/src/mutation/MutationRequestAuthorization.ts +77 -0
- package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
- package/src/mutation/MutationRequestTokens.ts +98 -0
- package/src/mutation/MutationTransferTokens.ts +87 -0
- package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
- package/src/query/Query.ts +187 -0
- package/src/query/QueryActiveSession.ts +89 -0
- package/src/query/QueryAtom.ts +275 -0
- package/src/query/QueryBalance.ts +102 -0
- package/src/query/QueryBatch.ts +145 -0
- package/src/query/QueryBatchHistory.ts +86 -0
- package/src/query/QueryContinuId.ts +91 -0
- package/src/query/QueryMetaType.ts +177 -0
- package/src/query/QueryMetaTypeViaAtom.ts +199 -0
- package/src/query/QueryPolicy.ts +91 -0
- package/src/query/QueryToken.ts +90 -0
- package/src/query/QueryUserActivity.ts +153 -0
- package/src/query/QueryWalletBundle.ts +92 -0
- package/src/query/QueryWalletList.ts +106 -0
- package/src/response/EnhancedResponse.ts +345 -0
- package/src/response/Response.ts +254 -0
- package/src/response/ResponseActiveSession.ts +72 -0
- package/src/response/ResponseAtom.ts +132 -0
- package/src/response/ResponseAuthorizationGuest.ts +119 -0
- package/src/response/ResponseBalance.ts +153 -0
- package/src/response/ResponseClaimShadowWallet.ts +56 -0
- package/src/response/ResponseContinuId.ts +101 -0
- package/src/response/ResponseCreateIdentifier.ts +56 -0
- package/src/response/ResponseCreateMeta.ts +58 -0
- package/src/response/ResponseCreateRule.ts +56 -0
- package/src/response/ResponseCreateToken.ts +58 -0
- package/src/response/ResponseCreateWallet.ts +58 -0
- package/src/response/ResponseLinkIdentifier.ts +87 -0
- package/src/response/ResponseMetaBatch.ts +72 -0
- package/src/response/ResponseMetaType.ts +108 -0
- package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
- package/src/response/ResponsePolicy.ts +90 -0
- package/src/response/ResponseProposeMolecule.ts +155 -0
- package/src/response/ResponseQueryActiveSession.ts +105 -0
- package/src/response/ResponseQueryUserActivity.ts +89 -0
- package/src/response/ResponseRequestAuthorization.ts +100 -0
- package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
- package/src/response/ResponseRequestTokens.ts +58 -0
- package/src/response/ResponseTransferTokens.ts +72 -0
- package/src/response/ResponseWalletBundle.ts +95 -0
- package/src/response/ResponseWalletList.ts +165 -0
- package/src/schemas/index.ts +457 -0
- package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
- package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
- package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
- package/src/subscribe/Subscribe.ts +182 -0
- package/src/subscribe/WalletStatusSubscribe.ts +70 -0
- package/src/subscribe/index.ts +59 -0
- package/src/types/assertions.ts +722 -0
- package/src/types/client.ts +567 -0
- package/src/types/crypto.ts +541 -0
- package/src/types/graphql.ts +630 -0
- package/src/types/guards.ts +659 -0
- package/src/types/index.ts +614 -0
- package/src/types/response.ts +133 -0
- package/src/types/template-literals.ts +382 -0
- package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
- package/src/validation/ValidationService.ts +607 -0
- package/src/validation/schemas.ts +447 -0
- package/src/versions/HashAtom.ts +170 -0
- package/src/versions/Version4.ts +120 -0
- package/src/versions/index.ts +80 -0
package/src/core/Atom.ts
ADDED
|
@@ -0,0 +1,646 @@
|
|
|
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
|
+
/**
|
|
50
|
+
* Atom class - The fundamental unit of KnishIO DLT transactions
|
|
51
|
+
*
|
|
52
|
+
* Atoms represent single, monodirectional actions in the distributed ledger.
|
|
53
|
+
* They are the building blocks of Molecules and maintain strict type safety
|
|
54
|
+
* while ensuring perfect compatibility with other SDK implementations.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import { shake256, convertToBase17 } from '@/libraries/crypto'
|
|
58
|
+
import JsSHA from 'jssha'
|
|
59
|
+
import { PROTOCOL_CONFIG } from '@/constants'
|
|
60
|
+
import { handleIsotope, isWalletAddress, isPosition } from '@/types/guards'
|
|
61
|
+
import type {
|
|
62
|
+
AtomIsotope,
|
|
63
|
+
AtomParams,
|
|
64
|
+
WalletAddress,
|
|
65
|
+
TokenSlug,
|
|
66
|
+
MetaType,
|
|
67
|
+
MetaId,
|
|
68
|
+
BatchId
|
|
69
|
+
} from '@/types'
|
|
70
|
+
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// ATOM CONSTANTS WITH CONST ASSERTIONS (2025 TYPESCRIPT)
|
|
73
|
+
// =============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Atom default values with const assertions for immutability
|
|
77
|
+
*/
|
|
78
|
+
export const ATOM_DEFAULTS = {
|
|
79
|
+
POSITION: '',
|
|
80
|
+
WALLET_ADDRESS: '',
|
|
81
|
+
ISOTOPE: 'C',
|
|
82
|
+
TOKEN: 'USER',
|
|
83
|
+
VALUE: null,
|
|
84
|
+
BATCH_ID: null,
|
|
85
|
+
META_TYPE: null,
|
|
86
|
+
META_ID: null,
|
|
87
|
+
META: [],
|
|
88
|
+
OTS_FRAGMENT: null,
|
|
89
|
+
INDEX: null,
|
|
90
|
+
VERSION: PROTOCOL_CONFIG.DEFAULT_SDK_VERSION
|
|
91
|
+
} as const satisfies {
|
|
92
|
+
readonly POSITION: string
|
|
93
|
+
readonly WALLET_ADDRESS: string
|
|
94
|
+
readonly ISOTOPE: AtomIsotope
|
|
95
|
+
readonly TOKEN: string
|
|
96
|
+
readonly VALUE: null
|
|
97
|
+
readonly BATCH_ID: null
|
|
98
|
+
readonly META_TYPE: null
|
|
99
|
+
readonly META_ID: null
|
|
100
|
+
readonly META: readonly []
|
|
101
|
+
readonly OTS_FRAGMENT: null
|
|
102
|
+
readonly INDEX: null
|
|
103
|
+
readonly VERSION: number
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Atom validation constants with const assertions
|
|
108
|
+
*/
|
|
109
|
+
export const ATOM_VALIDATION = {
|
|
110
|
+
MIN_INDEX: 0,
|
|
111
|
+
MAX_INDEX: Number.MAX_SAFE_INTEGER,
|
|
112
|
+
REQUIRED_FIELDS: ['position', 'walletAddress', 'isotope'] as const,
|
|
113
|
+
OPTIONAL_FIELDS: ['token', 'value', 'batchId', 'metaType', 'metaId', 'meta'] as const
|
|
114
|
+
} as const
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// ATOM INTERFACE AND TYPES
|
|
118
|
+
// =============================================================================
|
|
119
|
+
|
|
120
|
+
export interface AtomMetaData {
|
|
121
|
+
readonly key: string
|
|
122
|
+
readonly value: string | number | boolean | null
|
|
123
|
+
readonly [additionalProps: string]: unknown
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface AtomCreationParams extends AtomParams {
|
|
127
|
+
version?: number
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface AtomHashableData {
|
|
131
|
+
position: string
|
|
132
|
+
walletAddress: string
|
|
133
|
+
isotope: AtomIsotope
|
|
134
|
+
token: string
|
|
135
|
+
value: string | null
|
|
136
|
+
batchId: string | null
|
|
137
|
+
metaType: string | null
|
|
138
|
+
metaId: string | null
|
|
139
|
+
meta: AtomMetaData[] | null
|
|
140
|
+
createdAt: string
|
|
141
|
+
index: number
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// =============================================================================
|
|
145
|
+
// ATOM CLASS IMPLEMENTATION
|
|
146
|
+
// =============================================================================
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Atom - Represents a single, atomic operation in the distributed ledger
|
|
150
|
+
* Maintains full compatibility with JavaScript SDK implementation
|
|
151
|
+
*/
|
|
152
|
+
export default class Atom {
|
|
153
|
+
// Core properties matching JS SDK exactly
|
|
154
|
+
public position: string
|
|
155
|
+
public walletAddress: WalletAddress | string
|
|
156
|
+
public isotope: AtomIsotope
|
|
157
|
+
public token: TokenSlug | string
|
|
158
|
+
public value: string | number | null
|
|
159
|
+
public batchId: BatchId | string | null
|
|
160
|
+
public metaType: MetaType | string | null
|
|
161
|
+
public metaId: MetaId | string | null
|
|
162
|
+
public meta: AtomMetaData[]
|
|
163
|
+
public otsFragment: string | null
|
|
164
|
+
public index: number | null
|
|
165
|
+
public createdAt: string
|
|
166
|
+
public version: number
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a new Atom instance
|
|
170
|
+
* Constructor signature matches JavaScript SDK exactly
|
|
171
|
+
*/
|
|
172
|
+
constructor({
|
|
173
|
+
position = ATOM_DEFAULTS.POSITION,
|
|
174
|
+
walletAddress = ATOM_DEFAULTS.WALLET_ADDRESS as any,
|
|
175
|
+
isotope = ATOM_DEFAULTS.ISOTOPE as AtomIsotope,
|
|
176
|
+
token = ATOM_DEFAULTS.TOKEN,
|
|
177
|
+
value = ATOM_DEFAULTS.VALUE,
|
|
178
|
+
batchId = ATOM_DEFAULTS.BATCH_ID,
|
|
179
|
+
metaType = ATOM_DEFAULTS.META_TYPE,
|
|
180
|
+
metaId = ATOM_DEFAULTS.META_ID,
|
|
181
|
+
meta = null,
|
|
182
|
+
otsFragment = ATOM_DEFAULTS.OTS_FRAGMENT,
|
|
183
|
+
index = ATOM_DEFAULTS.INDEX,
|
|
184
|
+
createdAt = null,
|
|
185
|
+
version = ATOM_DEFAULTS.VERSION
|
|
186
|
+
}: AtomCreationParams = {}) {
|
|
187
|
+
|
|
188
|
+
// Use const assertion values with nullish coalescing (2025 pattern)
|
|
189
|
+
this.position = position ?? ATOM_DEFAULTS.POSITION
|
|
190
|
+
this.walletAddress = walletAddress ?? ATOM_DEFAULTS.WALLET_ADDRESS
|
|
191
|
+
this.isotope = isotope ?? ATOM_DEFAULTS.ISOTOPE
|
|
192
|
+
this.token = token ?? ATOM_DEFAULTS.TOKEN
|
|
193
|
+
this.value = value
|
|
194
|
+
this.batchId = batchId
|
|
195
|
+
this.metaType = metaType
|
|
196
|
+
this.metaId = metaId
|
|
197
|
+
this.meta = meta ? [...meta] : [...ATOM_DEFAULTS.META]
|
|
198
|
+
this.otsFragment = otsFragment
|
|
199
|
+
this.index = index
|
|
200
|
+
this.version = version ?? ATOM_DEFAULTS.VERSION
|
|
201
|
+
// CRITICAL: Use provided createdAt or generate timestamp in milliseconds as string (Implementation Guide requirement)
|
|
202
|
+
this.createdAt = createdAt || String(+new Date())
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// =============================================================================
|
|
206
|
+
// INSTANCE METHODS - MATCH JAVASCRIPT SDK
|
|
207
|
+
// =============================================================================
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get aggregated metadata for this atom
|
|
211
|
+
* Matches JavaScript SDK Meta.aggregateMeta functionality
|
|
212
|
+
*/
|
|
213
|
+
public aggregatedMeta(): Record<string, unknown> {
|
|
214
|
+
const aggregated: Record<string, unknown> = {}
|
|
215
|
+
|
|
216
|
+
for (const metaItem of this.meta) {
|
|
217
|
+
if (metaItem && typeof metaItem === 'object' && 'key' in metaItem) {
|
|
218
|
+
aggregated[metaItem.key] = metaItem.value
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return aggregated
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get values that will be used for hashing
|
|
227
|
+
* Must match JavaScript SDK getHashableValues exactly - returns array of strings!
|
|
228
|
+
*/
|
|
229
|
+
public getHashableValues(): string[] {
|
|
230
|
+
const hashableValues: string[] = []
|
|
231
|
+
for (const property of Atom.getHashableProps()) {
|
|
232
|
+
const value = (this as any)[property]
|
|
233
|
+
|
|
234
|
+
// All nullable values are not hashed (only custom keys)
|
|
235
|
+
if (value === null && !['position', 'walletAddress'].includes(property)) {
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Hashing individual meta keys and values
|
|
240
|
+
if (property === 'meta') {
|
|
241
|
+
for (const meta of value) {
|
|
242
|
+
if (typeof meta.value !== 'undefined' && meta.value !== null) {
|
|
243
|
+
hashableValues.push(String(meta.key))
|
|
244
|
+
hashableValues.push(String(meta.value))
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// Default value
|
|
249
|
+
hashableValues.push(value === null ? '' : String(value))
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return hashableValues
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get structured data for JSON serialization
|
|
257
|
+
* Separate from hashable values to maintain proper typing
|
|
258
|
+
*/
|
|
259
|
+
public getStructuredData(): AtomHashableData {
|
|
260
|
+
return {
|
|
261
|
+
position: this.position || '',
|
|
262
|
+
walletAddress: this.walletAddress?.toString() || '',
|
|
263
|
+
isotope: this.isotope,
|
|
264
|
+
token: this.token?.toString() || '',
|
|
265
|
+
value: this.value !== null ? this.value.toString() : null,
|
|
266
|
+
batchId: this.batchId?.toString() || null,
|
|
267
|
+
metaType: this.metaType?.toString() || null,
|
|
268
|
+
metaId: this.metaId?.toString() || null,
|
|
269
|
+
meta: this.meta.length > 0 ? [...this.meta] : null,
|
|
270
|
+
createdAt: this.createdAt,
|
|
271
|
+
index: this.index !== null ? this.index : 0
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Returns JSON-ready object for cross-SDK compatibility (2025 TS best practices)
|
|
277
|
+
*
|
|
278
|
+
* Provides clean serialization of atomic operations with optional OTS fragments.
|
|
279
|
+
* Follows 2025 TypeScript best practices with proper type safety and validation.
|
|
280
|
+
*
|
|
281
|
+
* @param options - Serialization options
|
|
282
|
+
* @param options.includeOtsFragments - Include OTS signature fragments (default: true)
|
|
283
|
+
* @param options.validateFields - Validate required fields (default: false)
|
|
284
|
+
* @return JSON-serializable object
|
|
285
|
+
* @throws Error if atom is in invalid state for serialization
|
|
286
|
+
*/
|
|
287
|
+
public toJSON(options: {
|
|
288
|
+
includeOtsFragments?: boolean
|
|
289
|
+
validateFields?: boolean
|
|
290
|
+
} = {}): AtomHashableData & { otsFragment?: string | null } {
|
|
291
|
+
const {
|
|
292
|
+
includeOtsFragments = true,
|
|
293
|
+
validateFields = false
|
|
294
|
+
} = options
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
// Validate required fields if requested
|
|
298
|
+
if (validateFields) {
|
|
299
|
+
const requiredFields = ['position', 'walletAddress', 'isotope', 'token'] as const
|
|
300
|
+
for (const field of requiredFields) {
|
|
301
|
+
if (!this[field]) {
|
|
302
|
+
throw new Error(`Required field '${field}' is missing or empty`)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Core atom properties (always included) - use structured data for JSON
|
|
308
|
+
const structuredData = this.getStructuredData()
|
|
309
|
+
const serialized: AtomHashableData & { otsFragment?: string | null } = {
|
|
310
|
+
...structuredData
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Optional OTS fragments (can be large, so optional)
|
|
314
|
+
if (includeOtsFragments && this.otsFragment) {
|
|
315
|
+
serialized.otsFragment = this.otsFragment
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return serialized
|
|
319
|
+
|
|
320
|
+
} catch (error) {
|
|
321
|
+
throw new Error(`Atom serialization failed: ${(error as Error).message}`)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Create a deep copy of this atom
|
|
327
|
+
*/
|
|
328
|
+
public clone(): Atom {
|
|
329
|
+
return new Atom({
|
|
330
|
+
position: this.position,
|
|
331
|
+
walletAddress: this.walletAddress as any,
|
|
332
|
+
isotope: this.isotope,
|
|
333
|
+
token: this.token,
|
|
334
|
+
value: this.value,
|
|
335
|
+
batchId: this.batchId,
|
|
336
|
+
metaType: this.metaType,
|
|
337
|
+
metaId: this.metaId,
|
|
338
|
+
meta: this.meta.map(m => ({ ...m })) as any,
|
|
339
|
+
otsFragment: this.otsFragment,
|
|
340
|
+
index: this.index,
|
|
341
|
+
version: this.version
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check if atom is valid for inclusion in molecules
|
|
347
|
+
* TypeScript 2025: Uses exhaustive type guards for compile-time completeness
|
|
348
|
+
*/
|
|
349
|
+
public isValid(): boolean {
|
|
350
|
+
// TypeScript 2025: Enhanced validation with type guards
|
|
351
|
+
if (!isPosition(this.position) || !isWalletAddress(this.walletAddress)) {
|
|
352
|
+
return false
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Exhaustive isotope validation with compile-time completeness
|
|
356
|
+
return handleIsotope(this.isotope, {
|
|
357
|
+
V: () => this.value !== null && !isNaN(Number(this.value)), // Value atoms must have numeric value
|
|
358
|
+
M: () => Boolean(this.metaType && this.metaId), // Meta atoms must have metaType and metaId
|
|
359
|
+
C: () => true, // Continue atoms are always valid if position/address present
|
|
360
|
+
U: () => true, // User atoms
|
|
361
|
+
T: () => true, // Token atoms
|
|
362
|
+
I: () => true, // Identity atoms
|
|
363
|
+
R: () => true, // Rule atoms
|
|
364
|
+
B: () => true, // Buffer atoms
|
|
365
|
+
F: () => true // Fuse atoms
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// =============================================================================
|
|
370
|
+
// STATIC METHODS - MATCH JAVASCRIPT SDK EXACTLY
|
|
371
|
+
// =============================================================================
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Get properties that should be included in hash calculation
|
|
375
|
+
* MUST match JavaScript SDK implementation exactly
|
|
376
|
+
*/
|
|
377
|
+
static getHashableProps(): string[] {
|
|
378
|
+
return [
|
|
379
|
+
'position',
|
|
380
|
+
'walletAddress',
|
|
381
|
+
'isotope',
|
|
382
|
+
'token',
|
|
383
|
+
'value',
|
|
384
|
+
'batchId',
|
|
385
|
+
'metaType',
|
|
386
|
+
'metaId',
|
|
387
|
+
'meta',
|
|
388
|
+
'createdAt'
|
|
389
|
+
]
|
|
390
|
+
// NOTE: 'index' excluded - matches JavaScript canonical exactly
|
|
391
|
+
// Index is for atom ordering, not hash calculation
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get properties for unclaimed shadow wallets
|
|
396
|
+
* Matches JavaScript SDK getUnclaimedProps
|
|
397
|
+
*/
|
|
398
|
+
static getUnclaimedProps(): string[] {
|
|
399
|
+
return [
|
|
400
|
+
'position',
|
|
401
|
+
'walletAddress',
|
|
402
|
+
'isotope',
|
|
403
|
+
'token',
|
|
404
|
+
'value',
|
|
405
|
+
'batchId',
|
|
406
|
+
'metaType',
|
|
407
|
+
'metaId',
|
|
408
|
+
'meta',
|
|
409
|
+
'createdAt'
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Create atom from parameters - factory method
|
|
415
|
+
* Matches JavaScript SDK Atom.create static method
|
|
416
|
+
*/
|
|
417
|
+
static create(params: AtomCreationParams): Atom {
|
|
418
|
+
return new Atom(params)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Creates an Atom instance from JSON data (2025 TS best practices)
|
|
423
|
+
*
|
|
424
|
+
* Handles cross-SDK atom deserialization with robust error handling.
|
|
425
|
+
* Essential for reconstructing atoms from other SDK implementations.
|
|
426
|
+
*
|
|
427
|
+
* @param json - JSON string or object to deserialize
|
|
428
|
+
* @param options - Deserialization options
|
|
429
|
+
* @param options.validateStructure - Validate required fields (default: true)
|
|
430
|
+
* @param options.strictMode - Strict validation mode (default: false)
|
|
431
|
+
* @return Reconstructed atom instance
|
|
432
|
+
* @throws Error if JSON is invalid or required fields are missing
|
|
433
|
+
*/
|
|
434
|
+
static fromJSON(json: string | Record<string, unknown>, options: {
|
|
435
|
+
validateStructure?: boolean
|
|
436
|
+
strictMode?: boolean
|
|
437
|
+
} = {}): Atom {
|
|
438
|
+
const {
|
|
439
|
+
validateStructure = true,
|
|
440
|
+
strictMode = false
|
|
441
|
+
} = options
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
// Parse JSON safely
|
|
445
|
+
const data = typeof json === 'string' ? JSON.parse(json) : json
|
|
446
|
+
|
|
447
|
+
// Validate required fields in strict mode
|
|
448
|
+
if (strictMode || validateStructure) {
|
|
449
|
+
const requiredFields = ['position', 'walletAddress', 'isotope', 'token'] as const
|
|
450
|
+
for (const field of requiredFields) {
|
|
451
|
+
if (!data[field]) {
|
|
452
|
+
throw new Error(`Required field '${field}' is missing or empty`)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Create atom instance with required fields
|
|
458
|
+
const atom = new Atom({
|
|
459
|
+
position: data.position as string,
|
|
460
|
+
walletAddress: data.walletAddress as any,
|
|
461
|
+
isotope: data.isotope as AtomIsotope,
|
|
462
|
+
token: data.token as string,
|
|
463
|
+
value: data.value as string | number | null,
|
|
464
|
+
batchId: data.batchId as string | null,
|
|
465
|
+
metaType: data.metaType as string | null,
|
|
466
|
+
metaId: data.metaId as string | null,
|
|
467
|
+
meta: data.meta as any,
|
|
468
|
+
index: data.index as number | null,
|
|
469
|
+
version: data.version as number
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// Set additional properties that may not be in constructor
|
|
473
|
+
if (data.otsFragment) {
|
|
474
|
+
atom.otsFragment = data.otsFragment as string
|
|
475
|
+
}
|
|
476
|
+
if (data.createdAt) {
|
|
477
|
+
atom.createdAt = data.createdAt as string
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return atom
|
|
481
|
+
|
|
482
|
+
} catch (error) {
|
|
483
|
+
throw new Error(`Atom deserialization failed: ${(error as Error).message}`)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Convert JSON object back to Atom instance
|
|
489
|
+
* Matches JavaScript SDK jsonToObject method - kept for compatibility
|
|
490
|
+
*/
|
|
491
|
+
static jsonToObject(jsonData: Record<string, unknown>): Atom {
|
|
492
|
+
return Atom.fromJSON(jsonData)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Hash multiple atoms to create molecular hash
|
|
497
|
+
* MUST match JavaScript SDK algorithm exactly for cross-platform compatibility
|
|
498
|
+
*
|
|
499
|
+
* CRITICAL: This implementation must exactly mirror the JavaScript SDK approach:
|
|
500
|
+
* 1. Sort atoms by index property
|
|
501
|
+
* 2. For each sorted atom: ADD number of atoms to hashableValues array
|
|
502
|
+
* 3. CONCATENATE all atom's hashable values to the array
|
|
503
|
+
* 4. Iterate through hashableValues array and update SHAKE256 sponge
|
|
504
|
+
*/
|
|
505
|
+
static hashAtoms({ atoms }: { atoms: Atom[] }): string {
|
|
506
|
+
if (!atoms || atoms.length === 0) {
|
|
507
|
+
return ''
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Step 1: Sort atoms by index to ensure deterministic hashing (matches JS SDK)
|
|
511
|
+
const atomList = Atom.sortAtoms(atoms)
|
|
512
|
+
const numberOfAtoms = String(atoms.length)
|
|
513
|
+
let hashableValues: string[] = []
|
|
514
|
+
|
|
515
|
+
// Step 2: Build hashableValues array exactly like JavaScript SDK
|
|
516
|
+
for (const atom of atomList) {
|
|
517
|
+
// Add number of atoms (matching JS SDK comment: "Add number of atoms (???)")
|
|
518
|
+
hashableValues.push(numberOfAtoms)
|
|
519
|
+
|
|
520
|
+
// Add atom's properties - concatenate the array returned by getHashableValues
|
|
521
|
+
hashableValues = hashableValues.concat(atom.getHashableValues())
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Step 3: Create molecular hash using SHAKE256 exactly like JS SDK (iterative updates)
|
|
525
|
+
const molecularSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
526
|
+
|
|
527
|
+
// CRITICAL FIX: Use iterative updates like JavaScript, not string concatenation
|
|
528
|
+
for (const hashableValue of hashableValues) {
|
|
529
|
+
molecularSponge.update(hashableValue) // Match JavaScript SDK exactly
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Step 4: Get hex hash (256 bits = 64 hex chars) and convert to base17
|
|
533
|
+
const hexHash = molecularSponge.getHash('HEX', { outputLen: 256 })
|
|
534
|
+
|
|
535
|
+
// Step 5: Convert hex hash to base17 format (Implementation Guide requirement)
|
|
536
|
+
return convertToBase17(hexHash.toLowerCase())
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Sort atoms by index
|
|
541
|
+
* Matches JavaScript SDK sortAtoms method
|
|
542
|
+
*/
|
|
543
|
+
static sortAtoms(atoms: Atom[]): Atom[] {
|
|
544
|
+
return [...atoms].sort((a, b) => {
|
|
545
|
+
const indexA = a.index !== null ? a.index : 0
|
|
546
|
+
const indexB = b.index !== null ? b.index : 0
|
|
547
|
+
return indexA - indexB
|
|
548
|
+
})
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Generate next atom index for a collection
|
|
553
|
+
* Matches JavaScript SDK generateNextAtomIndex method
|
|
554
|
+
*/
|
|
555
|
+
static generateNextAtomIndex(atoms: Atom[]): number {
|
|
556
|
+
if (!atoms || atoms.length === 0) {
|
|
557
|
+
return 0
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const indices = atoms
|
|
561
|
+
.map(atom => atom.index !== null ? atom.index : 0)
|
|
562
|
+
.filter(index => typeof index === 'number')
|
|
563
|
+
|
|
564
|
+
return indices.length > 0 ? Math.max(...indices) + 1 : 0
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Filter atoms by isotope(s)
|
|
569
|
+
* Matches JavaScript SDK isotopeFilter method
|
|
570
|
+
*/
|
|
571
|
+
static isotopeFilter(isotopes: AtomIsotope | AtomIsotope[], atoms: Atom[]): Atom[] {
|
|
572
|
+
const targetIsotopes = Array.isArray(isotopes) ? isotopes : [isotopes]
|
|
573
|
+
return atoms.filter(atom => targetIsotopes.includes(atom.isotope))
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// =============================================================================
|
|
577
|
+
// VALIDATION AND UTILITY METHODS
|
|
578
|
+
// =============================================================================
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Validate atom data structure
|
|
582
|
+
*/
|
|
583
|
+
static validateAtom(atom: unknown): atom is Atom {
|
|
584
|
+
if (!atom || typeof atom !== 'object') {
|
|
585
|
+
return false
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const atomObj = atom as Record<string, unknown>
|
|
589
|
+
|
|
590
|
+
return typeof atomObj.position === 'string' &&
|
|
591
|
+
typeof atomObj.walletAddress === 'string' &&
|
|
592
|
+
typeof atomObj.isotope === 'string' &&
|
|
593
|
+
typeof atomObj.token === 'string' &&
|
|
594
|
+
(atomObj.value === null || typeof atomObj.value === 'string' || typeof atomObj.value === 'number') &&
|
|
595
|
+
Array.isArray(atomObj.meta)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Check if atom is a specific isotope type
|
|
600
|
+
*/
|
|
601
|
+
static isIsotope(atom: Atom, isotope: AtomIsotope): boolean {
|
|
602
|
+
return atom.isotope === isotope
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Get all unique isotopes from atom collection
|
|
607
|
+
*/
|
|
608
|
+
static getUniqueIsotopes(atoms: Atom[]): AtomIsotope[] {
|
|
609
|
+
const isotopes = new Set<AtomIsotope>()
|
|
610
|
+
for (const atom of atoms) {
|
|
611
|
+
isotopes.add(atom.isotope)
|
|
612
|
+
}
|
|
613
|
+
return Array.from(isotopes)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Calculate total value for value atoms
|
|
618
|
+
*/
|
|
619
|
+
static calculateTotalValue(atoms: Atom[]): number {
|
|
620
|
+
return atoms
|
|
621
|
+
.filter(atom => atom.isotope === 'V' && atom.value !== null)
|
|
622
|
+
.reduce((total, atom) => total + Number(atom.value), 0)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Group atoms by isotope
|
|
627
|
+
*/
|
|
628
|
+
static groupByIsotope(atoms: Atom[]): Record<AtomIsotope, Atom[]> {
|
|
629
|
+
const groups: Partial<Record<AtomIsotope, Atom[]>> = {}
|
|
630
|
+
|
|
631
|
+
for (const atom of atoms) {
|
|
632
|
+
if (!groups[atom.isotope]) {
|
|
633
|
+
groups[atom.isotope] = []
|
|
634
|
+
}
|
|
635
|
+
groups[atom.isotope]!.push(atom)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return groups as Record<AtomIsotope, Atom[]>
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// =============================================================================
|
|
643
|
+
// TYPE EXPORTS
|
|
644
|
+
// =============================================================================
|
|
645
|
+
|
|
646
|
+
// Types are already exported from the types package
|