@typemove/iota 1.10.0-rc.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 (134) hide show
  1. package/LICENSE +201 -0
  2. package/Readme.md +161 -0
  3. package/builtin/0x1/package.json +5 -0
  4. package/builtin/0x2/package.json +5 -0
  5. package/builtin/0x3/package.json +5 -0
  6. package/builtin/package.json +5 -0
  7. package/codegen/package.json +5 -0
  8. package/dist/cjs/builtin/0x1.d.ts +778 -0
  9. package/dist/cjs/builtin/0x1.d.ts.map +1 -0
  10. package/dist/cjs/builtin/0x1.js +4297 -0
  11. package/dist/cjs/builtin/0x1.js.map +1 -0
  12. package/dist/cjs/builtin/0x2.d.ts +4256 -0
  13. package/dist/cjs/builtin/0x2.d.ts.map +1 -0
  14. package/dist/cjs/builtin/0x2.js +16540 -0
  15. package/dist/cjs/builtin/0x2.js.map +1 -0
  16. package/dist/cjs/builtin/0x3.d.ts +1978 -0
  17. package/dist/cjs/builtin/0x3.d.ts.map +1 -0
  18. package/dist/cjs/builtin/0x3.js +6120 -0
  19. package/dist/cjs/builtin/0x3.js.map +1 -0
  20. package/dist/cjs/builtin/index.d.ts +4 -0
  21. package/dist/cjs/builtin/index.d.ts.map +1 -0
  22. package/dist/cjs/builtin/index.js +11 -0
  23. package/dist/cjs/builtin/index.js.map +1 -0
  24. package/dist/cjs/codegen/codegen.d.ts +20 -0
  25. package/dist/cjs/codegen/codegen.d.ts.map +1 -0
  26. package/dist/cjs/codegen/codegen.js +253 -0
  27. package/dist/cjs/codegen/codegen.js.map +1 -0
  28. package/dist/cjs/codegen/index.d.ts +2 -0
  29. package/dist/cjs/codegen/index.d.ts.map +1 -0
  30. package/dist/cjs/codegen/index.js +5 -0
  31. package/dist/cjs/codegen/index.js.map +1 -0
  32. package/dist/cjs/index.d.ts +4 -0
  33. package/dist/cjs/index.d.ts.map +1 -0
  34. package/dist/cjs/index.js +11 -0
  35. package/dist/cjs/index.js.map +1 -0
  36. package/dist/cjs/models.d.ts +18 -0
  37. package/dist/cjs/models.d.ts.map +1 -0
  38. package/dist/cjs/models.js +5 -0
  39. package/dist/cjs/models.js.map +1 -0
  40. package/dist/cjs/move-coder.d.ts +27 -0
  41. package/dist/cjs/move-coder.d.ts.map +1 -0
  42. package/dist/cjs/move-coder.js +274 -0
  43. package/dist/cjs/move-coder.js.map +1 -0
  44. package/dist/cjs/package.json +3 -0
  45. package/dist/cjs/sui-chain-adapter.d.ts +15 -0
  46. package/dist/cjs/sui-chain-adapter.d.ts.map +1 -0
  47. package/dist/cjs/sui-chain-adapter.js +88 -0
  48. package/dist/cjs/sui-chain-adapter.js.map +1 -0
  49. package/dist/cjs/to-internal.d.ts +4 -0
  50. package/dist/cjs/to-internal.d.ts.map +1 -0
  51. package/dist/cjs/to-internal.js +99 -0
  52. package/dist/cjs/to-internal.js.map +1 -0
  53. package/dist/cjs/transaction.d.ts +15 -0
  54. package/dist/cjs/transaction.d.ts.map +1 -0
  55. package/dist/cjs/transaction.js +98 -0
  56. package/dist/cjs/transaction.js.map +1 -0
  57. package/dist/esm/builtin/0x1.d.ts +778 -0
  58. package/dist/esm/builtin/0x1.d.ts.map +1 -0
  59. package/dist/esm/builtin/0x1.js +4293 -0
  60. package/dist/esm/builtin/0x1.js.map +1 -0
  61. package/dist/esm/builtin/0x2.d.ts +4256 -0
  62. package/dist/esm/builtin/0x2.d.ts.map +1 -0
  63. package/dist/esm/builtin/0x2.js +16534 -0
  64. package/dist/esm/builtin/0x2.js.map +1 -0
  65. package/dist/esm/builtin/0x3.d.ts +1978 -0
  66. package/dist/esm/builtin/0x3.d.ts.map +1 -0
  67. package/dist/esm/builtin/0x3.js +6115 -0
  68. package/dist/esm/builtin/0x3.js.map +1 -0
  69. package/dist/esm/builtin/index.d.ts +4 -0
  70. package/dist/esm/builtin/index.d.ts.map +1 -0
  71. package/dist/esm/builtin/index.js +7 -0
  72. package/dist/esm/builtin/index.js.map +1 -0
  73. package/dist/esm/codegen/codegen.d.ts +20 -0
  74. package/dist/esm/codegen/codegen.d.ts.map +1 -0
  75. package/dist/esm/codegen/codegen.js +247 -0
  76. package/dist/esm/codegen/codegen.js.map +1 -0
  77. package/dist/esm/codegen/index.d.ts +2 -0
  78. package/dist/esm/codegen/index.d.ts.map +1 -0
  79. package/dist/esm/codegen/index.js +2 -0
  80. package/dist/esm/codegen/index.js.map +1 -0
  81. package/dist/esm/codegen/run.d.ts +3 -0
  82. package/dist/esm/codegen/run.d.ts.map +1 -0
  83. package/dist/esm/codegen/run.js +49 -0
  84. package/dist/esm/codegen/run.js.map +1 -0
  85. package/dist/esm/index.d.ts +4 -0
  86. package/dist/esm/index.d.ts.map +1 -0
  87. package/dist/esm/index.js +4 -0
  88. package/dist/esm/index.js.map +1 -0
  89. package/dist/esm/models.d.ts +18 -0
  90. package/dist/esm/models.d.ts.map +1 -0
  91. package/dist/esm/models.js +2 -0
  92. package/dist/esm/models.js.map +1 -0
  93. package/dist/esm/move-coder.d.ts +27 -0
  94. package/dist/esm/move-coder.d.ts.map +1 -0
  95. package/dist/esm/move-coder.js +268 -0
  96. package/dist/esm/move-coder.js.map +1 -0
  97. package/dist/esm/sui-chain-adapter.d.ts +15 -0
  98. package/dist/esm/sui-chain-adapter.d.ts.map +1 -0
  99. package/dist/esm/sui-chain-adapter.js +84 -0
  100. package/dist/esm/sui-chain-adapter.js.map +1 -0
  101. package/dist/esm/to-internal.d.ts +4 -0
  102. package/dist/esm/to-internal.d.ts.map +1 -0
  103. package/dist/esm/to-internal.js +96 -0
  104. package/dist/esm/to-internal.js.map +1 -0
  105. package/dist/esm/transaction.d.ts +15 -0
  106. package/dist/esm/transaction.d.ts.map +1 -0
  107. package/dist/esm/transaction.js +83 -0
  108. package/dist/esm/transaction.js.map +1 -0
  109. package/package.json +63 -0
  110. package/src/abis/0x1.json +4213 -0
  111. package/src/abis/0x2.json +26692 -0
  112. package/src/abis/0x3.json +10598 -0
  113. package/src/builtin/0x1.ts +6110 -0
  114. package/src/builtin/0x2.ts +24700 -0
  115. package/src/builtin/0x3.ts +9680 -0
  116. package/src/builtin/index.ts +6 -0
  117. package/src/codegen/codegen.ts +290 -0
  118. package/src/codegen/index.ts +1 -0
  119. package/src/codegen/run.ts +57 -0
  120. package/src/index.ts +4 -0
  121. package/src/models.ts +21 -0
  122. package/src/move-coder.ts +318 -0
  123. package/src/sui-chain-adapter.ts +113 -0
  124. package/src/tests/abis/testnet/0x1e2b124f746a339b3cf99b9f969393a96594519aafb1d06517aacfeeae20e7a5.json +265 -0
  125. package/src/tests/abis/testnet/0x6c4a21e3e7e6b6d51c4604021633e1d97e24e37a696f8c082cd48f37503e602a.json +2429 -0
  126. package/src/tests/abis/testnet/0x7f7a37c826c88bcfe9aecc042453395ddfa9df6f29cb7c97590bf86cf2b0a75e.json +1356 -0
  127. package/src/tests/abis/testnet/0xebaa2ad3eacc230f309cd933958cc52684df0a41ae7ac214d186b80f830867d2.json +10060 -0
  128. package/src/tests/types/testnet/0x1e2b124f746a339b3cf99b9f969393a96594519aafb1d06517aacfeeae20e7a5.ts +291 -0
  129. package/src/tests/types/testnet/0x6c4a21e3e7e6b6d51c4604021633e1d97e24e37a696f8c082cd48f37503e602a.ts +2468 -0
  130. package/src/tests/types/testnet/0x7f7a37c826c88bcfe9aecc042453395ddfa9df6f29cb7c97590bf86cf2b0a75e.ts +1389 -0
  131. package/src/tests/types/testnet/0xebaa2ad3eacc230f309cd933958cc52684df0a41ae7ac214d186b80f830867d2.ts +9522 -0
  132. package/src/tests/types/testnet/index.ts +7 -0
  133. package/src/to-internal.ts +127 -0
  134. 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,290 @@
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
133
+ ? this.ADDRESS_TYPE
134
+ : `${this.ADDRESS_TYPE} | TransactionObjectArgument | TransactionArgument`,
135
+ callValue: `_args.push(transactionArgumentOrObject(args[${idx}], tx))`
136
+ })
137
+ } else if (arg.isVector()) {
138
+ // TODO fix pure vector
139
+ args.push({
140
+ paramType: isView
141
+ ? `${this.ADDRESS_TYPE}[]`
142
+ : `(${this.ADDRESS_TYPE} | TransactionObjectArgument)[] | TransactionArgument`,
143
+ callValue: `_args.push(transactionArgumentOrVec(args[${idx}], tx))`
144
+ })
145
+ } else {
146
+ // Handle pure type
147
+ let pureFunction = ''
148
+ const paramType = isView
149
+ ? this.generateTypeForDescriptor(arg, module.address)
150
+ : `${this.generateTypeForDescriptor(arg, module.address)} | TransactionArgument`
151
+
152
+ switch (arg.qname.toLowerCase()) {
153
+ case 'u8':
154
+ pureFunction = `transactionArgumentOrPureU8`
155
+ break
156
+ case 'u16':
157
+ pureFunction = `transactionArgumentOrPureU16`
158
+ break
159
+ case 'u32':
160
+ pureFunction = `transactionArgumentOrPureU32`
161
+ break
162
+ case 'u64':
163
+ pureFunction = `transactionArgumentOrPureU64`
164
+ break
165
+ case 'u128':
166
+ pureFunction = `transactionArgumentOrPureU128`
167
+ break
168
+ case 'u256':
169
+ pureFunction = `transactionArgumentOrPureU256`
170
+ break
171
+ case 'bool':
172
+ pureFunction = `transactionArgumentOrPureBool`
173
+ break
174
+ case 'string':
175
+ pureFunction = `transactionArgumentOrPureString`
176
+ break
177
+ case 'address':
178
+ pureFunction = `transactionArgumentOrPureAddress`
179
+ break
180
+ // case 'vector':
181
+ // case 'option':
182
+ default:
183
+ pureFunction = `transactionArgumentOrPure`
184
+ // paramType = 'TransactionArgument'
185
+ }
186
+ const callValue = pureFunction ? `_args.push(${pureFunction}(args[${idx}], tx))` : `_args.push(args[${idx}])`
187
+
188
+ args.push({
189
+ paramType,
190
+ callValue
191
+ })
192
+ }
193
+ }
194
+ return args
195
+ }
196
+
197
+ protected generateViewFunction(module: InternalMoveModule, func: InternalMoveFunction): string {
198
+ if (func.visibility === InternalMoveFunctionVisibility.PRIVATE) {
199
+ return ''
200
+ }
201
+ const genericString = this.generateFunctionTypeParameters(func)
202
+
203
+ const typeParamArg = func.typeParams
204
+ .map((v, idx) => {
205
+ return `TypeDescriptor<T${idx}> | string`
206
+ })
207
+ .join(',')
208
+
209
+ const args = this.generateArgs(module, func, true)
210
+ const returnType = `${this.generateFunctionReturnTypeParameters(func, module.address)}`
211
+
212
+ return `export async function ${camel(normalizeToJSName(func.name))}${genericString}(
213
+ client: IotaClient,
214
+ args: [${args.map((a) => a.paramType).join(',')}],
215
+ ${
216
+ typeParamArg.length > 0 ? `typeArguments: [${typeParamArg}]` : ``
217
+ } ): Promise<TypedDevInspectResults<${returnType}>> {
218
+ const tx = new Transaction()
219
+ builder.${camel(normalizeToJSName(func.name))}(tx, args ${typeParamArg.length > 0 ? `, typeArguments` : ''})
220
+ const inspectRes = await client.devInspectTransactionBlock({
221
+ transactionBlock: tx,
222
+ sender: ZERO_ADDRESS
223
+ })
224
+
225
+ return (await getMoveCoder(client)).decodeDevInspectResult<${returnType}>(inspectRes)
226
+ }`
227
+ }
228
+
229
+ protected generateBuilderForFunction(
230
+ address: string,
231
+ module: InternalMoveModule,
232
+ func: InternalMoveFunction
233
+ ): string {
234
+ if (func.visibility === InternalMoveFunctionVisibility.PRIVATE && func.isEntry !== true) {
235
+ return ''
236
+ }
237
+
238
+ const args = this.generateArgs(module, func, false)
239
+
240
+ const genericString = this.generateFunctionTypeParameters(func)
241
+
242
+ const typeParamArg = func.typeParams
243
+ .map((v, idx) => {
244
+ return `TypeDescriptor<T${idx}> | string`
245
+ })
246
+ .join(',')
247
+ const typeParamToString = func.typeParams
248
+ .map((v, idx) => {
249
+ return `typeof typeArguments[${idx}] === 'string' ? typeArguments[${idx}] : typeArguments[${idx}].getSignature()`
250
+ })
251
+ .join(',')
252
+
253
+ return `export function ${camel(normalizeToJSName(func.name))}${genericString}(tx: Transaction,
254
+ args: [${args.map((a) => a.paramType).join(',')}],
255
+ ${typeParamArg.length > 0 ? `typeArguments: [${typeParamArg}]` : ``} ):
256
+ TransactionArgument & [ ${'TransactionArgument,'.repeat(args.length)} ] {
257
+ const _args: any[] = []
258
+ ${args.map((a) => a.callValue).join('\n')}
259
+
260
+ // @ts-ignore
261
+ return tx.moveCall({
262
+ target: "${address}::${module.name}::${func.name}",
263
+ arguments: _args,
264
+ ${typeParamArg.length > 0 ? `typeArguments: [${typeParamToString}]` : ``}
265
+ })
266
+ }`
267
+ }
268
+
269
+ generateImports(): string {
270
+ return `
271
+ ${super.generateImports()}
272
+ import { ZERO_ADDRESS, TypedDevInspectResults, getMoveCoder } from '@typemove/iota'
273
+ import { Transaction, TransactionArgument, TransactionObjectArgument } from '@iota/iota-sdk/transactions'
274
+ import { IotaClient } from '@iota/iota-sdk/client'
275
+ import { transactionArgumentOrObject,
276
+ transactionArgumentOrVec,
277
+ transactionArgumentOrPure,
278
+ transactionArgumentOrPureU8,
279
+ transactionArgumentOrPureU16,
280
+ transactionArgumentOrPureU32,
281
+ transactionArgumentOrPureU64,
282
+ transactionArgumentOrPureU128,
283
+ transactionArgumentOrPureU256,
284
+ transactionArgumentOrPureBool,
285
+ transactionArgumentOrPureString,
286
+ transactionArgumentOrPureAddress,
287
+ } from '@typemove/iota'
288
+ `
289
+ }
290
+ }
@@ -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-sui')
20
+ .description('CLI to generate typescript types from SUI 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,318 @@
1
+ import { TypedDevInspectResults, TypedEventInstance, TypedFunctionPayload } from './models.js'
2
+ import {
3
+ AbstractMoveCoder,
4
+ ANY_TYPE,
5
+ DecodedStruct,
6
+ parseMoveType,
7
+ SPLITTER,
8
+ TypeDescriptor,
9
+ InternalMoveModule
10
+ } from '@typemove/move'
11
+ import {
12
+ MoveCallIotaTransaction,
13
+ IotaCallArg,
14
+ IotaEvent,
15
+ IotaMoveNormalizedModule,
16
+ IotaMoveObject,
17
+ DevInspectResults,
18
+ IotaClient
19
+ } from '@iota/iota-sdk/client'
20
+ import { toInternalModule } from './to-internal.js'
21
+ import { IotaChainAdapter } from './sui-chain-adapter.js'
22
+ import { dynamic_field } from './builtin/0x2.js'
23
+ import { BcsType, bcs } from '@iota/iota-sdk/bcs'
24
+
25
+ // import { Encoding } from '@mysten/bcs/types', this doesn't get exported correctly
26
+ export type Encoding = 'base58' | 'base64' | 'hex'
27
+
28
+ import { normalizeIotaObjectId, normalizeIotaAddress } from '@iota/iota-sdk/utils'
29
+
30
+ export class MoveCoder extends AbstractMoveCoder<
31
+ // IotaNetwork,
32
+ IotaMoveNormalizedModule,
33
+ IotaEvent | IotaMoveObject
34
+ > {
35
+ constructor(client: IotaClient) {
36
+ super(new IotaChainAdapter(client))
37
+ }
38
+
39
+ async maybeGetMoveEnum(type: string) {
40
+ return undefined
41
+ }
42
+
43
+ load(module: IotaMoveNormalizedModule, address: string): InternalMoveModule {
44
+ let m = this.moduleMapping.get(module.address + '::' + module.name)
45
+ const mDeclared = this.moduleMapping.get(address + '::' + module.name)
46
+ if (m && mDeclared) {
47
+ return m
48
+ }
49
+ this.accounts.add(module.address)
50
+ m = toInternalModule(module)
51
+ this.loadInternal(m, address)
52
+ return m
53
+ }
54
+
55
+ protected async decode(data: any, type: TypeDescriptor): Promise<any> {
56
+ switch (type.qname) {
57
+ case '0x1::ascii::Char':
58
+ if (data !== undefined && typeof data !== 'string') {
59
+ // bcs
60
+ const byte = (await super.decode(data, type)).byte as number
61
+ return String.fromCharCode(byte)
62
+ }
63
+ case '0x1::ascii::String':
64
+ if (data !== undefined && typeof data !== 'string') {
65
+ // bcs verified
66
+ const bytes = (await super.decode(data, type)).bytes as number[]
67
+ return new TextDecoder().decode(new Uint8Array(bytes))
68
+ }
69
+ case '0x2::object::ID':
70
+ if (data !== undefined && typeof data !== 'string') {
71
+ // bcs verified
72
+ const bytes = (await super.decode(data, type)).bytes as string
73
+ return normalizeIotaObjectId(bytes)
74
+ }
75
+ case '0x2::url::Url':
76
+ if (data !== undefined && typeof data !== 'string') {
77
+ // bcs
78
+ return (await super.decode(data, type)).url
79
+ }
80
+ case '0x2::coin::Coin':
81
+ if (data !== undefined && typeof data !== 'string') {
82
+ // bcs
83
+ const bytes = (await super.decode(data, type)).id.id.bytes as number[]
84
+ return new TextDecoder().decode(new Uint8Array(bytes))
85
+ }
86
+ return data
87
+ case '0x2::balance::Balance':
88
+ if (data.value) {
89
+ // bcs verfied
90
+ const balance = await super.decode(data, type)
91
+ return balance.value
92
+ }
93
+ return BigInt(data)
94
+ case '0x1::option::Option':
95
+ if (data === null) {
96
+ return data
97
+ }
98
+ if (data.vec) {
99
+ // bcs verifed
100
+ let vec = await super.decode(data, type)
101
+ vec = vec.vec
102
+ if (vec.length === 0) {
103
+ return null
104
+ }
105
+ return vec[0]
106
+ }
107
+ return this.decode(data, type.typeArgs[0])
108
+ case 'Address':
109
+ const str = data as string
110
+ return normalizeIotaAddress(str)
111
+ case '0x1::string::String':
112
+ if (typeof data !== 'string') {
113
+ // bcs
114
+ return new TextDecoder().decode(new Uint8Array(data.bytes))
115
+ }
116
+ default:
117
+ return super.decode(data, type)
118
+ }
119
+ }
120
+
121
+ decodeEvent<T>(event: IotaEvent): Promise<TypedEventInstance<T> | undefined> {
122
+ return this.decodedStruct(event)
123
+ }
124
+ filterAndDecodeEvents<T>(type: TypeDescriptor<T> | string, resources: IotaEvent[]): Promise<TypedEventInstance<T>[]> {
125
+ if (typeof type === 'string') {
126
+ type = parseMoveType(type)
127
+ }
128
+ return this.filterAndDecodeStruct(type, resources)
129
+ }
130
+
131
+ async getDynamicFields<T1, T2>(
132
+ objects: IotaMoveObject[],
133
+ keyType: TypeDescriptor<T1> = ANY_TYPE,
134
+ valueType: TypeDescriptor<T2> = ANY_TYPE
135
+ ): Promise<dynamic_field.Field<T1, T2>[]> {
136
+ // const type = dynamic_field.Field.TYPE
137
+ // Not using the code above to avoid cycle initialize failed
138
+ const type = new TypeDescriptor<dynamic_field.Field<T1, T2>>('0x2::dynamic_field::Field')
139
+ type.typeArgs = [keyType, valueType]
140
+ const res = await this.filterAndDecodeObjects(type, objects)
141
+ return res.map((o) => o.data_decoded)
142
+ }
143
+
144
+ filterAndDecodeObjects<T>(
145
+ type: TypeDescriptor<T>,
146
+ objects: IotaMoveObject[]
147
+ ): Promise<DecodedStruct<IotaMoveObject, T>[]> {
148
+ return this.filterAndDecodeStruct(type, objects)
149
+ }
150
+
151
+ async decodeFunctionPayload(
152
+ payload: MoveCallIotaTransaction,
153
+ inputs: IotaCallArg[]
154
+ ): Promise<MoveCallIotaTransaction> {
155
+ const functionType = [payload.package, payload.module, payload.function].join(SPLITTER)
156
+ const func = await this.getMoveFunction(functionType)
157
+ const params = this.adapter.getMeaningfulFunctionParams(func.params)
158
+ const args = []
159
+ for (const value of payload.arguments || []) {
160
+ const argValue = value as any
161
+ if ('Input' in (argValue as any)) {
162
+ const idx = argValue.Input
163
+ const arg = inputs[idx]
164
+ if (arg.type === 'pure') {
165
+ args.push(arg.value)
166
+ } else if (arg.type === 'object') {
167
+ // object is not there
168
+ args.push(undefined)
169
+ } else {
170
+ console.error('unexpected function arg value')
171
+ args.push(undefined)
172
+ }
173
+ // args.push(arg) // TODO check why ts not work using arg.push(arg)
174
+ } else {
175
+ args.push(undefined)
176
+ }
177
+ }
178
+
179
+ const argumentsTyped = await this.decodeArray(args, params, false)
180
+ return {
181
+ ...payload,
182
+ arguments_decoded: argumentsTyped
183
+ } as TypedFunctionPayload<any>
184
+ }
185
+
186
+ private bcsRegistered = new Set<string>()
187
+ private bcsRegistry = new Map<string, BcsType<any>>()
188
+
189
+ private async getBCSTypeWithArgs(type: TypeDescriptor, args: BcsType<any>[] = []): Promise<BcsType<any>> {
190
+ const qname = type.qname
191
+ const sig = type.getNormalizedSignature()
192
+ const cached = this.bcsRegistry.get(sig)
193
+ if (cached) {
194
+ return cached
195
+ }
196
+ const lowerQname = qname.toLowerCase()
197
+ switch (lowerQname) {
198
+ case 'u8':
199
+ case 'u16':
200
+ case 'u32':
201
+ case 'u64':
202
+ case 'u128':
203
+ case 'u256':
204
+ case 'bool':
205
+ return bcs[lowerQname]()
206
+ case 'address':
207
+ return bcs.Address
208
+ case 'vector':
209
+ return bcs.vector(args[0])
210
+ default:
211
+ if (!qname.includes('::')) {
212
+ throw `Unimplemented builtin type ${qname}`
213
+ }
214
+ }
215
+ let moveStruct
216
+ try {
217
+ moveStruct = await this.getMoveStruct(qname)
218
+ } catch (e) {
219
+ console.error('Invalid move address', qname)
220
+ throw e
221
+ }
222
+ const structDef: Record<string, any> = {}
223
+ for (const field of moveStruct.fields) {
224
+ if (field.type.qname.startsWith('T') && args.length) {
225
+ const index = +field.type.qname.slice(1)
226
+ structDef[field.name] = args[index]
227
+ } else if (field.type.typeArgs.length && args.length) {
228
+ structDef[field.name] = await this.getBCSTypeWithArgs(field.type, args)
229
+ } else {
230
+ structDef[field.name] = await this.getBCSType(field.type)
231
+ }
232
+ }
233
+ return bcs.struct(qname, structDef)
234
+ }
235
+
236
+ async getBCSType(type: TypeDescriptor): Promise<BcsType<any>> {
237
+ const args = await Promise.all(type.typeArgs.map((x) => this.getBCSType(x)))
238
+ const bcsType = await this.getBCSTypeWithArgs(type, args)
239
+ this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
240
+ return bcsType
241
+ }
242
+
243
+ async registerBCSTypes(type: TypeDescriptor): Promise<void> {
244
+ const sig = type.getNormalizedSignature()
245
+ if (this.bcsRegistered.has(sig)) {
246
+ return
247
+ }
248
+ this.bcsRegistered.add(sig)
249
+
250
+ const bcsType = await this.getBCSType(type)
251
+ this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
252
+ }
253
+
254
+ async decodeBCS(type: TypeDescriptor, data: Uint8Array | string, encoding?: Encoding): Promise<any> {
255
+ await this.registerBCSTypes(type)
256
+ if (typeof data == 'string') {
257
+ const buf = Buffer.from(data, encoding as any)
258
+ data = new Uint8Array(buf, buf.byteOffset, buf.byteLength)
259
+ }
260
+ const bcsType = this.bcsRegistry.get(type.getNormalizedSignature())
261
+ return bcsType?.parse(data)
262
+ }
263
+
264
+ async decodeDevInspectResult<T extends any[]>(inspectRes: DevInspectResults): Promise<TypedDevInspectResults<T>> {
265
+ const returnValues = []
266
+ if (inspectRes.results != null) {
267
+ for (const r of inspectRes.results) {
268
+ if (r.returnValues) {
269
+ for (const returnValue of r.returnValues) {
270
+ const type = parseMoveType(returnValue[1])
271
+ const bcsDecoded = await this.decodeBCS(type, new Uint8Array(returnValue[0]))
272
+ const decoded = await this.decodeType(bcsDecoded, type)
273
+ returnValues.push(decoded)
274
+ }
275
+ } else {
276
+ returnValues.push(null)
277
+ }
278
+ }
279
+ }
280
+ return { ...inspectRes, results_decoded: returnValues as any }
281
+ }
282
+ }
283
+
284
+ const DEFAULT_ENDPOINT = 'https://api.mainnet.iota.cafe/'
285
+ const CODER_MAP = new Map<string, MoveCoder>()
286
+ const CHAIN_ID_CODER_MAP = new Map<string, MoveCoder>()
287
+
288
+ export function defaultMoveCoder(endpoint: string = DEFAULT_ENDPOINT): MoveCoder {
289
+ let coder = CODER_MAP.get(endpoint)
290
+ if (!coder) {
291
+ coder = new MoveCoder(new IotaClient({ url: endpoint }))
292
+ CODER_MAP.set(endpoint, coder)
293
+ }
294
+ return coder
295
+ }
296
+
297
+ const PROVIDER_CODER_MAP = new Map<IotaClient, MoveCoder>()
298
+
299
+ let DEFAULT_CHAIN_ID: string | undefined
300
+
301
+ export async function getMoveCoder(client: IotaClient): Promise<MoveCoder> {
302
+ let coder = PROVIDER_CODER_MAP.get(client)
303
+ if (!coder) {
304
+ coder = new MoveCoder(client)
305
+ // TODO how to dedup
306
+ const id = await client.getChainIdentifier()
307
+ const defaultCoder = defaultMoveCoder()
308
+ if (!DEFAULT_CHAIN_ID) {
309
+ DEFAULT_CHAIN_ID = await defaultCoder.adapter.getChainId()
310
+ }
311
+ if (id === DEFAULT_CHAIN_ID) {
312
+ coder = defaultCoder
313
+ }
314
+
315
+ PROVIDER_CODER_MAP.set(client, coder)
316
+ }
317
+ return coder
318
+ }