@typemove/move 1.0.0-rc.10

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.
@@ -0,0 +1,390 @@
1
+ import {
2
+ accountAddressString,
3
+ moduleQname,
4
+ SPLITTER,
5
+ VECTOR_STR,
6
+ } from './utils.js'
7
+ import {
8
+ DecodedStruct,
9
+ matchType,
10
+ parseMoveType,
11
+ TypeDescriptor,
12
+ } from './types.js'
13
+ import {
14
+ InternalMoveFunction,
15
+ InternalMoveModule,
16
+ InternalMoveStruct,
17
+ } from './internal-models.js'
18
+ // import { bytesToBigInt } from '../utils/index.js'
19
+ import { ChainAdapter } from './chain-adapter.js'
20
+
21
+ export abstract class AbstractMoveCoder<ModuleType, StructType> {
22
+ protected moduleMapping = new Map<string, InternalMoveModule>()
23
+ private typeMapping = new Map<string, InternalMoveStruct>()
24
+ private funcMapping = new Map<string, InternalMoveFunction>()
25
+ // network: string
26
+ adapter: ChainAdapter<ModuleType, StructType>
27
+
28
+ protected constructor(adapter: ChainAdapter<ModuleType, StructType>) {
29
+ this.adapter = adapter
30
+ }
31
+
32
+ contains(account: string, name: string) {
33
+ return this.moduleMapping.has(moduleQname({ address: account, name }))
34
+ }
35
+
36
+ abstract load(module: ModuleType): InternalMoveModule
37
+
38
+ protected loadInternal(module: InternalMoveModule) {
39
+ const account = accountAddressString(module.address)
40
+ if (this.contains(account, module.name)) {
41
+ return
42
+ }
43
+ this.moduleMapping.set(
44
+ moduleQname({ address: account, name: module.name }),
45
+ module
46
+ )
47
+
48
+ for (const struct of module.structs) {
49
+ // TODO move to util
50
+ const key = [account, module.name, struct.name].join(SPLITTER)
51
+ this.typeMapping.set(key, struct)
52
+ }
53
+
54
+ for (const func of module.exposedFunctions) {
55
+ // if (!func.isEntry) {
56
+ // continue
57
+ // }
58
+ const key = [account, module.name, func.name].join(SPLITTER)
59
+ this.funcMapping.set(key, func)
60
+ }
61
+ }
62
+
63
+ protected decodeBigInt(data: any): bigint {
64
+ if (Array.isArray(data)) {
65
+ throw new Error('data is in byte array')
66
+ // Only sui function need this, strange
67
+ // const bytes = data as number[]
68
+ // return bytesToBigInt(new Uint8Array(bytes.slice().reverse()))
69
+ }
70
+
71
+ return BigInt(data)
72
+ }
73
+
74
+ protected encodeBigInt(data: bigint): any {
75
+ return '0x' + data.toString(16)
76
+ }
77
+
78
+ private requestMap = new Map<string, Promise<InternalMoveModule>>()
79
+
80
+ async getMoveStruct(type: string): Promise<InternalMoveStruct> {
81
+ const [account_, module, typeName] = type.split(SPLITTER)
82
+ const account = accountAddressString(account_)
83
+ type = [account, module, typeName].join(SPLITTER)
84
+
85
+ let struct = this.typeMapping.get(type)
86
+ if (struct) {
87
+ return struct
88
+ }
89
+ const key = account + SPLITTER + module
90
+ let resp = this.requestMap.get(account + SPLITTER + module)
91
+ if (!resp) {
92
+ resp = this.adapter.fetchModule(account, module).then((m) => {
93
+ return this.load(m)
94
+ })
95
+ this.requestMap.set(key, resp)
96
+ }
97
+ await resp
98
+ struct = this.typeMapping.get(type)
99
+ if (struct) {
100
+ return struct
101
+ }
102
+ throw new Error(
103
+ 'Failed to load function ' + type + ' type are not imported anywhere'
104
+ )
105
+ }
106
+
107
+ async getMoveFunction(type: string): Promise<InternalMoveFunction> {
108
+ const [account_, module, typeName] = type.split(SPLITTER)
109
+ const account = accountAddressString(account_)
110
+ type = [account, module, typeName].join(SPLITTER)
111
+
112
+ let func = this.funcMapping.get(type)
113
+ if (func) {
114
+ return func
115
+ }
116
+ const key = account + SPLITTER + module
117
+ let resp = this.requestMap.get(account + SPLITTER + module)
118
+ if (!resp) {
119
+ resp = this.adapter.fetchModule(account, module).then((m) => {
120
+ return this.load(m)
121
+ })
122
+ this.requestMap.set(key, resp)
123
+ }
124
+ await resp
125
+ func = this.funcMapping.get(type)
126
+ if (func) {
127
+ return func
128
+ }
129
+ throw new Error(
130
+ 'Failed to load function ' + type + ' type are not imported anywhere'
131
+ )
132
+ }
133
+
134
+ protected async decode<T>(data: any, type: TypeDescriptor<T>): Promise<T> {
135
+ // process simple type
136
+ if (type.reference) {
137
+ return data
138
+ }
139
+ switch (type.qname) {
140
+ case 'signer': // TODO check this, aptos only
141
+ case 'address':
142
+ case 'Address':
143
+ case '0x1::string::String':
144
+ case 'bool':
145
+ case 'Bool':
146
+ case 'u8':
147
+ case 'U8':
148
+ case 'u16':
149
+ case 'U16':
150
+ case 'u32':
151
+ case 'U32':
152
+ return data
153
+ case 'u64':
154
+ case 'U64':
155
+ case 'u128':
156
+ case 'U128':
157
+ case 'u256':
158
+ case 'U256':
159
+ return this.decodeBigInt(data) as any
160
+ }
161
+
162
+ // process vector
163
+ if (type.qname.toLowerCase() === VECTOR_STR) {
164
+ // vector<u8> as hex string
165
+ if (type.typeArgs[0].qname === 'u8' || type.typeArgs[0].qname === 'U8') {
166
+ return data
167
+ }
168
+
169
+ const res = []
170
+ for (const entry of data) {
171
+ res.push(await this.decode(entry, type.typeArgs[0]))
172
+ }
173
+ return res as any
174
+ }
175
+
176
+ // Process complex type
177
+ const struct = await this.getMoveStruct(type.qname)
178
+
179
+ const typeCtx = new Map<string, TypeDescriptor>()
180
+ for (const [idx, typeArg] of type.typeArgs.entries()) {
181
+ typeCtx.set('T' + idx, typeArg)
182
+ }
183
+
184
+ const typedData: any = {}
185
+
186
+ for (const field of struct.fields) {
187
+ let filedType = field.type
188
+ filedType = filedType.applyTypeArgs(typeCtx)
189
+ const fieldValue = this.adapter.getData(data)[field.name]
190
+ const value = await this.decode(fieldValue, filedType)
191
+ typedData[field.name] = value
192
+ }
193
+ return typedData
194
+ }
195
+
196
+ protected async encode(data: any, type: TypeDescriptor): Promise<any> {
197
+ // process simple type
198
+ if (type.reference) {
199
+ return data
200
+ }
201
+ switch (type.qname) {
202
+ case 'signer': // TODO check this, aptos only
203
+ case 'address':
204
+ case 'Address':
205
+ case '0x2::object::ID':
206
+ case '0x2::coin::Coin':
207
+ case '0x1::string::String':
208
+ case 'bool':
209
+ case 'Bool':
210
+ case 'u8':
211
+ case 'U8':
212
+ case 'u16':
213
+ case 'U16':
214
+ case 'u32':
215
+ case 'U32':
216
+ return data
217
+ case 'u64':
218
+ case 'U64':
219
+ case 'u128':
220
+ case 'U128':
221
+ case 'u256':
222
+ case 'U256':
223
+ return this.encodeBigInt(data)
224
+ }
225
+
226
+ // process vector
227
+ if (type.qname.toLowerCase() === VECTOR_STR) {
228
+ // vector<u8> as hex string
229
+ if (type.typeArgs[0].qname === 'u8' || type.typeArgs[0].qname === 'U8') {
230
+ return data
231
+ }
232
+
233
+ const res = []
234
+ for (const entry of data) {
235
+ res.push(this.encode(entry, type.typeArgs[0]))
236
+ }
237
+ return res
238
+ }
239
+
240
+ // Process complex type
241
+ const struct = await this.getMoveStruct(type.qname)
242
+
243
+ const typeCtx = new Map<string, TypeDescriptor>()
244
+ for (const [idx, typeArg] of type.typeArgs.entries()) {
245
+ typeCtx.set('T' + idx, typeArg)
246
+ }
247
+
248
+ const typedData: any = {}
249
+
250
+ for (const field of struct.fields) {
251
+ let filedType = field.type
252
+ filedType = filedType.applyTypeArgs(typeCtx)
253
+ const value = await this.encode(data[field.name], filedType)
254
+ typedData[field.name] = value
255
+ }
256
+ return typedData
257
+ }
258
+
259
+ async decodeArray(
260
+ entries: any[],
261
+ types: TypeDescriptor[],
262
+ strict = true
263
+ ): Promise<any[]> {
264
+ const entriesDecoded: any[] = []
265
+ for (const [idx, arg] of entries.entries()) {
266
+ // TODO consider apply payload.type_arguments, but this might be hard since we don't code gen for them
267
+ const argType = types[idx]
268
+ try {
269
+ if (!strict && arg === undefined) {
270
+ entriesDecoded.push(arg)
271
+ } else {
272
+ entriesDecoded.push(await this.decode(arg, argType))
273
+ }
274
+ } catch (e) {
275
+ throw Error(
276
+ 'Decoding error for ' +
277
+ JSON.stringify(arg) +
278
+ 'using type' +
279
+ argType +
280
+ e.toString()
281
+ )
282
+ }
283
+ }
284
+ return entriesDecoded
285
+ }
286
+
287
+ async encodeArray(
288
+ entriesDecoded: any[],
289
+ types: TypeDescriptor[]
290
+ ): Promise<any[]> {
291
+ const entries: any[] = []
292
+ for (const [idx, arg] of entriesDecoded.entries()) {
293
+ // TODO consider apply payload.type_arguments, but this might be hard since we don't code gen for them
294
+ const argType = types[idx]
295
+ try {
296
+ entries.push(await this.encode(arg, argType))
297
+ } catch (e) {
298
+ throw Error(
299
+ 'Decoding error for ' +
300
+ JSON.stringify(arg) +
301
+ 'using type' +
302
+ argType +
303
+ e.toString()
304
+ )
305
+ }
306
+ }
307
+ return entries
308
+ }
309
+
310
+ async encodeCallArgs(args: any[], func: string): Promise<any[]> {
311
+ const f = await this.getMoveFunction(func)
312
+ return this.encodeArray(
313
+ args,
314
+ this.adapter.getMeaningfulFunctionParams(f.params)
315
+ )
316
+ }
317
+
318
+ async decodeCallResult(res: any[], func: string): Promise<any[]> {
319
+ const f = await this.getMoveFunction(func)
320
+ return this.decodeArray(res, f.return)
321
+ }
322
+
323
+ async filterAndDecodeStruct<T, ST extends StructType>(
324
+ typeMatcher: TypeDescriptor<T>,
325
+ structsWithTags: ST[]
326
+ ): Promise<DecodedStruct<ST, T>[]> {
327
+ if (!structsWithTags) {
328
+ return [] as any
329
+ }
330
+ // const typeMatcherDescriptor = parseMoveType(typeMatcher)
331
+ const results: DecodedStruct<ST, T>[] = []
332
+ for (const resource of structsWithTags) {
333
+ const resourceType = this.adapter.getType(resource)
334
+ const resourceTypeDescriptor = parseMoveType(resourceType)
335
+ if (!matchType(typeMatcher, resourceTypeDescriptor)) {
336
+ continue
337
+ }
338
+
339
+ const result = await this.decodedStruct<T, ST>(resource)
340
+ if (result) {
341
+ results.push(result)
342
+ } else {
343
+ console.error('decoding error')
344
+ }
345
+ }
346
+ return results
347
+ }
348
+
349
+ protected async decodedStruct<T, ST extends StructType>(
350
+ typeStruct: ST
351
+ ): Promise<DecodedStruct<ST, T> | undefined> {
352
+ const typeDescriptor = parseMoveType(this.adapter.getType(typeStruct))
353
+ const typeArguments = typeDescriptor.typeArgs.map((t) => t.getSignature())
354
+
355
+ let dataTyped = undefined
356
+ try {
357
+ dataTyped = await this.decode(typeStruct, typeDescriptor)
358
+ } catch (e) {
359
+ throw Error(
360
+ 'Decoding error for struct' + JSON.stringify(typeStruct) + e.toString()
361
+ )
362
+ // return undefined
363
+ }
364
+ return {
365
+ ...typeStruct,
366
+ data_decoded: dataTyped,
367
+ type_arguments: typeArguments,
368
+ }
369
+ }
370
+ async decodedType<T, ST>(
371
+ typeStruct: ST,
372
+ type: TypeDescriptor<T>
373
+ ): Promise<T | undefined> {
374
+ if (typeStruct === null || typeStruct == undefined) {
375
+ return typeStruct as any
376
+ }
377
+ if (typeof typeStruct === 'object') {
378
+ if ('type' in typeStruct) {
379
+ const typeInStruct = parseMoveType(
380
+ (typeStruct.type as any).toString() || ''
381
+ )
382
+ if (!matchType(type, typeInStruct)) {
383
+ return undefined
384
+ }
385
+ }
386
+ }
387
+
388
+ return await this.decode(typeStruct, type)
389
+ }
390
+ }
package/src/account.ts ADDED
@@ -0,0 +1,93 @@
1
+ import { moduleQname, moduleQnameForType } from './utils.js'
2
+ import { InternalMoveModule } from './internal-models.js'
3
+
4
+ export class AccountModulesImportInfo {
5
+ // account to module
6
+ imports: Map<string, Set<string>>
7
+ account: string
8
+ moduleName: string
9
+
10
+ constructor(account: string, tsModuleName: string) {
11
+ this.account = account
12
+ this.moduleName = tsModuleName
13
+ this.imports = new Map<string, Set<string>>()
14
+ }
15
+
16
+ addImport(account: string, module: string) {
17
+ if (account === this.account) {
18
+ return
19
+ }
20
+ let accountModules = this.imports.get(account)
21
+ if (!accountModules) {
22
+ accountModules = new Set<string>()
23
+ this.imports.set(account, accountModules)
24
+ }
25
+ accountModules.add(module)
26
+ }
27
+ }
28
+
29
+ export class AccountRegister {
30
+ accountImports = new Map<string, AccountModulesImportInfo>()
31
+ pendingAccounts = new Set<string>()
32
+
33
+ register(
34
+ module: InternalMoveModule,
35
+ tsModuleName: string
36
+ ): AccountModulesImportInfo {
37
+ const currentModuleFqn = moduleQname(module)
38
+
39
+ let accountModuleImports = this.accountImports.get(module.address)
40
+ if (!accountModuleImports) {
41
+ accountModuleImports = new AccountModulesImportInfo(
42
+ module.address,
43
+ tsModuleName
44
+ )
45
+ this.accountImports.set(module.address, accountModuleImports)
46
+ // the account has already be processed, delete pending task
47
+ this.pendingAccounts.delete(module.address)
48
+ }
49
+
50
+ this.registerStruct(module, accountModuleImports)
51
+ this.registerFunctions(module, accountModuleImports)
52
+
53
+ this.accountImports.set(currentModuleFqn, accountModuleImports)
54
+ return accountModuleImports
55
+ }
56
+
57
+ private registerFunctions(
58
+ module: InternalMoveModule,
59
+ accountModuleImports: AccountModulesImportInfo
60
+ ): void {
61
+ for (const func of module.exposedFunctions) {
62
+ if (!func.isEntry) {
63
+ continue
64
+ }
65
+ for (const param of func.params) {
66
+ for (const type of param.dependedTypes()) {
67
+ const [account, module] = moduleQnameForType(type)
68
+ accountModuleImports.addImport(account, module)
69
+ if (!this.accountImports.has(account)) {
70
+ this.pendingAccounts.add(account)
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ private registerStruct(
78
+ module: InternalMoveModule,
79
+ accountModuleImports: AccountModulesImportInfo
80
+ ): void {
81
+ for (const struct of module.structs) {
82
+ for (const field of struct.fields) {
83
+ for (const type of field.type.dependedTypes()) {
84
+ const [account, module] = moduleQnameForType(type)
85
+ accountModuleImports.addImport(account, module)
86
+ if (!this.accountImports.has(account)) {
87
+ this.pendingAccounts.add(account)
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,29 @@
1
+ import { InternalMoveModule, InternalMoveStruct } from './internal-models.js'
2
+ import { TypeDescriptor } from './types.js'
3
+
4
+ export abstract class ChainAdapter<ModuleType, StructType> {
5
+ endpoint: string
6
+
7
+ constructor(endpoint: string) {
8
+ this.endpoint = endpoint
9
+ }
10
+
11
+ abstract fetchModule(account: string, module: string): Promise<ModuleType>
12
+
13
+ abstract fetchModules(account: string): Promise<ModuleType[]>
14
+ abstract toInternalModules(modules: ModuleType[]): InternalMoveModule[]
15
+
16
+ // Get all structs that represent Events
17
+ abstract getAllEventStructs(
18
+ module: InternalMoveModule[]
19
+ ): Map<string, InternalMoveStruct>
20
+
21
+ // Get the parameters that actually have arguments in runtime
22
+ // Aptos first signer and Sui's last TxContext are no use
23
+ abstract getMeaningfulFunctionParams(
24
+ params: TypeDescriptor[]
25
+ ): TypeDescriptor[]
26
+
27
+ abstract getType(base: StructType): string
28
+ abstract getData<T>(base: StructType): any
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './types.js'
2
+ export * from './utils.js'
3
+ export * from './account.js'
4
+ export * from './chain-adapter.js'
5
+ export * from './abstract-codegen.js'
6
+ export * from './abstract-move-coder.js'
7
+ export * from './internal-models.js'
@@ -0,0 +1,40 @@
1
+ import { TypeDescriptor } from './types.js'
2
+
3
+ export interface InternalMoveModule {
4
+ address: string
5
+ name: string
6
+ exposedFunctions: InternalMoveFunction[]
7
+ structs: InternalMoveStruct[]
8
+ }
9
+
10
+ export interface InternalMoveFunction {
11
+ name: string
12
+ visibility: InternalMoveFunctionVisibility
13
+ isEntry: boolean
14
+ typeParams: InternalMoveTypeParam[]
15
+ params: TypeDescriptor[]
16
+ return: TypeDescriptor[]
17
+ }
18
+
19
+ export interface InternalMoveStruct {
20
+ name: string
21
+ isNative: boolean
22
+ abilities: string[]
23
+ typeParams: InternalMoveTypeParam[]
24
+ fields: InternalMoveStructField[]
25
+ }
26
+
27
+ export interface InternalMoveStructField {
28
+ name: string
29
+ type: TypeDescriptor
30
+ }
31
+
32
+ export enum InternalMoveFunctionVisibility {
33
+ PRIVATE = 'private',
34
+ PUBLIC = 'public',
35
+ FRIEND = 'friend',
36
+ }
37
+
38
+ export type InternalMoveTypeParam = {
39
+ constraints: string[]
40
+ }