-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
json-abi.ts
148 lines (128 loc) · 4.15 KB
/
json-abi.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* Types for Fuel JSON ABI Format as defined on:
* https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md#json-abi-format
*/
import { genericRegEx } from './constants';
export interface JsonAbiFragmentType {
readonly type: string;
readonly name?: string;
// TODO: Remove `null` when forc doesn't output nulls (https://github.com/FuelLabs/sway/issues/926)
readonly components?: ReadonlyArray<JsonAbiFragmentType> | null;
readonly typeArguments?: ReadonlyArray<JsonAbiFragmentType> | null;
}
export interface JsonAbiFragment {
readonly type:
| 'function'
// We actually shouldn't accept string here, but when importing a JSON file
// TS types string literals as strings so we have to.
// TODO: Remove when TS issue is resolved: https://github.com/microsoft/TypeScript/issues/32063
| string;
readonly name: string;
readonly inputs?: ReadonlyArray<JsonAbiFragmentType>;
readonly outputs?: ReadonlyArray<JsonAbiFragmentType>;
}
export interface JsonFlatAbiFragmentType {
readonly typeId: number;
readonly type: string;
readonly name?: string;
readonly components?: ReadonlyArray<JsonFlatAbiFragmentArgumentType> | null;
readonly typeParameters?: ReadonlyArray<number> | null;
}
export interface JsonFlatAbiFragmentArgumentType {
readonly type: number;
readonly name?: string;
readonly typeArguments?: ReadonlyArray<JsonFlatAbiFragmentArgumentType> | null;
}
export interface JsonFlatAbiFragmentFunction {
readonly name: string;
readonly inputs?: ReadonlyArray<JsonFlatAbiFragmentArgumentType>;
readonly output?: Readonly<JsonFlatAbiFragmentArgumentType>;
}
export interface JsonFlatAbi {
readonly types: ReadonlyArray<JsonFlatAbiFragmentType>;
readonly functions: ReadonlyArray<JsonFlatAbiFragmentFunction>;
}
/**
* A JSON ABI object
*/
export type JsonAbi = ReadonlyArray<JsonAbiFragment> | JsonFlatAbi;
export class ABI {
readonly types: ReadonlyArray<JsonFlatAbiFragmentType>;
readonly functions: ReadonlyArray<JsonFlatAbiFragmentFunction>;
constructor(jsonAbi: JsonFlatAbi) {
this.types = jsonAbi.types;
this.functions = jsonAbi.functions;
}
parseInput(
input: JsonFlatAbiFragmentArgumentType,
typeArgumentsList: Map<number, JsonAbiFragmentType> = new Map()
): JsonAbiFragmentType {
const type = this.types[input.type];
let components;
let typeArguments: Array<JsonAbiFragmentType> | undefined;
if (!type) {
throw new Error(`${input.type} not found`);
}
if (Array.isArray(input.typeArguments)) {
typeArguments = input.typeArguments.map((ta) => this.parseInput(ta, typeArgumentsList));
}
if (Array.isArray(type.typeParameters) && Array.isArray(typeArguments)) {
type.typeParameters.forEach((tp, index) => {
if (typeArguments?.[index]) {
typeArgumentsList.set(tp, typeArguments[index]);
}
});
}
if (Array.isArray(type.components)) {
components = type.components.map((c) => this.parseInput(c, typeArgumentsList));
}
if (genericRegEx.test(type.type)) {
const typeInput = typeArgumentsList.get(type.typeId);
if (typeInput) {
return {
...typeInput,
name: input.name,
};
}
}
return {
type: type.type,
name: input.name,
typeArguments,
components,
};
}
static unflatten(jsonAbi: JsonAbi) {
if (Array.isArray(jsonAbi)) {
return jsonAbi as ReadonlyArray<JsonAbiFragment>;
}
const abi = new ABI(jsonAbi as JsonFlatAbi);
return abi.unflatten();
}
unflatten(): ReadonlyArray<JsonAbiFragment> {
return this.functions.map((functionType) => ({
type: 'function',
name: functionType.name,
inputs: (functionType.inputs || []).map((i) => this.parseInput(i)),
outputs: functionType.output ? [this.parseInput(functionType.output)] : [],
}));
}
}
/**
* Checks if a given type is a reference type
* See: https://github.com/FuelLabs/sway/issues/1368
*/
export const isReferenceType = (type: string) => {
switch (type) {
case 'u8':
case 'u16':
case 'u32':
case 'u64':
case 'bool': {
return false;
}
default: {
return true;
}
}
};