@typemove/sui 2.0.0 → 2.0.1-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.
- package/dist/esm/builtin/0x1.d.ts +1030 -0
- package/dist/esm/builtin/0x1.d.ts.map +1 -0
- package/dist/esm/builtin/0x1.js +7799 -0
- package/dist/esm/builtin/0x1.js.map +1 -0
- package/dist/esm/builtin/0x2.d.ts +4766 -0
- package/dist/esm/builtin/0x2.d.ts.map +1 -0
- package/dist/esm/builtin/0x2.js +23180 -0
- package/dist/esm/builtin/0x2.js.map +1 -0
- package/dist/esm/builtin/0x3.d.ts +1964 -0
- package/dist/esm/builtin/0x3.d.ts.map +1 -0
- package/dist/esm/builtin/0x3.js +7606 -0
- package/dist/esm/builtin/0x3.js.map +1 -0
- package/dist/esm/builtin/index.d.ts +4 -0
- package/dist/esm/builtin/index.d.ts.map +1 -0
- package/dist/esm/builtin/index.js +7 -0
- package/dist/esm/builtin/index.js.map +1 -0
- package/dist/esm/codegen/codegen.d.ts +20 -0
- package/dist/esm/codegen/codegen.d.ts.map +1 -0
- package/dist/esm/codegen/codegen.js +247 -0
- package/dist/esm/codegen/codegen.js.map +1 -0
- package/dist/esm/codegen/index.d.ts +2 -0
- package/dist/esm/codegen/index.d.ts.map +1 -0
- package/dist/esm/codegen/index.js +2 -0
- package/dist/esm/codegen/index.js.map +1 -0
- package/dist/esm/codegen/run.d.ts +3 -0
- package/dist/esm/codegen/run.d.ts.map +1 -0
- package/dist/esm/codegen/run.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/models.d.ts +21 -0
- package/dist/esm/models.d.ts.map +1 -0
- package/dist/esm/models.js +2 -0
- package/dist/esm/models.js.map +1 -0
- package/dist/esm/move-coder.d.ts +28 -0
- package/dist/esm/move-coder.d.ts.map +1 -0
- package/dist/esm/move-coder.js +311 -0
- package/dist/esm/move-coder.js.map +1 -0
- package/dist/esm/sui-chain-adapter.d.ts +28 -0
- package/dist/esm/sui-chain-adapter.d.ts.map +1 -0
- package/dist/esm/sui-chain-adapter.js +109 -0
- package/dist/esm/sui-chain-adapter.js.map +1 -0
- package/dist/esm/to-internal.d.ts +6 -0
- package/dist/esm/to-internal.d.ts.map +1 -0
- package/dist/esm/to-internal.js +170 -0
- package/dist/esm/to-internal.js.map +1 -0
- package/dist/esm/transaction.d.ts +15 -0
- package/dist/esm/transaction.d.ts.map +1 -0
- package/dist/esm/transaction.js +83 -0
- package/dist/esm/transaction.js.map +1 -0
- package/package.json +6 -6
- package/src/abis/0x1.json +9096 -0
- package/src/abis/0x2.json +38512 -0
- package/src/abis/0x3.json +14755 -0
- package/src/builtin/0x1.ts +10657 -0
- package/src/builtin/0x2.ts +33567 -0
- package/src/builtin/0x3.ts +11437 -0
- package/src/builtin/index.ts +6 -0
- package/src/codegen/codegen.ts +292 -0
- package/src/codegen/index.ts +1 -0
- package/src/codegen/run.ts +57 -0
- package/src/index.ts +13 -0
- package/src/models.ts +32 -0
- package/src/move-coder.ts +364 -0
- package/src/sui-chain-adapter.ts +142 -0
- package/src/to-internal.ts +201 -0
- package/src/transaction.ts +127 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { TypedSimulateResults, 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 { SuiGrpcClient } from '@mysten/sui/grpc'
|
|
13
|
+
import type { GrpcTypes } from '@mysten/sui/grpc'
|
|
14
|
+
import {
|
|
15
|
+
SuiChainAdapter,
|
|
16
|
+
ModuleWithAddress,
|
|
17
|
+
getGrpcClient,
|
|
18
|
+
getGrpcFullnodeUrl,
|
|
19
|
+
SuiEventInput,
|
|
20
|
+
SuiMoveObjectInput
|
|
21
|
+
} from './sui-chain-adapter.js'
|
|
22
|
+
import { toInternalModule } from './to-internal.js'
|
|
23
|
+
import { dynamic_field } from './builtin/0x2.js'
|
|
24
|
+
import { BcsType, bcs } from '@mysten/sui/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 { normalizeSuiObjectId, normalizeSuiAddress } from '@mysten/sui/utils'
|
|
30
|
+
|
|
31
|
+
export class MoveCoder extends AbstractMoveCoder<ModuleWithAddress, SuiEventInput | SuiMoveObjectInput> {
|
|
32
|
+
constructor(client: SuiGrpcClient) {
|
|
33
|
+
super(new SuiChainAdapter(client))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
load(entry: ModuleWithAddress, address: string): InternalMoveModule {
|
|
37
|
+
address = accountTypeString(address)
|
|
38
|
+
const { address: moduleAddress, module } = entry
|
|
39
|
+
let m = this.moduleMapping.get(moduleAddress + '::' + module.name)
|
|
40
|
+
const mDeclared = this.moduleMapping.get(address + '::' + module.name)
|
|
41
|
+
if (m && mDeclared) {
|
|
42
|
+
return m
|
|
43
|
+
}
|
|
44
|
+
this.accounts.add(moduleAddress)
|
|
45
|
+
m = toInternalModule(module, moduleAddress)
|
|
46
|
+
this.loadInternal(m, address)
|
|
47
|
+
return m
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected async decode(data: any, type: TypeDescriptor): Promise<any> {
|
|
51
|
+
// UID handled before the switch to avoid the existing fall-through chain
|
|
52
|
+
// (case '0x2::url::Url' / '0x2::coin::Coin' bodies fire on non-string
|
|
53
|
+
// inputs and corrupt UID output). gRPC's unified Object.json flattens UID
|
|
54
|
+
// to a bare address string — re-wrap to `{ id: '0x<32-byte>' }` so
|
|
55
|
+
// downstream `.id.id`-style accessors keep working. The BCS-shaped path
|
|
56
|
+
// (`{ id: { bytes: Uint8Array(32) } }`) is delegated to super.decode,
|
|
57
|
+
// which then hits the inner ID case.
|
|
58
|
+
if (type.qname === '0x2::object::UID') {
|
|
59
|
+
if (typeof data === 'string') {
|
|
60
|
+
return { id: normalizeSuiObjectId(data) } as any
|
|
61
|
+
}
|
|
62
|
+
return super.decode(data, type)
|
|
63
|
+
}
|
|
64
|
+
switch (type.qname) {
|
|
65
|
+
case '0x1::ascii::Char':
|
|
66
|
+
if (data !== undefined && typeof data !== 'string') {
|
|
67
|
+
// bcs
|
|
68
|
+
const byte = (await super.decode(data, type)).byte as number
|
|
69
|
+
return String.fromCharCode(byte)
|
|
70
|
+
}
|
|
71
|
+
case '0x1::ascii::String':
|
|
72
|
+
if (data !== undefined && typeof data !== 'string') {
|
|
73
|
+
// bcs verified
|
|
74
|
+
const bytes = (await super.decode(data, type)).bytes as number[]
|
|
75
|
+
return new TextDecoder().decode(new Uint8Array(bytes))
|
|
76
|
+
}
|
|
77
|
+
case '0x2::object::ID':
|
|
78
|
+
if (data !== undefined && typeof data !== 'string') {
|
|
79
|
+
// bcs verified
|
|
80
|
+
const bytes = (await super.decode(data, type)).bytes as string
|
|
81
|
+
return normalizeSuiObjectId(bytes)
|
|
82
|
+
}
|
|
83
|
+
case '0x2::url::Url':
|
|
84
|
+
if (data !== undefined && typeof data !== 'string') {
|
|
85
|
+
// bcs
|
|
86
|
+
return (await super.decode(data, type)).url
|
|
87
|
+
}
|
|
88
|
+
case '0x2::coin::Coin':
|
|
89
|
+
if (data !== undefined && typeof data !== 'string') {
|
|
90
|
+
// bcs
|
|
91
|
+
const bytes = (await super.decode(data, type)).id.id.bytes as number[]
|
|
92
|
+
return new TextDecoder().decode(new Uint8Array(bytes))
|
|
93
|
+
}
|
|
94
|
+
return data
|
|
95
|
+
case '0x2::balance::Balance':
|
|
96
|
+
if (data.value) {
|
|
97
|
+
// bcs verfied
|
|
98
|
+
const balance = await super.decode(data, type)
|
|
99
|
+
return balance.value
|
|
100
|
+
}
|
|
101
|
+
return BigInt(data)
|
|
102
|
+
case '0x1::option::Option':
|
|
103
|
+
if (data === null) {
|
|
104
|
+
return data
|
|
105
|
+
}
|
|
106
|
+
if (data.vec) {
|
|
107
|
+
// bcs verifed
|
|
108
|
+
let vec = await super.decode(data, type)
|
|
109
|
+
vec = vec.vec
|
|
110
|
+
if (vec.length === 0) {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
return vec[0]
|
|
114
|
+
}
|
|
115
|
+
return this.decode(data, type.typeArgs[0])
|
|
116
|
+
case 'Address':
|
|
117
|
+
const str = data as string
|
|
118
|
+
return normalizeSuiAddress(str)
|
|
119
|
+
case '0x1::string::String':
|
|
120
|
+
if (typeof data !== 'string') {
|
|
121
|
+
// bcs
|
|
122
|
+
return new TextDecoder().decode(new Uint8Array(data.bytes))
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
return super.decode(data, type)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
decodeEvent<T>(event: SuiEventInput): Promise<TypedEventInstance<T> | undefined> {
|
|
130
|
+
return this.decodedStruct(event)
|
|
131
|
+
}
|
|
132
|
+
filterAndDecodeEvents<T>(
|
|
133
|
+
type: TypeDescriptor<T> | string,
|
|
134
|
+
resources: SuiEventInput[]
|
|
135
|
+
): Promise<TypedEventInstance<T>[]> {
|
|
136
|
+
if (typeof type === 'string') {
|
|
137
|
+
type = parseMoveType(type)
|
|
138
|
+
}
|
|
139
|
+
return this.filterAndDecodeStruct(type, resources)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getDynamicFields<T1, T2>(
|
|
143
|
+
objects: SuiMoveObjectInput[],
|
|
144
|
+
keyType: TypeDescriptor<T1> = ANY_TYPE,
|
|
145
|
+
valueType: TypeDescriptor<T2> = ANY_TYPE
|
|
146
|
+
): Promise<dynamic_field.Field<T1, T2>[]> {
|
|
147
|
+
const type = new TypeDescriptor<dynamic_field.Field<T1, T2>>('0x2::dynamic_field::Field')
|
|
148
|
+
type.typeArgs = [keyType, valueType]
|
|
149
|
+
const res = await this.filterAndDecodeObjects(type, objects)
|
|
150
|
+
return res.map((o) => o.data_decoded)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
filterAndDecodeObjects<T>(
|
|
154
|
+
type: TypeDescriptor<T>,
|
|
155
|
+
objects: SuiMoveObjectInput[]
|
|
156
|
+
): Promise<DecodedStruct<SuiMoveObjectInput, T>[]> {
|
|
157
|
+
return this.filterAndDecodeStruct(type, objects)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Decodes a parsed Move call payload against the loaded module ABI.
|
|
161
|
+
// Inputs are gRPC `Input[]`: kind=PURE carries a Uint8Array of BCS bytes
|
|
162
|
+
// that we decode using the corresponding parameter's Move type;
|
|
163
|
+
// object inputs (IMMUTABLE_OR_OWNED / SHARED / receiving) are surfaced as
|
|
164
|
+
// undefined since their on-chain values aren't part of the payload.
|
|
165
|
+
async decodeFunctionPayload(payload: GrpcTypes.MoveCall, inputs: GrpcTypes.Input[]): Promise<any> {
|
|
166
|
+
const functionType = [payload.package, payload.module, payload.function].join(SPLITTER)
|
|
167
|
+
const func = await this.getMoveFunction(functionType)
|
|
168
|
+
const params = this.adapter.getMeaningfulFunctionParams(func.params)
|
|
169
|
+
const args: any[] = []
|
|
170
|
+
for (const value of payload.arguments ?? []) {
|
|
171
|
+
const av = value as any
|
|
172
|
+
const idx: number | undefined = av?.input
|
|
173
|
+
if (idx == null || idx < 0 || idx >= inputs.length) {
|
|
174
|
+
args.push(undefined)
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
const arg = inputs[idx]
|
|
178
|
+
if (arg?.pure) {
|
|
179
|
+
const paramType: TypeDescriptor | undefined = params[args.length]
|
|
180
|
+
try {
|
|
181
|
+
const bytes = arg.pure instanceof Uint8Array ? arg.pure : new Uint8Array(arg.pure)
|
|
182
|
+
const decoded: any = paramType ? await this.decodeBCS(paramType, bytes) : bytes
|
|
183
|
+
args.push(decoded)
|
|
184
|
+
} catch {
|
|
185
|
+
args.push(undefined)
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
// Object inputs (and unknown kinds) — value isn't carried in the payload.
|
|
189
|
+
args.push(undefined)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const argumentsTyped = await this.decodeArray(args, params, false)
|
|
194
|
+
return {
|
|
195
|
+
...payload,
|
|
196
|
+
arguments_decoded: argumentsTyped
|
|
197
|
+
} as TypedFunctionPayload<any>
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private bcsRegistered = new Set<string>()
|
|
201
|
+
private bcsRegistry = new Map<string, BcsType<any>>()
|
|
202
|
+
|
|
203
|
+
private async getBCSTypeWithArgs(type: TypeDescriptor, args: BcsType<any>[] = []): Promise<BcsType<any>> {
|
|
204
|
+
const qname = type.qname
|
|
205
|
+
const sig = type.getNormalizedSignature()
|
|
206
|
+
const cached = this.bcsRegistry.get(sig)
|
|
207
|
+
if (cached) {
|
|
208
|
+
return cached
|
|
209
|
+
}
|
|
210
|
+
const lowerQname = qname.toLowerCase()
|
|
211
|
+
switch (lowerQname) {
|
|
212
|
+
case 'u8':
|
|
213
|
+
case 'u16':
|
|
214
|
+
case 'u32':
|
|
215
|
+
case 'u64':
|
|
216
|
+
case 'u128':
|
|
217
|
+
case 'u256':
|
|
218
|
+
case 'bool':
|
|
219
|
+
return bcs[lowerQname]()
|
|
220
|
+
case 'address':
|
|
221
|
+
return bcs.Address
|
|
222
|
+
case 'vector':
|
|
223
|
+
return bcs.vector(args[0])
|
|
224
|
+
default:
|
|
225
|
+
if (!qname.includes('::')) {
|
|
226
|
+
throw `Unimplemented builtin type ${qname}`
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
let moveStruct
|
|
230
|
+
try {
|
|
231
|
+
moveStruct = await this.getMoveStruct(qname)
|
|
232
|
+
} catch (e) {
|
|
233
|
+
console.error('Invalid move address', qname)
|
|
234
|
+
throw e
|
|
235
|
+
}
|
|
236
|
+
const structDef: Record<string, any> = {}
|
|
237
|
+
for (const field of moveStruct.fields) {
|
|
238
|
+
if (field.type.qname.startsWith('T') && args.length) {
|
|
239
|
+
const index = +field.type.qname.slice(1)
|
|
240
|
+
structDef[field.name] = args[index]
|
|
241
|
+
} else if (field.type.typeArgs.length && args.length) {
|
|
242
|
+
structDef[field.name] = await this.getBCSTypeWithArgs(field.type, args)
|
|
243
|
+
} else {
|
|
244
|
+
structDef[field.name] = await this.getBCSType(field.type)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return bcs.struct(qname, structDef)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async getBCSType(type: TypeDescriptor): Promise<BcsType<any>> {
|
|
251
|
+
const args = await Promise.all(type.typeArgs.map((x) => this.getBCSType(x)))
|
|
252
|
+
const bcsType = await this.getBCSTypeWithArgs(type, args)
|
|
253
|
+
this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
|
|
254
|
+
return bcsType
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async registerBCSTypes(type: TypeDescriptor): Promise<void> {
|
|
258
|
+
const sig = type.getNormalizedSignature()
|
|
259
|
+
if (this.bcsRegistered.has(sig)) {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
this.bcsRegistered.add(sig)
|
|
263
|
+
|
|
264
|
+
const bcsType = await this.getBCSType(type)
|
|
265
|
+
this.bcsRegistry.set(type.getNormalizedSignature(), bcsType)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async decodeBCS(type: TypeDescriptor, data: Uint8Array | string, encoding?: Encoding): Promise<any> {
|
|
269
|
+
await this.registerBCSTypes(type)
|
|
270
|
+
if (typeof data == 'string') {
|
|
271
|
+
const buf = Buffer.from(data, encoding as any)
|
|
272
|
+
data = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
273
|
+
}
|
|
274
|
+
const bcsType = this.bcsRegistry.get(type.getNormalizedSignature())
|
|
275
|
+
return bcsType?.parse(data)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Replaces decodeDevInspectResult. gRPC simulateTransaction returns
|
|
279
|
+
// `commandResults: CommandResult[]` where each CommandResult.returnValues is
|
|
280
|
+
// `CommandOutput[]` of raw `{ bcs: Uint8Array }` — there is no per-return
|
|
281
|
+
// type string the way devInspect carried one. The caller must therefore pass
|
|
282
|
+
// the Move return-type signatures explicitly (codegen emits them next to
|
|
283
|
+
// each generated view helper).
|
|
284
|
+
async decodeSimulateResult<T extends any[]>(
|
|
285
|
+
simulateRes: any,
|
|
286
|
+
returnTypeSignatures: string[],
|
|
287
|
+
typeArguments?: (string | TypeDescriptor)[]
|
|
288
|
+
): Promise<TypedSimulateResults<T>> {
|
|
289
|
+
// Generic view helpers embed return signatures like "T0" / "vector<T1>"
|
|
290
|
+
// at codegen time, but the caller supplies concrete type arguments only
|
|
291
|
+
// at runtime. Substitute them in before BCS decoding — otherwise the BCS
|
|
292
|
+
// type registry sees the bare type-parameter qname and rejects it as an
|
|
293
|
+
// unimplemented builtin.
|
|
294
|
+
const ctx = new Map<string, TypeDescriptor>()
|
|
295
|
+
if (typeArguments && typeArguments.length > 0) {
|
|
296
|
+
typeArguments.forEach((t, i) => {
|
|
297
|
+
ctx.set('T' + i, typeof t === 'string' ? parseMoveType(t) : t)
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const returnValues: any[] = []
|
|
302
|
+
const commandResults = simulateRes?.commandResults
|
|
303
|
+
if (Array.isArray(commandResults)) {
|
|
304
|
+
let typeIdx = 0
|
|
305
|
+
for (const r of commandResults) {
|
|
306
|
+
const rvs = r?.returnValues
|
|
307
|
+
if (Array.isArray(rvs) && rvs.length > 0) {
|
|
308
|
+
for (const rv of rvs) {
|
|
309
|
+
const sig = returnTypeSignatures[typeIdx++]
|
|
310
|
+
if (!sig) {
|
|
311
|
+
returnValues.push(null)
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
314
|
+
let type = parseMoveType(sig)
|
|
315
|
+
if (ctx.size > 0) {
|
|
316
|
+
type = type.applyTypeArgs(ctx)
|
|
317
|
+
}
|
|
318
|
+
const bcsBytes = rv?.bcs instanceof Uint8Array ? rv.bcs : new Uint8Array(rv?.bcs ?? [])
|
|
319
|
+
const bcsDecoded = await this.decodeBCS(type, bcsBytes)
|
|
320
|
+
const decoded = await this.decodeType(bcsDecoded, type)
|
|
321
|
+
returnValues.push(decoded)
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
returnValues.push(null)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { ...simulateRes, results_decoded: returnValues as any }
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const DEFAULT_ENDPOINT = getGrpcFullnodeUrl('mainnet')
|
|
333
|
+
const CODER_MAP = new Map<string, MoveCoder>()
|
|
334
|
+
|
|
335
|
+
export function defaultMoveCoder(endpoint: string = DEFAULT_ENDPOINT): MoveCoder {
|
|
336
|
+
let coder = CODER_MAP.get(endpoint)
|
|
337
|
+
if (!coder) {
|
|
338
|
+
coder = new MoveCoder(getGrpcClient(endpoint))
|
|
339
|
+
CODER_MAP.set(endpoint, coder)
|
|
340
|
+
}
|
|
341
|
+
return coder
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const PROVIDER_CODER_MAP = new Map<SuiGrpcClient, MoveCoder>()
|
|
345
|
+
|
|
346
|
+
let DEFAULT_CHAIN_ID: string | undefined
|
|
347
|
+
|
|
348
|
+
export async function getMoveCoder(client: SuiGrpcClient): Promise<MoveCoder> {
|
|
349
|
+
let coder = PROVIDER_CODER_MAP.get(client)
|
|
350
|
+
if (!coder) {
|
|
351
|
+
coder = new MoveCoder(client)
|
|
352
|
+
const { chainIdentifier } = await client.core.getChainIdentifier()
|
|
353
|
+
const defaultCoder = defaultMoveCoder()
|
|
354
|
+
if (!DEFAULT_CHAIN_ID) {
|
|
355
|
+
DEFAULT_CHAIN_ID = await defaultCoder.adapter.getChainId()
|
|
356
|
+
}
|
|
357
|
+
if (chainIdentifier === DEFAULT_CHAIN_ID) {
|
|
358
|
+
coder = defaultCoder
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
PROVIDER_CODER_MAP.set(client, coder)
|
|
362
|
+
}
|
|
363
|
+
return coder
|
|
364
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { toInternalModule } from './to-internal.js'
|
|
2
|
+
import {
|
|
3
|
+
InternalMoveModule,
|
|
4
|
+
InternalMoveStruct,
|
|
5
|
+
ChainAdapter,
|
|
6
|
+
moduleQname,
|
|
7
|
+
SPLITTER,
|
|
8
|
+
TypeDescriptor
|
|
9
|
+
} from '@typemove/move'
|
|
10
|
+
|
|
11
|
+
import type { GrpcTypes } from '@mysten/sui/grpc'
|
|
12
|
+
import { SuiGrpcClient } from '@mysten/sui/grpc'
|
|
13
|
+
import type { SuiClientTypes } from '@mysten/sui/client'
|
|
14
|
+
|
|
15
|
+
// Decoder-input types now use @mysten/sui/client's unified SuiClientTypes —
|
|
16
|
+
// the same shapes returned by SuiGrpcClient (and that the GraphQL/JSON-RPC
|
|
17
|
+
// clients implement). Per the SDK's own warnings, the `.json` field's exact
|
|
18
|
+
// field names may vary between transports for some payloads; downstream code
|
|
19
|
+
// reading `getData` should treat it as opaque object data.
|
|
20
|
+
export type SuiEventInput = SuiClientTypes.Event
|
|
21
|
+
export type SuiMoveObjectInput = SuiClientTypes.Object<{ json: true }>
|
|
22
|
+
|
|
23
|
+
// Adapter ModuleType is the proto Module plus the package address (which the
|
|
24
|
+
// proto doesn't carry per-entry — only the wrapping Package has storageId).
|
|
25
|
+
export interface ModuleWithAddress {
|
|
26
|
+
address: string
|
|
27
|
+
module: GrpcTypes.Module
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class SuiChainAdapter extends ChainAdapter<ModuleWithAddress, SuiEventInput | SuiMoveObjectInput> {
|
|
31
|
+
client: SuiGrpcClient
|
|
32
|
+
|
|
33
|
+
constructor(client: SuiGrpcClient) {
|
|
34
|
+
super()
|
|
35
|
+
this.client = client
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async getChainId(): Promise<string> {
|
|
39
|
+
const { chainIdentifier } = await this.client.core.getChainIdentifier()
|
|
40
|
+
return chainIdentifier ?? ''
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async fetchModule(account: string, module: string): Promise<ModuleWithAddress> {
|
|
44
|
+
// gRPC has no single-module-fetch RPC; pull the whole package and filter.
|
|
45
|
+
const modules = await this.fetchModules(account)
|
|
46
|
+
const m = modules.find((x) => x.module.name === module)
|
|
47
|
+
if (!m) {
|
|
48
|
+
throw Error(`Module ${module} not found in package ${account}`)
|
|
49
|
+
}
|
|
50
|
+
return m
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async fetchModules(account: string): Promise<ModuleWithAddress[]> {
|
|
54
|
+
const { response } = await this.client.movePackageService.getPackage({ packageId: account })
|
|
55
|
+
const pkg = response.package
|
|
56
|
+
if (!pkg) {
|
|
57
|
+
throw Error(`No package returned for ${account}`)
|
|
58
|
+
}
|
|
59
|
+
// gRPC returns the canonical long-form storage id (0x0000...0002); the rest
|
|
60
|
+
// of typemove keys system packages by their short form (0x2). Preserve the
|
|
61
|
+
// caller-supplied address so framework lookups and short-form import paths
|
|
62
|
+
// (`@typemove/sui/builtin/0x2`) keep working.
|
|
63
|
+
return (pkg.modules ?? []).map((module) => ({ address: account, module }))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getMeaningfulFunctionParams(params: TypeDescriptor[]): TypeDescriptor[] {
|
|
67
|
+
return params
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
toInternalModules(modules: ModuleWithAddress[]): InternalMoveModule[] {
|
|
71
|
+
return modules.map(({ address, module }) => toInternalModule(module, address))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getAllEventStructs(modules: InternalMoveModule[]): Map<string, InternalMoveStruct> {
|
|
75
|
+
const eventMap = new Map<string, InternalMoveStruct>()
|
|
76
|
+
for (const module of modules) {
|
|
77
|
+
const qname = moduleQname(module)
|
|
78
|
+
for (const struct of module.structs) {
|
|
79
|
+
const abilities = new Set(struct.abilities)
|
|
80
|
+
if (abilities.has('Drop') && abilities.has('Copy')) {
|
|
81
|
+
eventMap.set(qname + SPLITTER + struct.name, struct)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return eventMap
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getType(base: SuiEventInput | SuiMoveObjectInput): string {
|
|
89
|
+
// Unified SuiClientTypes: Event has `eventType`, Object has `type`.
|
|
90
|
+
const v = base as any
|
|
91
|
+
return v.eventType ?? v.type ?? ''
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getData(val: SuiEventInput | SuiMoveObjectInput) {
|
|
95
|
+
if (val === undefined) {
|
|
96
|
+
throw Error('val is undefined')
|
|
97
|
+
}
|
|
98
|
+
// Pass through primitives (e.g. a UID flattened to a bare string) — the
|
|
99
|
+
// decoder's per-type case handlers know how to interpret them.
|
|
100
|
+
if (val === null || typeof val !== 'object') {
|
|
101
|
+
return val as any
|
|
102
|
+
}
|
|
103
|
+
const v = val as any
|
|
104
|
+
// Unified SuiClientTypes shapes: Event.json / Object<{json:true}>.json
|
|
105
|
+
// carries the decoded Move struct content. Anything else (a nested
|
|
106
|
+
// already-flat struct value passed in during recursion) is its own data.
|
|
107
|
+
if (v.json != null) {
|
|
108
|
+
return v.json as any
|
|
109
|
+
}
|
|
110
|
+
return val as any
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function inferNetworkFromUrl(url: string): string {
|
|
115
|
+
if (url.includes('mainnet')) return 'mainnet'
|
|
116
|
+
if (url.includes('testnet')) return 'testnet'
|
|
117
|
+
if (url.includes('devnet')) return 'devnet'
|
|
118
|
+
if (url.includes('localnet') || url.includes('127.0.0.1') || url.includes('localhost')) return 'localnet'
|
|
119
|
+
return 'custom'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// gRPC counterpart of @mysten/sui/jsonRpc's `getJsonRpcFullnodeUrl`. The SDK
|
|
123
|
+
// doesn't ship one under /grpc — Sui's gRPC-Web endpoint is exposed on the
|
|
124
|
+
// same host as JSON-RPC over standard HTTPS, so we mirror the JSON-RPC list
|
|
125
|
+
// (sans the explicit :443 since the transport speaks HTTPS by default).
|
|
126
|
+
export function getGrpcFullnodeUrl(network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'): string {
|
|
127
|
+
switch (network) {
|
|
128
|
+
case 'mainnet':
|
|
129
|
+
return 'https://fullnode.mainnet.sui.io'
|
|
130
|
+
case 'testnet':
|
|
131
|
+
return 'https://fullnode.testnet.sui.io'
|
|
132
|
+
case 'devnet':
|
|
133
|
+
return 'https://fullnode.devnet.sui.io'
|
|
134
|
+
case 'localnet':
|
|
135
|
+
return 'http://127.0.0.1:9000'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getGrpcClient(endpoint: string): SuiGrpcClient {
|
|
140
|
+
const network = inferNetworkFromUrl(endpoint) as any
|
|
141
|
+
return new SuiGrpcClient({ network, baseUrl: endpoint })
|
|
142
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { GrpcTypes } from '@mysten/sui/grpc'
|
|
2
|
+
import {
|
|
3
|
+
accountTypeString,
|
|
4
|
+
InternalMoveEnum,
|
|
5
|
+
InternalMoveFunction,
|
|
6
|
+
InternalMoveFunctionVisibility,
|
|
7
|
+
InternalMoveModule,
|
|
8
|
+
InternalMoveStruct,
|
|
9
|
+
InternalMoveStructField,
|
|
10
|
+
SPLITTER,
|
|
11
|
+
TypeDescriptor
|
|
12
|
+
} from '@typemove/move'
|
|
13
|
+
|
|
14
|
+
// Re-exports for convenience: callers used to import named types from
|
|
15
|
+
// '@mysten/sui/jsonRpc'; now they come from the gRPC proto namespace.
|
|
16
|
+
type Module = GrpcTypes.Module
|
|
17
|
+
type DatatypeDescriptor = GrpcTypes.DatatypeDescriptor
|
|
18
|
+
type FieldDescriptor = GrpcTypes.FieldDescriptor
|
|
19
|
+
type FunctionDescriptor = GrpcTypes.FunctionDescriptor
|
|
20
|
+
type OpenSignature = GrpcTypes.OpenSignature
|
|
21
|
+
type OpenSignatureBody = GrpcTypes.OpenSignatureBody
|
|
22
|
+
|
|
23
|
+
// Proto enum constants kept as literals so we can compare against
|
|
24
|
+
// the ordinals returned in the wire-decoded shape without importing
|
|
25
|
+
// the namespaced enum at runtime.
|
|
26
|
+
const DATATYPE_KIND_STRUCT = 1
|
|
27
|
+
const DATATYPE_KIND_ENUM = 2
|
|
28
|
+
|
|
29
|
+
const VISIBILITY_PRIVATE = 1
|
|
30
|
+
const VISIBILITY_PUBLIC = 2
|
|
31
|
+
const VISIBILITY_FRIEND = 3
|
|
32
|
+
|
|
33
|
+
// OpenSignatureBody.Type
|
|
34
|
+
const TYPE_ADDRESS = 1
|
|
35
|
+
const TYPE_BOOL = 2
|
|
36
|
+
const TYPE_U8 = 3
|
|
37
|
+
const TYPE_U16 = 4
|
|
38
|
+
const TYPE_U32 = 5
|
|
39
|
+
const TYPE_U64 = 6
|
|
40
|
+
const TYPE_U128 = 7
|
|
41
|
+
const TYPE_U256 = 8
|
|
42
|
+
const TYPE_VECTOR = 9
|
|
43
|
+
const TYPE_DATATYPE = 10
|
|
44
|
+
const TYPE_TYPE_PARAMETER = 11
|
|
45
|
+
|
|
46
|
+
const ABILITY_NAMES: Record<number, string> = {
|
|
47
|
+
1: 'Copy',
|
|
48
|
+
2: 'Drop',
|
|
49
|
+
3: 'Store',
|
|
50
|
+
4: 'Key'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Convert a proto Module (from movePackageService.getPackage().package.modules[i])
|
|
54
|
+
// into typemove's InternalMoveModule. `packageAddress` is needed because the
|
|
55
|
+
// proto Module doesn't repeat the package id on every module entry.
|
|
56
|
+
export function toInternalModule(module: Module, packageAddress: string): InternalMoveModule {
|
|
57
|
+
packageAddress = accountTypeString(packageAddress)
|
|
58
|
+
const datatypes = module.datatypes ?? []
|
|
59
|
+
const functions = module.functions ?? []
|
|
60
|
+
|
|
61
|
+
const structs: InternalMoveStruct[] = datatypes.filter((d) => d.kind === DATATYPE_KIND_STRUCT).map(toInternalStruct)
|
|
62
|
+
|
|
63
|
+
const enums: InternalMoveEnum[] = datatypes.filter((d) => d.kind === DATATYPE_KIND_ENUM).map(toInternalEnum)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
address: packageAddress,
|
|
67
|
+
name: module.name ?? '',
|
|
68
|
+
exposedFunctions: functions.map(toInternalFunction),
|
|
69
|
+
structs,
|
|
70
|
+
enums
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function toInternalFunction(func: FunctionDescriptor): InternalMoveFunction {
|
|
75
|
+
let visibility: InternalMoveFunctionVisibility
|
|
76
|
+
switch (func.visibility) {
|
|
77
|
+
case VISIBILITY_PRIVATE:
|
|
78
|
+
visibility = InternalMoveFunctionVisibility.PRIVATE
|
|
79
|
+
break
|
|
80
|
+
case VISIBILITY_PUBLIC:
|
|
81
|
+
visibility = InternalMoveFunctionVisibility.PUBLIC
|
|
82
|
+
break
|
|
83
|
+
case VISIBILITY_FRIEND:
|
|
84
|
+
visibility = InternalMoveFunctionVisibility.FRIEND
|
|
85
|
+
break
|
|
86
|
+
default:
|
|
87
|
+
throw Error(`No visibility for function ${func.name}`)
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
typeParams: (func.typeParameters ?? []).map((p) => ({
|
|
91
|
+
constraints: (p.constraints ?? []).map(abilityName)
|
|
92
|
+
})),
|
|
93
|
+
isEntry: func.isEntry ?? false,
|
|
94
|
+
name: func.name ?? '',
|
|
95
|
+
params: (func.parameters ?? []).map(toTypeDescriptorFromSignature),
|
|
96
|
+
return: (func.returns ?? []).map(toTypeDescriptorFromSignature),
|
|
97
|
+
visibility
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function toInternalStruct(d: DatatypeDescriptor): InternalMoveStruct {
|
|
102
|
+
return {
|
|
103
|
+
abilities: (d.abilities ?? []).map(abilityName),
|
|
104
|
+
fields: (d.fields ?? []).map(toInternalField),
|
|
105
|
+
typeParams: (d.typeParameters ?? []).map((p) => ({
|
|
106
|
+
constraints: (p.constraints ?? []).map(abilityName)
|
|
107
|
+
})),
|
|
108
|
+
isNative: false,
|
|
109
|
+
isEvent: false,
|
|
110
|
+
name: d.name ?? ''
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function toInternalEnum(d: DatatypeDescriptor): InternalMoveEnum {
|
|
115
|
+
const variants: { [key: string]: InternalMoveStructField[] } = {}
|
|
116
|
+
for (const v of d.variants ?? []) {
|
|
117
|
+
variants[v.name ?? ''] = (v.fields ?? []).map(toInternalField)
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
name: d.name ?? '',
|
|
121
|
+
abilities: (d.abilities ?? []).map(abilityName),
|
|
122
|
+
typeParams: (d.typeParameters ?? []).map((p) => ({
|
|
123
|
+
constraints: (p.constraints ?? []).map(abilityName)
|
|
124
|
+
})),
|
|
125
|
+
variants
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function toInternalField(f: FieldDescriptor): InternalMoveStructField {
|
|
130
|
+
if (!f.type) {
|
|
131
|
+
throw Error(`Field ${f.name} has no type`)
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
name: f.name ?? '',
|
|
135
|
+
type: toTypeDescriptorFromBody(f.type)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// OpenSignature wraps an OpenSignatureBody with an optional reference marker.
|
|
140
|
+
// Used for function params and returns.
|
|
141
|
+
function toTypeDescriptorFromSignature(sig: OpenSignature): TypeDescriptor {
|
|
142
|
+
if (!sig.body) {
|
|
143
|
+
throw Error('OpenSignature has no body')
|
|
144
|
+
}
|
|
145
|
+
const desc = toTypeDescriptorFromBody(sig.body)
|
|
146
|
+
// Reference.IMMUTABLE=1, MUTABLE=2
|
|
147
|
+
if (sig.reference === 1) {
|
|
148
|
+
desc.reference = true
|
|
149
|
+
} else if (sig.reference === 2) {
|
|
150
|
+
desc.reference = true
|
|
151
|
+
desc.mutable = true
|
|
152
|
+
}
|
|
153
|
+
return desc
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function toTypeDescriptorFromBody(body: OpenSignatureBody): TypeDescriptor {
|
|
157
|
+
switch (body.type) {
|
|
158
|
+
case TYPE_ADDRESS:
|
|
159
|
+
return new TypeDescriptor('Address')
|
|
160
|
+
case TYPE_BOOL:
|
|
161
|
+
return new TypeDescriptor('Bool')
|
|
162
|
+
case TYPE_U8:
|
|
163
|
+
return new TypeDescriptor('U8')
|
|
164
|
+
case TYPE_U16:
|
|
165
|
+
return new TypeDescriptor('U16')
|
|
166
|
+
case TYPE_U32:
|
|
167
|
+
return new TypeDescriptor('U32')
|
|
168
|
+
case TYPE_U64:
|
|
169
|
+
return new TypeDescriptor('U64')
|
|
170
|
+
case TYPE_U128:
|
|
171
|
+
return new TypeDescriptor('U128')
|
|
172
|
+
case TYPE_U256:
|
|
173
|
+
return new TypeDescriptor('U256')
|
|
174
|
+
case TYPE_VECTOR: {
|
|
175
|
+
const inner = body.typeParameterInstantiation?.[0]
|
|
176
|
+
if (!inner) throw Error('Vector OpenSignatureBody missing type parameter')
|
|
177
|
+
return new TypeDescriptor('Vector', [toTypeDescriptorFromBody(inner)])
|
|
178
|
+
}
|
|
179
|
+
case TYPE_DATATYPE: {
|
|
180
|
+
// gRPC emits typeName as `<defining_id>::<module>::<name>` with the
|
|
181
|
+
// address in canonical long form (0x0000...0002). Normalize to the
|
|
182
|
+
// short form (0x2) so module-lookup keys match what's loaded into
|
|
183
|
+
// MoveCoder by short-form aware code paths.
|
|
184
|
+
const raw = body.typeName ?? ''
|
|
185
|
+
const parts = raw.split(SPLITTER)
|
|
186
|
+
const qname = parts.length === 3 ? [accountTypeString(parts[0]), parts[1], parts[2]].join(SPLITTER) : raw
|
|
187
|
+
const args = (body.typeParameterInstantiation ?? []).map(toTypeDescriptorFromBody)
|
|
188
|
+
return new TypeDescriptor(qname, args)
|
|
189
|
+
}
|
|
190
|
+
case TYPE_TYPE_PARAMETER:
|
|
191
|
+
return new TypeDescriptor('T' + (body.typeParameter ?? 0))
|
|
192
|
+
default:
|
|
193
|
+
throw new Error(`Unexpected OpenSignatureBody type: ${body.type}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function abilityName(a: number): string {
|
|
198
|
+
const n = ABILITY_NAMES[a]
|
|
199
|
+
if (!n) throw Error(`Unknown Ability value: ${a}`)
|
|
200
|
+
return n
|
|
201
|
+
}
|