-
Notifications
You must be signed in to change notification settings - Fork 0
/
gasProxy.js
139 lines (122 loc) · 4.45 KB
/
gasProxy.js
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
require("console.table");
// TODO: clean up and refactor this file - it could be a nice util
let createMethodGasProxy = (prop, obj, gasLib) => {
let handlerMethod = {
apply: (target, that, args) => {
let result = target.apply(that, args);
if (typeof result === "object" && "then" in result) {
return result.then(success => {
if (typeof success === "object" && "receipt" in success && "gasUsed" in success["receipt"]) {
gasLib.push({ method: prop, gasUsed: success.receipt.gasUsed });
}
return success;
});
} else return result;
}
};
return new Proxy(obj, handlerMethod);
};
// accepts objects of the form:
// { method: string, gasUsed: number, parameters: object[] }
let createContractGasProxy = (contract, gasLib, web3) => {
let handlerMain = {
get: (obj, prop) => {
if (prop in obj) {
let g = obj[prop];
if (typeof g === "function" && "call" in g && "sendTransaction" in g) {
return createMethodGasProxy(prop, g, gasLib);
} else {
return g;
}
} else return undefined;
}
};
return new Proxy(contract, handlerMain);
};
const createGasProxy = (contractType, gasLib, web3) => {
const recordGasAndCreateContractProxy = (prop, val) => {
let contractHandler = {
apply: (target, that, args) => {
let result = target.apply(that, args);
if (typeof result === "object" && "then" in result) {
return result.then(success => {
// new doesnt have a receipt, so get one and record the gas
return web3.eth.getTransactionReceipt(success.transactionHash).then(receipt => {
gasLib.push({ method: prop, gasUsed: receipt.gasUsed });
// the result of calling new is contract, for which we want a gas proxy
return createContractGasProxy(success, gasLib, web3);
});
});
}
}
};
return new Proxy(val, contractHandler);
};
let handlerMain = {
get: (obj, prop) => {
if (prop in obj) {
let g = obj[prop];
if (prop === "new" && typeof g === "function") {
// proxy the "new" function
return recordGasAndCreateContractProxy(prop, g);
} else return g;
}
}
};
return new Proxy(contractType, handlerMain);
};
const logGasLib = gasLib => {
let reducer = (accumulator, currentValue) => {
let records = accumulator.filter(a => a.method === currentValue.method);
if (records && records[0]) {
// update
const record = records[0];
record.totalGas += currentValue.gasUsed;
record.timesCalled += 1;
return accumulator;
} else {
const aggr = {
method: currentValue.method,
totalGas: currentValue.gasUsed,
timesCalled: 1,
averageGas: () => {
return aggr.totalGas / aggr.timesCalled;
}
};
// push
accumulator.push(aggr);
return accumulator;
}
};
let aggregates = gasLib.reduce(reducer, []);
let total = {
method: "TOTAL",
totalGas: aggregates.map(s => s.totalGas).reduce((accum, curr) => accum + curr, 0),
timesCalled: aggregates.map(s => s.timesCalled).reduce((accum, curr) => accum + curr, 0),
averageGas: () => {
if (total.totalGas === 0) return 0;
return total.totalGas / total.timesCalled;
}
};
aggregates.push(total);
// execute aggregate functions
const bufferedAggregates = aggregates.map(a => {
let ba = {};
for (const key in a) {
if (a.hasOwnProperty(key)) {
const element = a[key];
if (typeof element === "function") {
ba[key] = element();
} else {
ba[key] = element;
}
}
}
return ba;
});
console.table(bufferedAggregates);
};
module.exports = {
createGasProxy,
logGasLib
}