-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
interface.ts
133 lines (106 loc) · 4.26 KB
/
interface.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
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { BytesLike } from '@ethersproject/bytes';
import { arrayify } from '@ethersproject/bytes';
import { Logger } from '@ethersproject/logger';
import { versions } from '@fuel-ts/versions';
import { AbiCoder } from './abi-coder';
import type { InputValue } from './coders/abstract-coder';
import { FunctionFragment } from './function-fragment';
import type { JsonAbi, JsonAbiConfigurable } from './json-abi';
import { findOrThrow } from './utilities';
const logger = new Logger(versions.FUELS);
export class Interface<TAbi extends JsonAbi = JsonAbi> {
readonly functions!: Record<string, FunctionFragment>;
readonly configurables: Record<string, JsonAbiConfigurable>;
/*
TODO: Refactor so that there's no need for externalLoggedTypes
This is dedicated to external contracts added via `<base-invocation-scope.ts>.addContracts()` method.
This is used to decode logs from contracts other than the main contract
we're interacting with.
*/
private externalLoggedTypes: Record<string, Interface>;
readonly jsonAbi: TAbi;
constructor(jsonAbi: TAbi) {
this.jsonAbi = jsonAbi;
this.externalLoggedTypes = {};
this.functions = Object.fromEntries(
this.jsonAbi.functions.map((x) => [x.name, new FunctionFragment(this.jsonAbi, x.name)])
);
this.configurables = Object.fromEntries(this.jsonAbi.configurables.map((x) => [x.name, x]));
}
/**
* Returns function fragment for a dynamic input.
* @param nameOrSignatureOrSelector - name (e.g. 'transfer'), signature (e.g. 'transfer(address,uint256)') or selector (e.g. '0x00000000a9059cbb') of the function fragment
*/
getFunction(nameOrSignatureOrSelector: string): FunctionFragment {
const fn = Object.values<FunctionFragment>(this.functions).find(
(f) =>
f.name === nameOrSignatureOrSelector ||
f.signature === nameOrSignatureOrSelector ||
f.selector === nameOrSignatureOrSelector
);
if (fn !== undefined) return fn;
return logger.throwArgumentError(
`function ${nameOrSignatureOrSelector} not found.`,
'data',
fn
);
}
decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): any {
const fragment =
typeof functionFragment === 'string' ? this.getFunction(functionFragment) : functionFragment;
if (!fragment) {
throw new Error('Fragment not found');
}
return fragment.decodeArguments(data);
}
encodeFunctionData(
functionFragment: FunctionFragment | string,
values: Array<InputValue>,
offset = 0
): Uint8Array {
const fragment =
typeof functionFragment === 'string' ? this.getFunction(functionFragment) : functionFragment;
if (!fragment) {
throw new Error('Fragment not found');
}
return fragment.encodeArguments(values, offset);
}
// Decode the result of a function call
decodeFunctionResult(functionFragment: FunctionFragment | string, data: BytesLike): any {
const fragment =
typeof functionFragment === 'string' ? this.getFunction(functionFragment) : functionFragment;
return fragment.decodeOutput(data);
}
decodeLog(data: BytesLike, logId: number, receiptId: string): any {
const isExternalLoggedType = this.externalLoggedTypes[receiptId];
if (isExternalLoggedType) {
const externalInterface = this.externalLoggedTypes[receiptId];
return externalInterface.decodeLog(data, logId, receiptId);
}
const { loggedType } = findOrThrow(this.jsonAbi.loggedTypes, (type) => type.logId === logId);
return AbiCoder.decode(this.jsonAbi, loggedType, arrayify(data), 0);
}
updateExternalLoggedTypes(id: string, loggedTypes: Interface) {
this.externalLoggedTypes[id] = loggedTypes;
}
encodeConfigurable(name: string, value: InputValue) {
const configurable = findOrThrow(
this.jsonAbi.configurables,
(c) => c.name === name,
() => {
throw new Error(`configurable '${name}' doesn't exist`);
}
);
return AbiCoder.encode(this.jsonAbi, configurable.configurableType, value);
}
getTypeById(typeId: number) {
return findOrThrow(
this.jsonAbi.types,
(t) => t.typeId === typeId,
() => {
throw new Error(`type with typeId '${typeId}' doesn't exist`);
}
);
}
}