-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtask_helpers.ts
164 lines (140 loc) · 4.49 KB
/
task_helpers.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
162
163
164
import type ethers from "ethers";
import _ from "lodash";
import "../type-extensions";
export class FromArgType {
static validate(_argName: string, _argumentValue: any): void {
}
static parse(_argName: string, strValue: string): any {
if (/^\d+$/.test(strValue)) {
return _.toNumber(strValue);
} else {
return strValue;
}
}
}
export class AmountArgType {
static validate(_argName: string, _argumentValue: any): void {
}
static parse(_argName: string, strValue: string): any {
if (/^\d+$/.test(strValue)) {
return _.toNumber(strValue);
} else {
return strValue;
}
}
}
export interface FuncTaskCommonArgs {
name: string;
func: string;
args: string[];
from: string;
to?: string;
}
interface ResolvedFuncArgs {
contract: ethers.Contract;
sender: ethers.Signer;
unsignedTx: any;
}
export async function resolveFuncArgs(taskArgs: FuncTaskCommonArgs): Promise<ResolvedFuncArgs> {
const { name, func, args, from, to } = taskArgs;
let contract: ethers.Contract;
if (!to || to == "") { // 'to' address unspecified; lookup in deployments
const deployment = await hre.deployments.get(name);
contract = new hre.ethers.Contract(deployment.address, deployment.abi);
} else { // 'to' address specified
contract = await hre.ethers.getContractAt(name, to);
}
const sender = await hre.ethers.getSigner(from);
try {
const frag = contract.interface.getFunction(func);
const adjustedArgs = adjustFuncArgsType(frag, args);
const unsignedTx = await contract
.connect(sender)
.populateTransaction[func](...adjustedArgs);
return { contract, sender, unsignedTx };
} catch (e) {
// Fall back to explicit function signature
// build "function foo(uint a)" format
let strFrag = func;
if (!func.startsWith("function ")) {
strFrag = "function " + strFrag;
}
if (!func.includes("(")) {
strFrag = strFrag + "()" // heuristically append ()
}
const iface = new hre.ethers.utils.Interface([strFrag]); // improvised Interface with one function
const fragName = _.keys(iface.functions)[0]; // compact "foo(uint)" format
console.warn(`warn: function '${func}' not found in ${name}.. trying '${fragName}'`);
const frag = iface.getFunction(fragName);
const adjustedArgs = adjustFuncArgsType(frag, args);
const data = iface.encodeFunctionData(func, adjustedArgs);
const unsignedTx = {
from: sender.address,
to: contract.address,
data: data,
};
return { contract, sender, unsignedTx };
}
}
export function adjustFuncArgsType(frag: ethers.utils.FunctionFragment, args: any[]): any[] {
if (frag.inputs.length != args.length) {
throw new Error(`Argument count mismatch for ${frag.format()}: want ${frag.inputs.length}, have ${args.length}`);
}
for (let i = 0; i < args.length; i++) {
const ty = frag.inputs[i];
const arg = args[i];
// If an array is expected, try to split argument using comma.
if (ty.baseType == "array") {
args[i] = _.split(arg, ",");
}
// If a bool is expected, try to interpret the input as bool.
if (ty.baseType == "bool") {
args[i] = parseBool(arg);
}
}
return args;
}
function parseBool(arg: any): boolean {
if (_.isNumber(arg)) {
return !!arg;
}
if (_.isString(arg)) {
// Explicit string "false" and "true"
if (arg.toLowerCase() == "false") { return false; }
if (arg.toLowerCase() == "true") { return true; }
// Otherwise it must be a number
return !!Number(arg);
}
throw new Error(`Argument not boolean: '${arg}'`);
}
export interface NormalizeOpts {
raw?: boolean;
dec?: boolean;
eth?: boolean;
}
// Prettify function call result in array
export function normalizeCallResult(res: any, opts?: NormalizeOpts): any {
if (_.isArray(res) && !_.isString(res)) {
return _.map(res, (item) => normalizeCallResult(item, opts));
} else {
return normalizeItem(res, opts);
}
}
// Prettify RPC call result such as eth_getTransactionReceipt
export function normalizeRpcResult(obj: any, opts?: NormalizeOpts) {
return _.mapValues(obj, (val) => normalizeItem(val, opts));
}
function normalizeItem(item: any, opts?: NormalizeOpts): any {
if (item?.constructor.name == "BigNumber" || _.isNumber(item)) {
const num = hre.ethers.BigNumber.from(item);
if (opts?.dec) {
return num.toString();
} else if (opts?.eth) {
return hre.ethers.utils.formatEther(num);
} else {
return num.toHexString();
}
} else {
return item;
}
}