Skip to content

Commit 5d7c6a7

Browse files
committed
Fixed bug from EIP-150's 63/64th gas being passed along with calls to other contracts and returning amount of gas used by each multicall contract call.
1 parent 0f6dc0b commit 5d7c6a7

File tree

2 files changed

+39
-23
lines changed

2 files changed

+39
-23
lines changed

IceCreamSwapWeb3/Multicall.py

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ class MultiCall:
3131
CALLER_ADDRESS = "0x0000000000000000000000000000000000000123"
3232

3333
MULTICALL_DEPLOYMENTS: dict[int, str] = {
34-
56: "0xDD020f51961febA6C989b1865c44f3bFcEfCA58d",
35-
1116: "0x2De75065f4161c797d7168cE63CBc0261FE1ccF9",
36-
40: "0xCA4E8AC62E4A6D26e34039E5ded68dcf9d38bEea",
34+
56: "0xE396FfB7aa3123E3D742e50Ad409d212f87ADfA6",
35+
1116: "0xf7bfc56C50e544ba035d0b27978aF2f072b096f7",
36+
40: "0x4E1F64D55cD51E8D8D2125A5c1Fa613E78921C51",
3737
}
3838

3939
@classmethod
@@ -74,6 +74,10 @@ def add_undeployed_contract_call(self, contract_func: ContractFunction):
7474
self.calls.append(contract_func)
7575

7676
def call(self, use_revert: Optional[bool] = None, batch_size: int = 1_000):
77+
results, _ = self.call_with_gas(use_revert=use_revert, batch_size=batch_size)
78+
return results
79+
80+
def call_with_gas(self, use_revert: Optional[bool] = None, batch_size: int = 1_000):
7781
if use_revert is None:
7882
use_revert = self.w3.revert_reason_available
7983

@@ -87,20 +91,23 @@ def _inner_call(
8791
use_revert: bool,
8892
calls_with_calldata: list[tuple[ContractFunction, bytes]],
8993
batch_size: int
90-
):
94+
) -> tuple[list[Exception | tuple[any, ...]], list[int]]:
9195
kwargs = dict(
9296
use_revert=use_revert,
9397
batch_size=batch_size,
9498
)
9599
# make sure calls are not bigger than batch_size
96100
if len(calls_with_calldata) > batch_size:
97-
results = []
101+
results_combined = []
102+
gas_usages_combined = []
98103
for start in range(0, len(calls_with_calldata), batch_size):
99-
results += self._inner_call(
104+
results, gas_usages = self._inner_call(
100105
**kwargs,
101106
calls_with_calldata=calls_with_calldata[start: min(start + batch_size, len(calls_with_calldata))],
102107
)
103-
return results
108+
results_combined += results
109+
gas_usages_combined += gas_usages
110+
return results_combined, gas_usages_combined
104111

105112
if self.multicall.address is None:
106113
multicall_call = self._build_constructor_calldata(
@@ -111,8 +118,9 @@ def _inner_call(
111118
multicall_call = self._build_calldata(
112119
calls_with_calldata=calls_with_calldata
113120
)
121+
114122
try:
115-
raw_returns = self._call_multicall(
123+
raw_returns, gas_usages = self._call_multicall(
116124
multicall_call=multicall_call,
117125
retry=len(calls_with_calldata) == 1
118126
)
@@ -122,14 +130,15 @@ def _inner_call(
122130
sleep(1)
123131
return self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata)
124132
print(f"Multicall got Exception '{repr(e)}', splitting and retrying")
125-
left_results = self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[:len(calls_with_calldata) // 2])
126-
right_results = self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[len(calls_with_calldata) // 2:])
127-
return left_results + right_results
133+
left_results, left_gas_usages = self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[:len(calls_with_calldata) // 2])
134+
right_results, right_gas_usages = self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[len(calls_with_calldata) // 2:])
135+
return left_results + right_results, left_gas_usages + right_gas_usages
128136
results = self.decode_contract_function_results(raw_returns=raw_returns, contract_functions=[call for call, _ in calls_with_calldata])
129137
if len(results) == len(calls_with_calldata):
130-
return results
138+
return results, gas_usages
131139
# if not all calls were executed, recursively execute remaining calls and concatenate results
132-
return results + self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[len(results):])
140+
right_results, right_gas_usages = self._inner_call(**kwargs, calls_with_calldata=calls_with_calldata[len(results):])
141+
return results + right_results, gas_usages + right_gas_usages
133142

134143
@staticmethod
135144
def calculate_expected_contract_address(sender: str, nonce: int):
@@ -252,41 +261,48 @@ def _build_constructor_calldata(
252261
return multicall_call
253262

254263
@staticmethod
255-
def _decode_muilticall(multicall_result: bytes | list[tuple[bool, int, bytes]]) -> list[str | Exception]:
264+
def _decode_muilticall(
265+
multicall_result: bytes | list[tuple[bool, int, bytes]]
266+
) -> tuple[list[str | Exception], list[int]]:
256267
raw_returns: list[str or Exception] = []
268+
gas_usages: list[int] = []
257269

258270
if isinstance(multicall_result, list) or isinstance(multicall_result, tuple):
259271
# deployed multicall
260-
for sucess, _, raw_return in multicall_result:
272+
for sucess, gas_usage, raw_return in multicall_result:
261273
if not sucess:
262274
decoded = MultiCall.get_revert_reason(raw_return)
263275
raw_return = ContractLogicError(f"execution reverted: {decoded}")
264276
raw_returns.append(raw_return)
265-
return raw_returns
277+
gas_usages.append(gas_usage)
278+
return raw_returns, gas_usages
266279

267280
# undeployed multicall
268281
# decode returned data into segments
269282
multicall_result_copy = multicall_result[:]
270283
raw_returns_encoded = []
271284
while len(multicall_result_copy) != 0:
272285
data_len = int.from_bytes(multicall_result_copy[:2], byteorder='big')
273-
raw_returns_encoded.append(multicall_result_copy[2:data_len+2])
274-
multicall_result_copy = multicall_result_copy[data_len+2:]
286+
raw_returns_encoded.append(multicall_result_copy[2:data_len])
287+
multicall_result_copy = multicall_result_copy[data_len:]
275288

276289
# decode returned data for each call
277290
for raw_return_encoded in raw_returns_encoded:
278291
try:
279292
# we are using packed encoding to decrease size of return data, if not we could have used
280293
# success, raw_return = eth_abi.decode(['bool', 'bytes'], raw_return_encoded)
281294
success = raw_return_encoded[0] == 1
282-
raw_return = raw_return_encoded[1:]
295+
gas_usage = int(raw_return_encoded[1:5].hex(), 16)
296+
raw_return = raw_return_encoded[5:]
283297
if not success:
284298
decoded = MultiCall.get_revert_reason(raw_return)
285299
raw_return = ContractLogicError(f"execution reverted: {decoded}")
286300
except Exception as e:
287301
raw_return = e
302+
gas_usage = None
303+
gas_usages.append(gas_usage)
288304
raw_returns.append(raw_return)
289-
return raw_returns
305+
return raw_returns, gas_usages
290306

291307
@staticmethod
292308
def get_revert_reason(revert_bytes: bytes) -> str:
@@ -398,8 +414,8 @@ def main(
398414
# calling a deployed contract
399415
multicall.add_call(usdt_contract.functions.decimals())
400416

401-
multicall_result = multicall.call()
402-
print(multicall_result)
417+
multicall_results, gas_usages = multicall.call_with_gas()
418+
print(list(zip(multicall_results, gas_usages)))
403419

404420

405421
if __name__ == "__main__":
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
608060405234801561001057600080fd5b506040516105fc3803806105fc83398101604081905261002f91610474565b600060055a61003e919061050b565b61004b90620f424061052d565b90506000835160001461006e578351602085016000f09050803b61006e57600080fd5b60008561007d576155f0610082565b620400005b62ffffff166001600160401b0381111561009e5761009e6103b1565b6040519080825280601f01601f1916602001820160405280156100c8576020820181803683370190505b506020858101805192935091818401916100e2908461052d565b92506000606060005b83811015610378578551600196870196600091821a91908216900361014c5786516014909701966c01000000000000000000000000900493506001600160a01b03841615801561014357506001600160a01b03891615155b1561014c578893505b600281166000036101ce57865160029097019660f01c806001600160401b0381111561017a5761017a6103b1565b6040519080825280601f01601f1916602001820160405280156101a4576020820181803683370190505b5093506020840160005b828110156101c65789810151828201526020016101ae565b505096909601955b6101da8a61271061052d565b5a116101e65750610378565b600080856001600160a01b03168c5a6101ff9190610546565b8660405161020d9190610559565b60006040518083038160008787f1925050503d806000811461024b576040519150601f19603f3d011682016040523d82523d6000602084013e610250565b606091505b5091509150600081516001610265919061052d565b838360405160200161027993929190610575565b6040516020818303038152906040529050600060208c018a0390508b516155f0036102e9578b5182516102ac908361052d565b11156102bc575050505050610378565b6020820160005b83518110156102dc57818101518c8201526020016102c3565b505081518a019950610367565b8b5182516102f89060026105b1565b610302908361052d565b1115610312575050505050610378565b60208201825181015b8082101561036457815160001a8060041c600f821691506027600982110281603001018e535060276009821102816030010160018e01535060028c019b5060018201915061031b565b50505b5050600190930192506100eb915050565b601f198786030187528b61038e57865160208801f35b8660405162461bcd60e51b81526004016103a891906105c8565b60405180910390fd5b634e487b7160e01b600052604160045260246000fd5b60005b838110156103e25781810151838201526020016103ca565b50506000910152565b600082601f8301126103fc57600080fd5b81516001600160401b03811115610415576104156103b1565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610443576104436103b1565b60405281815283820160200185101561045b57600080fd5b61046c8260208301602087016103c7565b949350505050565b60008060006060848603121561048957600080fd5b8351801515811461049957600080fd5b60208501519093506001600160401b038111156104b557600080fd5b6104c1868287016103eb565b604086015190935090506001600160401b038111156104df57600080fd5b6104eb868287016103eb565b9150509250925092565b634e487b7160e01b600052601160045260246000fd5b60008261052857634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610540576105406104f5565b92915050565b81810381811115610540576105406104f5565b6000825161056b8184602087016103c7565b9190910192915050565b61ffff60f01b8460f01b16815282151560f81b6002820152600082516105a28160038501602087016103c7565b91909101600301949350505050565b8082028115828204841417610540576105406104f5565b60208152600082518060208401526105e78160408501602087016103c7565b601f01601f1916919091016040019291505056fe
1+
608060405234801561001057600080fd5b5060405161068538038061068583398101604081905261002f916104c3565b600060055a61003e9190610554565b61004b90620f4240610576565b90506000835160001461006e578351602085016000f09050803b61006e57600080fd5b60008561007d576155f0610082565b620400005b62ffffff166001600160401b0381111561009e5761009e6103f6565b6040519080825280601f01601f1916602001820160405280156100c8576020820181803683370190505b506020858101805192935091818401916100e29084610576565b92506000606060005b838110156103bd57855160019687019660009190911a90811661014a5786516014909701966c01000000000000000000000000900493506001600160a01b03841615801561014157506001600160a01b03891615155b1561014a578893505b600281166101c957865160029097019660f01c806001600160401b03811115610175576101756103f6565b6040519080825280601f01601f19166020018201604052801561019f576020820181803683370190505b5093506020840160005b828110156101c15789810151828201526020016101a9565b505096909601955b6101d58a612710610576565b5a116101e157506103bd565b60005a90506000806001600160a01b0387166101fd8e8561058e565b8760405161020b91906105a5565b60006040518083038160008787f1925050503d8060008114610249576040519150601f19603f3d011682016040523d82523d6000602084013e61024e565b606091505b509092509050600a6102608e8561058e565b61026a9190610554565b610274908e610576565b5a1161028357505050506103bd565b6000815160076102939190610576565b835a61029f908761058e565b846040516020016102b394939291906105c1565b6040516020818303038152906040529050600060208d018b0390508c516155f01415610325578c5182516102e79083610576565b11156102f8575050505050506103bd565b6020820160005b835181101561031857818101518d8201526020016102ff565b505081518b019a506103a4565b8c518251610334906002610617565b61033e9083610576565b111561034f575050505050506103bd565b60208201825181015b808210156103a157815160001a8060041c600f821691506027600982110281603001018f535060276009821102816030010160018f01535060028d019c50600182019150610358565b50505b50505050505080806103b590610636565b9150506100eb565b601f198786030187528b6103d357865160208801f35b8660405162461bcd60e51b81526004016103ed9190610651565b60405180910390fd5b634e487b7160e01b600052604160045260246000fd5b60005b8381101561042757818101518382015260200161040f565b83811115610436576000848401525b50505050565b600082601f83011261044d57600080fd5b81516001600160401b0380821115610467576104676103f6565b604051601f8301601f19908116603f0116810190828211818310171561048f5761048f6103f6565b816040528381528660208588010111156104a857600080fd5b6104b984602083016020890161040c565b9695505050505050565b6000806000606084860312156104d857600080fd5b835180151581146104e857600080fd5b60208501519093506001600160401b038082111561050557600080fd5b6105118783880161043c565b9350604086015191508082111561052757600080fd5b506105348682870161043c565b9150509250925092565b634e487b7160e01b600052601160045260246000fd5b60008261057157634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156105895761058961053e565b500190565b6000828210156105a0576105a061053e565b500390565b600082516105b781846020870161040c565b9190910192915050565b60f085901b6001600160f01b031916815283151560f81b600282015260e083901b6001600160e01b0319166003820152815160009061060781600785016020870161040c565b9190910160070195945050505050565b60008160001904831182151516156106315761063161053e565b500290565b600060001982141561064a5761064a61053e565b5060010190565b602081526000825180602084015261067081604085016020870161040c565b601f01601f1916919091016040019291505056fe

0 commit comments

Comments
 (0)