Open
Description
How can we call an external code, and reliably catch errors?
Consider the following code, which tries to create "safeBalance
" method, which calls balanceOf and never reverts.
(spoiler: try/catch
doesn't catch a lot of cases)
- It returns a proper balance of an external token
- It properly catches revert in the external balanceOf method
But....
- it crashes if calling non-existent address (e.g.
address(0)
) - it crashes if the target contract doesn't have that method.
- it crashes if the target contract returns wrong number of arguments.
So basically, we can't rely on try/catch ...
The only alternative is to resort to low-level call (address.call()
) and manually parse the result - in realSafeBalance()
This solution is error-prone, type-unsafe and more expensive in its gas usage.
pragma solidity ^0.8.17;
//SPDX-License-Identifier: MIT
interface IERC20 {
function balanceOf(address) external returns (uint);
}
contract ATestSafeBalance {
event Debug(uint bal);
constructor () {
IERC20 a;
// a = IERC20(address(this));
// a = IERC20(address(0));
// a = new Token();
// a = new RevertToken();
a = IERC20(address(new NoReturnValue()));
uint bal = pseudoSafeBalance(a,address(this));
emit Debug(bal);
}
function pseudoSafeBalance(IERC20 token, address addr) public returns (uint) {
try token.balanceOf(addr) returns (uint ret) {
return ret;
}
catch {
return 11111;
}
}
function realSafeBalance(IERC20 token, address addr) public returns (uint retBalance) {
(bool success, bytes memory ret) = address(token).call(abi.encodeCall(IERC20.balanceOf, addr));
if (!success || ret.length != 32) return 11111;
(retBalance) = abi.decode(ret, (uint));
}
}
contract NoReturnValue {
function balanceOf(address) external {
}
}
contract RevertToken is IERC20 {
function balanceOf(address) external override returns (uint) {
revert("just because");
}
}
contract Token is IERC20 {
function balanceOf(address) external override returns (uint) {
return 1;
}
}
Metadata
Metadata
Assignees
Labels
A lot to implement but still doable by a single person. The task is large or difficult.Changes are very prominent and affect users or the project in a major way.Any changes to the language, e.g. new featuresSomething we consider an essential part of Solidity 1.0.The proposal is too vague to be implemented right away