-
Notifications
You must be signed in to change notification settings - Fork 307
/
Copy pathdecoder.ts
161 lines (149 loc) · 5.15 KB
/
decoder.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
149
150
151
152
153
154
155
156
157
158
159
160
161
import { ABIParameter, ABIType, ABIVariable, FunctionArtifact } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
/**
* The type of our decoded ABI.
*/
export type DecodedReturn = bigint | boolean | DecodedReturn[] | { [key: string]: DecodedReturn };
/**
* Decodes return values from a function call.
* Missing support for integer and string.
*/
class ReturnValuesDecoder {
constructor(private artifact: FunctionArtifact, private flattened: Fr[]) {}
/**
* Decodes a single return value from field to the given type.
* @param abiType - The type of the return value.
* @returns The decoded return value.
*/
private decodeReturn(abiType: ABIType): DecodedReturn {
switch (abiType.kind) {
case 'field':
return this.getNextField().value;
case 'integer':
if (abiType.sign === 'signed') {
throw new Error('Unsupported type: signed integer');
}
return this.getNextField().value;
case 'boolean':
return !this.getNextField().isZero();
case 'array': {
const array = [];
for (let i = 0; i < abiType.length; i += 1) {
array.push(this.decodeReturn(abiType.type));
}
return array;
}
case 'struct': {
const struct: { [key: string]: DecodedReturn } = {};
for (const field of abiType.fields) {
struct[field.name] = this.decodeReturn(field.type);
}
return struct;
}
default:
throw new Error(`Unsupported type: ${abiType.kind}`);
}
}
/**
* Gets the next field in the flattened return values.
* @returns The next field in the flattened return values.
*/
private getNextField(): Fr {
const field = this.flattened.shift();
if (!field) {
throw new Error('Not enough return values');
}
return field;
}
/**
* Decodes all the return values for the given function ABI.
* Aztec.nr support only single return value
* The return value can however be simple types, structs or arrays
* @returns The decoded return values.
*/
public decode(): DecodedReturn {
if (this.artifact.returnTypes.length > 1) {
throw new Error('Multiple return values not supported');
}
if (this.artifact.returnTypes.length === 0) {
return [];
}
return this.decodeReturn(this.artifact.returnTypes[0]);
}
}
/**
* Decodes return values from a function call.
* @param abi - The ABI entry of the function.
* @param returnValues - The decoded return values.
* @returns
*/
export function decodeReturnValues(abi: FunctionArtifact, returnValues: Fr[]) {
return new ReturnValuesDecoder(abi, returnValues.slice()).decode();
}
/**
* Decodes the signature of a function from the name and parameters.
*/
export class FunctionSignatureDecoder {
private separator: string;
constructor(private name: string, private parameters: ABIParameter[], private includeNames = false) {
this.separator = includeNames ? ', ' : ',';
}
/**
* Decodes a single function parameter type for the function signature.
* @param param - The parameter type to decode.
* @returns A string representing the parameter type.
*/
private getParameterType(param: ABIType): string {
switch (param.kind) {
case 'field':
return 'Field';
case 'integer':
if (param.sign === 'signed') {
throw new Error('Unsupported type: signed integer');
}
return `u${param.width}`;
case 'boolean':
return 'bool';
case 'array':
return `[${this.getParameterType(param.type)};${param.length}]`;
case 'struct':
return `(${param.fields.map(field => `${this.decodeParameter(field)}`).join(this.separator)})`;
default:
throw new Error(`Unsupported type: ${param.kind}`);
}
}
/**
* Decodes a single function parameter for the function signature.
* @param param - The parameter to decode.
* @returns A string representing the parameter type and optionally its name.
*/
private decodeParameter(param: ABIVariable): string {
const type = this.getParameterType(param.type);
return this.includeNames ? `${param.name}: ${type}` : type;
}
/**
* Decodes all the parameters and build the function signature
* @returns The function signature.
*/
public decode(): string {
return `${this.name}(${this.parameters.map(param => this.decodeParameter(param)).join(this.separator)})`;
}
}
/**
* Decodes a function signature from the name and parameters.
* @param name - The name of the function.
* @param parameters - The parameters of the function.
* @returns - The function signature.
*/
export function decodeFunctionSignature(name: string, parameters: ABIParameter[]) {
return new FunctionSignatureDecoder(name, parameters).decode();
}
/**
* Decodes a function signature from the name and parameters including parameter names.
* @param name - The name of the function.
* @param parameters - The parameters of the function.
* @returns - The user-friendly function signature.
*/
export function decodeFunctionSignatureWithParameterNames(name: string, parameters: ABIParameter[]) {
return new FunctionSignatureDecoder(name, parameters, true).decode();
}