-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
try/catch doesn't catch some call errors. #12725
Comments
Do I understand you correctly that you find it confusing that the catch block is only executed when the call reverted? Would you want the catch block to be executed for a successful call to the function but a failure to decode the return data? |
Yes. I think its confusing that the code that is covered syntactically by try/catch causes a revert. I understand that you can't add a real try/catch, since the EVM only support reverting an external call. |
If you send ether to the called function in the try/catch, how do you distinguish the ether being gone or not in the catch clause? The try/catch statement was not meant as something like a protection against malicious called contracts, it's rather a way to handle errors that come with reverts. Can you share a bit more about your use-case? |
|
Can you please share a bit more about your use-case? It is difficult to discuss breaking invariants at such an abstraction level without any examples. |
Here is a reference to our code, which is the implementation of "account abstraction" EIP-4337: https://bit.ly/3sMBirB However, the current code is not enough, in case the target address is not a contract, it reverts. using The only option we currently have is add an assembly code to do |
Ok, from your example, I see that the problem is rather the excodesize check and not the decoding failure (as you already said above). I'm less convinced to execute the catch block in case of a successful call and return data decoding. I think we should not change the invariant that if the catch code is executed, the call did not have any effect. Note that ether transfer is not the only state change that can happen and there are state changes that are impossible to detect. |
I still think that it should be possible to "catch" (that is, not throw) inside the try...catch block
I think that handling of decoding errors is highly required in complex contracts interactions. |
I still think that its worthwhile handling the general case of "void" function: in that case, you already call |
Just to chime in here. I have a use case related to ERC165-like "light" interface checking. Basically, try/catch guards could be used to experimentally call an external contract function and act on the basis of which catch block executes. This would have many useful applications. One in particular would be to check if one of the transfer addresses in a token transfer appears to be something that smells like a particular kind of token wallet etc. Of course, something as simple as:
serves such a purpose for view functions that take no parameters. Perhaps Solidity does not want to encourage this kind of usage, but a gas-efficient mechanism that could query external contracts in this way seems like a good thing. I know more efficient dispatchers are being discussed, and this consideration might be one to include in that implementation. |
Hello! I often have troubles detecting and catch calls to non contract accounts. There is one simple example:
If I face with some non-standart pair without name() method or it is not contract address - all my code reverts while I expect what it should catched in tr..catch block. |
@aathan , I have tried use code you provided, but do not understand what |
I stumbled almost to identical usage problem, where as I need to detect somehow if an address is a deployed / valid UniswapV2Pair or not. try catch does not work as expected and reverts where as I need it to catch it and continue on with my non blocking function. @bogdoslavik Have you figured a way to solve this issue? Here's my example
This reverts and does not honor the try catch. |
It's been a long time since I looked at this but I've corrected ptr in my original comment to read freeMemory, which is what (logically) it was probably meant to say in the first place. This is just building the necessary params for staticcall() to call a function that takes no params (the selector of which you pass to my example code as sel), so that you can inspect the result; I think my assumptino was that your target contract would implement some selector that would allow the boolean true return of staticcall to indicate the selector exists at the target contract. Read more about staticcall in the EVM docs and/or places like this one: https://nicolabernini.substack.com/p/an-example-staticcall-usage Perhaps take further discussion to the chat/help groups. Again, this is very old now and I'm just spending a few minutes to answer your question rather than fully confirm everything I've said here and in the prior comment is accurate. Your mileage may vary. |
@mkMoSs Before call, you have to check, what calling address have code:
for Solidity v0.8 (fn can be view)
But be aware of vulnerability described: stackoverflow |
@chriseth : sorry for late response: |
@bogdoslavik /*
* UniswapV2Library.pairFor is a pure function and generates the pair address based on factory address and a hex code
* Nothing else can generate this address.
*/
address _pairV2 = UniswapV2Library.pairFor(_factoryV2, _token0, _token1);
// is that pair deployed / existent?
// as of now, there is no other way (that I know of) to check if a pair has been deployed or not
// try _pairV2.getReserves() catch, reverts which is not what we want
if (_pairV2.code.length > 0) {
(uint _reserve0, uint _reserve1) = UniswapV2Library.getReserves(_factoryV2, _token0, _token1);
uint _amountV2 = UniswapV2Library.getAmountIn(_amount, _reserve0, _reserve1);
// ... do other stuff
}
// else do other stuff, but do not revert
I did read about the vulnerability issues with this method, as far as I can tell I should have no issues with my particular use case, unless I'm missing something. Thank you |
It's also worth mentioning ERC165 |
I ran into this today simply because some code i don't control had a try/catch block calling into my contract that happens to have a The fallback doesn't error and it doesn't return any data, but the caller was expecting an I'm not 100% sure because I didn't write the code with the try/catch but I'm pretty sure this isn't the behaviour they intended/expected. For the specific case of a simple return value that doesn't have complex decoding this could be a potential workaround, but the ergonomics degrade rapidly for more complex return types: (bool success, bytes memory returnData) = foo.staticcall(abi.encodeWithSignature("bar()"));
if (success && returnData.length == 0x20) {
address result = abi.decode(returnData, (address));
...
}
else {
} |
The following code is expected (by a developer using any other language) to catch failures related to the call.
But instead, it reverts.
This is due to the very confusing semantics of the "try/catch" construct in solidity: the catch block will only be called if the actual external code was reverted, but it might still revert in some compiler-generated code
Suggestion: solidity should not generate a "revert" code between the try and catch. Instead, it should jump into the catch block.
Currently, the following checks are done by solidity, and it reverts if any of these validation fails:
extcodesize
, to validate the target indeed has some codereturndatasize
is not zeroThe text was updated successfully, but these errors were encountered: