@typemove/iota 2.0.0 → 2.0.1

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 (68) hide show
  1. package/dist/esm/builtin/0x1.d.ts +733 -0
  2. package/dist/esm/builtin/0x1.d.ts.map +1 -0
  3. package/dist/esm/builtin/0x1.js +4293 -0
  4. package/dist/esm/builtin/0x1.js.map +1 -0
  5. package/dist/esm/builtin/0x2.d.ts +4607 -0
  6. package/dist/esm/builtin/0x2.d.ts.map +1 -0
  7. package/dist/esm/builtin/0x2.js +19016 -0
  8. package/dist/esm/builtin/0x2.js.map +1 -0
  9. package/dist/esm/builtin/0x3.d.ts +1901 -0
  10. package/dist/esm/builtin/0x3.d.ts.map +1 -0
  11. package/dist/esm/builtin/0x3.js +6165 -0
  12. package/dist/esm/builtin/0x3.js.map +1 -0
  13. package/dist/esm/builtin/index.d.ts +4 -0
  14. package/dist/esm/builtin/index.d.ts.map +1 -0
  15. package/dist/esm/builtin/index.js +7 -0
  16. package/dist/esm/builtin/index.js.map +1 -0
  17. package/dist/esm/codegen/codegen.d.ts +20 -0
  18. package/dist/esm/codegen/codegen.d.ts.map +1 -0
  19. package/dist/esm/codegen/codegen.js +245 -0
  20. package/dist/esm/codegen/codegen.js.map +1 -0
  21. package/dist/esm/codegen/index.d.ts +2 -0
  22. package/dist/esm/codegen/index.d.ts.map +1 -0
  23. package/dist/esm/codegen/index.js +2 -0
  24. package/dist/esm/codegen/index.js.map +1 -0
  25. package/dist/esm/codegen/run.d.ts +3 -0
  26. package/dist/esm/codegen/run.d.ts.map +1 -0
  27. package/dist/esm/codegen/run.js.map +1 -0
  28. package/dist/esm/index.d.ts +4 -0
  29. package/dist/esm/index.d.ts.map +1 -0
  30. package/dist/esm/index.js +4 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/models.d.ts +18 -0
  33. package/dist/esm/models.d.ts.map +1 -0
  34. package/dist/esm/models.js +2 -0
  35. package/dist/esm/models.js.map +1 -0
  36. package/dist/esm/move-coder.d.ts +26 -0
  37. package/dist/esm/move-coder.d.ts.map +1 -0
  38. package/dist/esm/move-coder.js +266 -0
  39. package/dist/esm/move-coder.js.map +1 -0
  40. package/dist/esm/sui-chain-adapter.d.ts +15 -0
  41. package/dist/esm/sui-chain-adapter.d.ts.map +1 -0
  42. package/dist/esm/sui-chain-adapter.js +84 -0
  43. package/dist/esm/sui-chain-adapter.js.map +1 -0
  44. package/dist/esm/to-internal.d.ts +4 -0
  45. package/dist/esm/to-internal.d.ts.map +1 -0
  46. package/dist/esm/to-internal.js +96 -0
  47. package/dist/esm/to-internal.js.map +1 -0
  48. package/dist/esm/transaction.d.ts +15 -0
  49. package/dist/esm/transaction.d.ts.map +1 -0
  50. package/dist/esm/transaction.js +83 -0
  51. package/dist/esm/transaction.js.map +1 -0
  52. package/package.json +6 -6
  53. package/src/abis/0x1.json +4213 -0
  54. package/src/abis/0x2.json +30623 -0
  55. package/src/abis/0x3.json +10691 -0
  56. package/src/builtin/0x1.ts +6065 -0
  57. package/src/builtin/0x2.ts +28181 -0
  58. package/src/builtin/0x3.ts +9666 -0
  59. package/src/builtin/index.ts +6 -0
  60. package/src/codegen/codegen.ts +288 -0
  61. package/src/codegen/index.ts +1 -0
  62. package/src/codegen/run.ts +57 -0
  63. package/src/index.ts +4 -0
  64. package/src/models.ts +21 -0
  65. package/src/move-coder.ts +316 -0
  66. package/src/sui-chain-adapter.ts +113 -0
  67. package/src/to-internal.ts +127 -0
  68. package/src/transaction.ts +127 -0
@@ -0,0 +1,6 @@
1
+ /* Autogenerated file. Do not edit manually. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ export * as _0x1 from './0x1.js'
5
+ export * as _0x2 from './0x2.js'
6
+ export * as _0x3 from './0x3.js'
@@ -0,0 +1,288 @@
1
+ import { IotaMoveNormalizedModule, IotaEvent, IotaMoveObject, IotaClient } from '@iota/iota-sdk/client'
2
+
3
+ import * as fs from 'fs'
4
+ import chalk from 'chalk'
5
+ import {
6
+ InternalMoveModule,
7
+ InternalMoveStruct,
8
+ structQname,
9
+ InternalMoveFunction,
10
+ InternalMoveFunctionVisibility,
11
+ normalizeToJSName,
12
+ camel
13
+ } from '@typemove/move'
14
+ import { AbstractCodegen } from '@typemove/move/codegen'
15
+ import { join } from 'path'
16
+ import { IotaChainAdapter } from '../sui-chain-adapter.js'
17
+
18
+ export async function codegen(
19
+ abisDir: string,
20
+ outDir = join('src', 'types', 'sui'),
21
+ endpoint: string,
22
+ genExample = false,
23
+ builtin = false
24
+ ) {
25
+ if (!fs.existsSync(abisDir)) {
26
+ console.error(chalk.red(`ABIs directory ${abisDir} does not exist`))
27
+ return 0
28
+ }
29
+ try {
30
+ const gen = new IotaCodegen(endpoint)
31
+ const numFiles = await gen.generate(abisDir, outDir, builtin)
32
+ if (numFiles > 0) {
33
+ console.log(chalk.green(`Generated for ${numFiles} accounts for Iota to ${outDir}`))
34
+ } else {
35
+ console.error(chalk.red(`No account found`))
36
+ }
37
+ return numFiles
38
+ } catch (e) {
39
+ console.error(chalk.red(`Failed to generate for ${abisDir}, please check if ABI json files are valid`))
40
+ console.log(e)
41
+ return 0
42
+ }
43
+ }
44
+
45
+ export class IotaCodegen extends AbstractCodegen<
46
+ // IotaNetwork,
47
+ IotaMoveNormalizedModule,
48
+ IotaEvent | IotaMoveObject
49
+ > {
50
+ ADDRESS_TYPE = 'string'
51
+ SYSTEM_PACKAGE = '@typemove/iota'
52
+ // ADDRESS_TYPE = 'string'
53
+ // MAIN_NET = IotaNetwork.MAIN_NET
54
+ // TEST_NET = IotaNetwork.TEST_NET
55
+ PREFIX = 'Iota'
56
+ // STRUCT_FIELD_NAME = 'fields'
57
+ // GENERATE_ON_ENTRY = true
58
+ PAYLOAD_OPTIONAL = true
59
+
60
+ constructor(endpoint: string) {
61
+ super(new IotaChainAdapter(new IotaClient({ url: endpoint })))
62
+ }
63
+
64
+ readModulesFile(fullPath: string) {
65
+ const res = super.readModulesFile(fullPath)
66
+ if (res.result) {
67
+ return res.result
68
+ }
69
+ return res
70
+ }
71
+
72
+ generateStructs(module: InternalMoveModule, struct: InternalMoveStruct, events: Set<string>): string {
73
+ let content = ''
74
+ switch (structQname(module, struct)) {
75
+ // TODO they should still have module code generated
76
+ case '0x1::ascii::Char':
77
+ case '0x1::ascii::String':
78
+ case '0x2::object::ID':
79
+ content += `export type ${struct.name} = string`
80
+ break
81
+ case '0x2::coin::Coin':
82
+ content += `export type ${struct.name}<T> = string`
83
+ break
84
+ case '0x2::balance::Balance':
85
+ content += `export type ${struct.name}<T> = bigint`
86
+ break
87
+ case '0x1::option::Option':
88
+ content += `export type Option<T> = T | undefined`
89
+ break
90
+ }
91
+ return content + super.generateStructs(module, struct, events, content !== '')
92
+ }
93
+
94
+ generateForEvents(module: InternalMoveModule, struct: InternalMoveStruct): string {
95
+ switch (structQname(module, struct)) {
96
+ case '0x1::ascii::Char':
97
+ case '0x1::ascii::String':
98
+ case '0x2::object::ID':
99
+ case '0x2::coin::Coin':
100
+ case '0x1::option::Option':
101
+ case '0x2::balance::Balance':
102
+ return ''
103
+ }
104
+ return super.generateForEvents(module, struct)
105
+ }
106
+
107
+ protected generateExtra(address: string | undefined, module: InternalMoveModule): string {
108
+ const funcs = module.exposedFunctions.map((f) =>
109
+ this.generateBuilderForFunction(address || module.address, module, f)
110
+ )
111
+
112
+ const viewFuncs = module.exposedFunctions.map((f) => this.generateViewFunction(module, f))
113
+
114
+ return `
115
+ export namespace builder {
116
+ ${funcs.join('\n')}
117
+ }
118
+ export namespace view {
119
+ ${viewFuncs.join('\n')}
120
+ }
121
+ `
122
+ }
123
+
124
+ private generateArgs(module: InternalMoveModule, func: InternalMoveFunction, isView: boolean) {
125
+ const args = []
126
+ const argsLen = func.params.length
127
+ for (const [idx, arg] of func.params.entries()) {
128
+ if (idx === argsLen - 1 && arg.qname === '0x2::tx_context::TxContext') {
129
+ // no op
130
+ } else if (arg.reference) {
131
+ args.push({
132
+ paramType: isView ? this.ADDRESS_TYPE : `${this.ADDRESS_TYPE} | TransactionObjectArgument`,
133
+ callValue: `_args.push(transactionArgumentOrObject(args[${idx}], tx))`
134
+ })
135
+ } else if (arg.isVector()) {
136
+ // TODO fix pure vector
137
+ args.push({
138
+ paramType: isView
139
+ ? `${this.ADDRESS_TYPE}[]`
140
+ : `(${this.ADDRESS_TYPE} | TransactionObjectArgument)[] | TransactionArgument`,
141
+ callValue: `_args.push(transactionArgumentOrVec(args[${idx}], tx))`
142
+ })
143
+ } else {
144
+ // Handle pure type
145
+ let pureFunction = ''
146
+ const paramType = isView
147
+ ? this.generateTypeForDescriptor(arg, module.address)
148
+ : `${this.generateTypeForDescriptor(arg, module.address)} | TransactionArgument`
149
+
150
+ switch (arg.qname.toLowerCase()) {
151
+ case 'u8':
152
+ pureFunction = `transactionArgumentOrPureU8`
153
+ break
154
+ case 'u16':
155
+ pureFunction = `transactionArgumentOrPureU16`
156
+ break
157
+ case 'u32':
158
+ pureFunction = `transactionArgumentOrPureU32`
159
+ break
160
+ case 'u64':
161
+ pureFunction = `transactionArgumentOrPureU64`
162
+ break
163
+ case 'u128':
164
+ pureFunction = `transactionArgumentOrPureU128`
165
+ break
166
+ case 'u256':
167
+ pureFunction = `transactionArgumentOrPureU256`
168
+ break
169
+ case 'bool':
170
+ pureFunction = `transactionArgumentOrPureBool`
171
+ break
172
+ case 'string':
173
+ pureFunction = `transactionArgumentOrPureString`
174
+ break
175
+ case 'address':
176
+ pureFunction = `transactionArgumentOrPureAddress`
177
+ break
178
+ // case 'vector':
179
+ // case 'option':
180
+ default:
181
+ pureFunction = `transactionArgumentOrPure`
182
+ // paramType = 'TransactionArgument'
183
+ }
184
+ const callValue = pureFunction ? `_args.push(${pureFunction}(args[${idx}], tx))` : `_args.push(args[${idx}])`
185
+
186
+ args.push({
187
+ paramType,
188
+ callValue
189
+ })
190
+ }
191
+ }
192
+ return args
193
+ }
194
+
195
+ protected generateViewFunction(module: InternalMoveModule, func: InternalMoveFunction): string {
196
+ if (func.visibility === InternalMoveFunctionVisibility.PRIVATE) {
197
+ return ''
198
+ }
199
+ const genericString = this.generateFunctionTypeParameters(func)
200
+
201
+ const typeParamArg = func.typeParams
202
+ .map((v, idx) => {
203
+ return `TypeDescriptor<T${idx}> | string`
204
+ })
205
+ .join(',')
206
+
207
+ const args = this.generateArgs(module, func, true)
208
+ const returnType = `${this.generateFunctionReturnTypeParameters(func, module.address)}`
209
+
210
+ return `export async function ${camel(normalizeToJSName(func.name))}${genericString}(
211
+ client: IotaClient,
212
+ args: [${args.map((a) => a.paramType).join(',')}],
213
+ ${
214
+ typeParamArg.length > 0 ? `typeArguments: [${typeParamArg}]` : ``
215
+ } ): Promise<TypedDevInspectResults<${returnType}>> {
216
+ const tx = new Transaction()
217
+ builder.${camel(normalizeToJSName(func.name))}(tx, args ${typeParamArg.length > 0 ? `, typeArguments` : ''})
218
+ const inspectRes = await client.devInspectTransactionBlock({
219
+ transactionBlock: tx,
220
+ sender: ZERO_ADDRESS
221
+ })
222
+
223
+ return (await getMoveCoder(client)).decodeDevInspectResult<${returnType}>(inspectRes)
224
+ }`
225
+ }
226
+
227
+ protected generateBuilderForFunction(
228
+ address: string,
229
+ module: InternalMoveModule,
230
+ func: InternalMoveFunction
231
+ ): string {
232
+ if (func.visibility === InternalMoveFunctionVisibility.PRIVATE && func.isEntry !== true) {
233
+ return ''
234
+ }
235
+
236
+ const args = this.generateArgs(module, func, false)
237
+
238
+ const genericString = this.generateFunctionTypeParameters(func)
239
+
240
+ const typeParamArg = func.typeParams
241
+ .map((v, idx) => {
242
+ return `TypeDescriptor<T${idx}> | string`
243
+ })
244
+ .join(',')
245
+ const typeParamToString = func.typeParams
246
+ .map((v, idx) => {
247
+ return `typeof typeArguments[${idx}] === 'string' ? typeArguments[${idx}] : typeArguments[${idx}].getSignature()`
248
+ })
249
+ .join(',')
250
+
251
+ return `export function ${camel(normalizeToJSName(func.name))}${genericString}(tx: Transaction,
252
+ args: [${args.map((a) => a.paramType).join(',')}],
253
+ ${typeParamArg.length > 0 ? `typeArguments: [${typeParamArg}]` : ``} ):
254
+ TransactionArgument & [ ${'TransactionArgument,'.repeat(args.length)} ] {
255
+ const _args: any[] = []
256
+ ${args.map((a) => a.callValue).join('\n')}
257
+
258
+ // @ts-ignore
259
+ return tx.moveCall({
260
+ target: "${address}::${module.name}::${func.name}",
261
+ arguments: _args,
262
+ ${typeParamArg.length > 0 ? `typeArguments: [${typeParamToString}]` : ``}
263
+ })
264
+ }`
265
+ }
266
+
267
+ generateImports(): string {
268
+ return `
269
+ ${super.generateImports()}
270
+ import { ZERO_ADDRESS, TypedDevInspectResults, getMoveCoder } from '@typemove/iota'
271
+ import { Transaction, TransactionArgument, TransactionObjectArgument } from '@iota/iota-sdk/transactions'
272
+ import { IotaClient } from '@iota/iota-sdk/client'
273
+ import { transactionArgumentOrObject,
274
+ transactionArgumentOrVec,
275
+ transactionArgumentOrPure,
276
+ transactionArgumentOrPureU8,
277
+ transactionArgumentOrPureU16,
278
+ transactionArgumentOrPureU32,
279
+ transactionArgumentOrPureU64,
280
+ transactionArgumentOrPureU128,
281
+ transactionArgumentOrPureU256,
282
+ transactionArgumentOrPureBool,
283
+ transactionArgumentOrPureString,
284
+ transactionArgumentOrPureAddress,
285
+ } from '@typemove/iota'
286
+ `
287
+ }
288
+ }
@@ -0,0 +1 @@
1
+ export * from './codegen.js'
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { codegen } from './codegen.js'
4
+ import { Command } from 'commander'
5
+ import { createRequire } from 'module'
6
+ import fs from 'fs'
7
+ import path from 'path'
8
+ import { IotaClient } from '@iota/iota-sdk/client'
9
+ const require = createRequire(import.meta.url)
10
+ let pkg = undefined
11
+ try {
12
+ pkg = require('../../package.json')
13
+ } catch (e) {
14
+ pkg = require('../../../package.json')
15
+ }
16
+
17
+ const program = new Command()
18
+ program
19
+ .name('typemove-iota')
20
+ .description('CLI to generate typescript types from Iota ABIs')
21
+ .showHelpAfterError()
22
+ .version(pkg.version)
23
+ .argument('<location>', 'Directory of ABI json files or address of account to generate types for')
24
+ .option('-a, --abi-dir <dir>', 'Directory to store downloaded ABI. Only useful if <location> is address', './abis')
25
+ .option('-t, --target-dir <dir>', 'Directory to output generated files', './types')
26
+ .option(
27
+ '-n, --network <mainnet|testnet|$url>',
28
+ 'Network to use, could be either "mainnet", "testnet" or any node URL',
29
+ 'mainnet'
30
+ )
31
+ .action(async (location, options) => {
32
+ let endpoint = options.network
33
+ if (endpoint == 'mainnet') {
34
+ endpoint = 'https://api.mainnet.iota.cafe/'
35
+ }
36
+ if (endpoint == 'testnet') {
37
+ endpoint = 'https://api.testnet.iota.cafe/'
38
+ }
39
+
40
+ const suiClient = new IotaClient({ url: endpoint })
41
+
42
+ let abisDir = location
43
+ if (location.startsWith('0x')) {
44
+ const abiAddress = abisDir
45
+ const abi = await suiClient.getNormalizedMoveModulesByPackage({ package: abiAddress })
46
+ abisDir = options.abiDir
47
+ if (!fs.existsSync(abisDir)) {
48
+ fs.mkdirSync(abisDir, { recursive: true })
49
+ }
50
+ fs.writeFileSync(path.join(abisDir, abiAddress + '.json'), JSON.stringify(abi, null, 2))
51
+ }
52
+
53
+ const num = await codegen(abisDir, options.targetDir, endpoint, true)
54
+ process.exit(num == 0 ? 1 : 0)
55
+ })
56
+
57
+ program.parse()
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './models.js'
2
+ export * from './transaction.js'
3
+
4
+ export { MoveCoder, defaultMoveCoder, getMoveCoder } from './move-coder.js'
package/src/models.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { IotaEvent, MoveCallIotaTransaction, IotaMoveObject, DevInspectResults } from '@iota/iota-sdk/client'
2
+ import { DecodedStruct } from '@typemove/move'
3
+
4
+ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000000000000000000000000000'
5
+
6
+ export type TypedEventInstance<T> = DecodedStruct<IotaEvent, T>
7
+ export type TypedIotaMoveObject<T> = DecodedStruct<IotaMoveObject, T>
8
+
9
+ export type TypedFunctionPayload<T extends Array<any>> = MoveCallIotaTransaction & {
10
+ /**
11
+ * decoded argument data using ABI, undefined if there is decoding error, usually because the ABI/data mismatch
12
+ */
13
+ arguments_decoded: T
14
+ }
15
+
16
+ export type TypedDevInspectResults<T extends Array<any>> = DevInspectResults & {
17
+ /**
18
+ * Decoded return values using ABI, undefined if there is decoding error, usually because the ABI/data mismatch
19
+ */
20
+ results_decoded?: T
21
+ }
@@ -0,0 +1,316 @@
1
+ import { TypedDevInspectResults, TypedEventInstance, TypedFunctionPayload } from './models.js'
2
+ import {
3
+ AbstractMoveCoder,
4
+ ANY_TYPE,
5
+ DecodedStruct,
6
+ accountTypeString,
7
+ parseMoveType,
8
+ SPLITTER,
9
+ TypeDescriptor,
10
+ InternalMoveModule
11
+ } from '@typemove/move'
12
+ import {
13
+ MoveCallIotaTransaction,
14
+ IotaCallArg,
15
+ IotaEvent,
16
+ IotaMoveNormalizedModule,
17
+ IotaMoveObject,
18
+ DevInspectResults,
19
+ IotaClient
20
+ } from '@iota/iota-sdk/client'
21
+ import { toInternalModule } from './to-internal.js'
22
+ import { IotaChainAdapter } from './sui-chain-adapter.js'
23
+ import { dynamic_field } from './builtin/0x2.js'
24
+ import { BcsType, bcs } from '@iota/iota-sdk/bcs'
25
+
26
+ // import { Encoding } from '@mysten/bcs/types', this doesn't get exported correctly
27
+ export type Encoding = 'base58' | 'base64' | 'hex'
28
+
29
+ import { normalizeIotaObjectId, normalizeIotaAddress } from '@iota/iota-sdk/utils'
30
+
31
+ export class MoveCoder extends AbstractMoveCoder<
32
+ // IotaNetwork,
33
+ IotaMoveNormalizedModule,
34
+ IotaEvent | IotaMoveObject
35
+ > {
36
+ constructor(client: IotaClient) {
37
+ super(new IotaChainAdapter(client))
38
+ }
39
+
40
+ load(module: IotaMoveNormalizedModule, address: string): InternalMoveModule {
41
+ address = accountTypeString(address)
42
+ let m = this.moduleMapping.get(module.address + '::' + module.name)
43
+ const mDeclared = this.moduleMapping.get(address + '::' + module.name)
44
+ if (m && mDeclared) {
45
+ return m
46
+ }
47
+ this.accounts.add(module.address)
48
+ m = toInternalModule(module)
49
+ this.loadInternal(m, address)
50
+ return m
51
+ }
52
+
53
+ protected async decode(data: any, type: TypeDescriptor): Promise<any> {
54
+ switch (type.qname) {
55
+ case '0x1::ascii::Char':
56
+ if (data !== undefined && typeof data !== 'string') {
57
+ // bcs
58
+ const byte = (await super.decode(data, type)).byte as number
59
+ return String.fromCharCode(byte)
60
+ }
61
+ case '0x1::ascii::String':
62
+ if (data !== undefined && typeof data !== 'string') {
63
+ // bcs verified
64
+ const bytes = (await super.decode(data, type)).bytes as number[]
65
+ return new TextDecoder().decode(new Uint8Array(bytes))
66
+ }
67
+ case '0x2::object::ID':
68
+ if (data !== undefined && typeof data !== 'string') {
69
+ // bcs verified
70
+ const bytes = (await super.decode(data, type)).bytes as string
71
+ return normalizeIotaObjectId(bytes)
72
+ }
73
+ case '0x2::url::Url':
74
+ if (data !== undefined && typeof data !== 'string') {
75
+ // bcs
76
+ return (await super.decode(data, type)).url
77
+ }
78
+ case '0x2::coin::Coin':
79
+ if (data !== undefined && typeof data !== 'string') {
80
+ // bcs
81
+ const bytes = (await super.decode(data, type)).id.id.bytes as number[]
82
+ return new TextDecoder().decode(new Uint8Array(bytes))
83
+ }
84
+ return data
85
+ case '0x2::balance::Balance':
86
+ if (data.value) {
87
+ // bcs verfied
88
+ const balance = await super.decode(data, type)
89
+ return balance.value
90
+ }
91
+ return BigInt(data)
92
+ case '0x1::option::Option':
93
+ if (data === null) {
94
+ return data
95
+ }
96
+ if (data.vec) {
97
+ // bcs verifed
98
+ let vec = await super.decode(data, type)
99
+ vec = vec.vec
100
+ if (vec.length === 0) {
101
+ return null
102
+ }
103
+ return vec[0]
104
+ }
105
+ return this.decode(data, type.typeArgs[0])
106
+ case 'Address':
107
+ const str = data as string
108
+ return normalizeIotaAddress(str)
109
+ case '0x1::string::String':
110
+ if (typeof data !== 'string') {
111
+ // bcs
112
+ return new TextDecoder().decode(new Uint8Array(data.bytes))
113
+ }
114
+ default:
115
+ return super.decode(data, type)
116
+ }
117
+ }
118
+
119
+ decodeEvent<T>(event: IotaEvent): Promise<TypedEventInstance<T> | undefined> {
120
+ return this.decodedStruct(event)
121
+ }
122
+ filterAndDecodeEvents<T>(type: TypeDescriptor<T> | string, resources: IotaEvent[]): Promise<TypedEventInstance<T>[]> {
123
+ if (typeof type === 'string') {
124
+ type = parseMoveType(type)
125
+ }
126
+ return this.filterAndDecodeStruct(type, resources)
127
+ }
128
+
129
+ async getDynamicFields<T1, T2>(
130
+ objects: IotaMoveObject[],
131
+ keyType: TypeDescriptor<T1> = ANY_TYPE,
132
+ valueType: TypeDescriptor<T2> = ANY_TYPE
133
+ ): Promise<dynamic_field.Field<T1, T2>[]> {
134
+ // const type = dynamic_field.Field.TYPE
135
+ // Not using the code above to avoid cycle initialize failed
136
+ const type = new TypeDescriptor<dynamic_field.Field<T1, T2>>('0x2::dynamic_field::Field')
137
+ type.typeArgs = [keyType, valueType]
138
+ const res = await this.filterAndDecodeObjects(type, objects)
139
+ return res.map((o) => o.data_decoded)
140
+ }
141
+
142
+ filterAndDecodeObjects<T>(
143
+ type: TypeDescriptor<T>,
144
+ objects: IotaMoveObject[]
145
+ ): Promise<DecodedStruct<IotaMoveObject, T>[]> {
146
+ return this.filterAndDecodeStruct(type, objects)
147
+ }
148
+
149
+ async decodeFunctionPayload(
150
+ payload: MoveCallIotaTransaction,
151
+ inputs: IotaCallArg[]
152
+ ): Promise<MoveCallIotaTransaction> {
153
+ const functionType = [payload.package, payload.module, payload.function].join(SPLITTER)
154
+ const func = await this.getMoveFunction(functionType)
155
+ const params = this.adapter.getMeaningfulFunctionParams(func.params)
156
+ const args = []
157
+ for (const value of payload.arguments || []) {
158
+ const argValue = value as any
159
+ if ('Input' in (argValue as any)) {
160
+ const idx = argValue.Input
161
+ const arg = inputs[idx]
162
+ if (arg.type === 'pure') {
163
+ args.push(arg.value)
164
+ } else if (arg.type === 'object') {
165
+ // object is not there
166
+ args.push(undefined)
167
+ } else {
168
+ console.error('unexpected function arg value')
169
+ args.push(undefined)
170
+ }
171
+ // args.push(arg) // TODO check why ts not work using arg.push(arg)
172
+ } else {
173
+ args.push(undefined)
174
+ }
175
+ }
176
+
177
+ const argumentsTyped = await this.decodeArray(args, params, false)
178
+ return {
179
+ ...payload,
180
+ arguments_decoded: argumentsTyped
181
+ } as TypedFunctionPayload<any>
182
+ }
183
+
184
+ private bcsRegistered = new Set<string>()
185
+ private bcsRegistry = new Map<string, BcsType<any>>()
186
+
187
+ private async getBCSTypeWithArgs(type: TypeDescriptor, args: BcsType<any>[] = []): Promise<BcsType<any>> {
188
+ const qname = type.qname
189
+ const sig = type.getNormalizedSignature()
190
+ const cached = this.bcsRegistry.get(sig)
191
+ if (cached) {
192
+ return cached
193
+ }
194
+ const lowerQname = qname.toLowerCase()
195
+ switch (lowerQname) {
196
+ case 'u8':
197
+ case 'u16':
198
+ case 'u32':
199
+ case 'u64':
200
+ case 'u128':
201
+ case 'u256':
202
+ case 'bool':
203
+ return bcs[lowerQname]()
204
+ case 'address':
205
+ return bcs.Address
206
+ case 'vector':
207
+ return bcs.vector(args[0])
208
+ default:
209
+ if (!qname.includes('::')) {
210
+ throw `Unimplemented builtin type ${qname}`
211
+ }
212
+ }
213
+ let moveStruct
214
+ try {
215
+ moveStruct = await this.getMoveStruct(qname)
216
+ } catch (e) {
217
+ console.error('Invalid move address', qname)
218
+ throw e
219
+ }
220
+ const structDef: Record<string, any> = {}
221
+ for (const field of moveStruct.fields) {
222
+ if (field.type.qname.startsWith('T') && args.length) {
223
+ const index = +field.type.qname.slice(1)
224
+ structDef[field.name] = args[index]
225
+ } else if (field.type.typeArgs.length && args.length) {
226
+ structDef[field.name] = await this.getBCSTypeWithArgs(field.type, args)
227
+ } else {
228
+ structDef[field.name] = await this.getBCSType(field.type)
229
+ }
230
+ }
231
+ return bcs.struct(qname, structDef)
232
+ }
233
+
234
+ async getBCSType(type: TypeDescriptor): Promise<BcsType<any>> {
235
+ const args = await Promise.all(type.typeArgs.map((x) => this.getBCSType(x)))
236
+ const bcsType = await this.getBCSTypeWithArgs(type, args)
237
+ this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
238
+ return bcsType
239
+ }
240
+
241
+ async registerBCSTypes(type: TypeDescriptor): Promise<void> {
242
+ const sig = type.getNormalizedSignature()
243
+ if (this.bcsRegistered.has(sig)) {
244
+ return
245
+ }
246
+ this.bcsRegistered.add(sig)
247
+
248
+ const bcsType = await this.getBCSType(type)
249
+ this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
250
+ }
251
+
252
+ async decodeBCS(type: TypeDescriptor, data: Uint8Array | string, encoding?: Encoding): Promise<any> {
253
+ await this.registerBCSTypes(type)
254
+ if (typeof data == 'string') {
255
+ const buf = Buffer.from(data, encoding as any)
256
+ data = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
257
+ }
258
+ const bcsType = this.bcsRegistry.get(type.getNormalizedSignature())
259
+ return bcsType?.parse(data)
260
+ }
261
+
262
+ async decodeDevInspectResult<T extends any[]>(inspectRes: DevInspectResults): Promise<TypedDevInspectResults<T>> {
263
+ const returnValues = []
264
+ if (inspectRes.results != null) {
265
+ for (const r of inspectRes.results) {
266
+ if (r.returnValues) {
267
+ for (const returnValue of r.returnValues) {
268
+ const type = parseMoveType(returnValue[1])
269
+ const bcsDecoded = await this.decodeBCS(type, new Uint8Array(returnValue[0]))
270
+ const decoded = await this.decodeType(bcsDecoded, type)
271
+ returnValues.push(decoded)
272
+ }
273
+ } else {
274
+ returnValues.push(null)
275
+ }
276
+ }
277
+ }
278
+ return { ...inspectRes, results_decoded: returnValues as any }
279
+ }
280
+ }
281
+
282
+ const DEFAULT_ENDPOINT = 'https://api.mainnet.iota.cafe/'
283
+ const CODER_MAP = new Map<string, MoveCoder>()
284
+ const CHAIN_ID_CODER_MAP = new Map<string, MoveCoder>()
285
+
286
+ export function defaultMoveCoder(endpoint: string = DEFAULT_ENDPOINT): MoveCoder {
287
+ let coder = CODER_MAP.get(endpoint)
288
+ if (!coder) {
289
+ coder = new MoveCoder(new IotaClient({ url: endpoint }))
290
+ CODER_MAP.set(endpoint, coder)
291
+ }
292
+ return coder
293
+ }
294
+
295
+ const PROVIDER_CODER_MAP = new Map<IotaClient, MoveCoder>()
296
+
297
+ let DEFAULT_CHAIN_ID: string | undefined
298
+
299
+ export async function getMoveCoder(client: IotaClient): Promise<MoveCoder> {
300
+ let coder = PROVIDER_CODER_MAP.get(client)
301
+ if (!coder) {
302
+ coder = new MoveCoder(client)
303
+ // TODO how to dedup
304
+ const id = await client.getChainIdentifier()
305
+ const defaultCoder = defaultMoveCoder()
306
+ if (!DEFAULT_CHAIN_ID) {
307
+ DEFAULT_CHAIN_ID = await defaultCoder.adapter.getChainId()
308
+ }
309
+ if (id === DEFAULT_CHAIN_ID) {
310
+ coder = defaultCoder
311
+ }
312
+
313
+ PROVIDER_CODER_MAP.set(client, coder)
314
+ }
315
+ return coder
316
+ }