algorandfoundation / puya

https://algorandfoundation.github.io/puya/
Other
91 stars 16 forks source link

Smart Contract standard errors #234

Open cusma opened 6 months ago

cusma commented 6 months ago

Problem

Returning precise error codes and informative error messages for AVM runtime failures is crucial for the Smart Contract clients (and overall user experience).

Solution

Include a native Algorand Python way to handle Smart Contract errors.

Common standard formatting, codes and messages should be harmonized with an ARC (e.g. 401:Unauthorized Caller).

Proposal

A base BaseError class with with standard errors (standard error_codes and harmonized error_messages) which can be inherited and extended as Error(BaseErrors) with Smart Contract specific error codes and messages.

Additionally, would be good to have utilities like: add_error(code: int, message: str) -> Error which automatically formats the error etc.

This would combine nicely for tests, like in pytest where you can do:

with pytest.raises(LogicError, match=Error.A_STANDARD_ERROR):

so that you write a test that expects a specific error code.

Pros and Cons

Pros: fostering the standardization of Smart Contract error codes and messages in the ecosystem.

Cons: I can not see any.

Dependencies

None

robdmoore commented 6 months ago

This fits nicely with ARC-56 too.

I wonder if this is a good reason to support raise (but naturally not try/catch given AVM doesn't support that).

cc @Loedn @achidlow @daniel-makerx

achidlow commented 6 months ago

To maintain semantic compatibility, raise in Python can only be used with objects that inherit from BaseException.

Given we don't support user objects, true inheritance (ie dynamic polymorphism), and try/catch/finally, I don't see this as a viable option.

From the other angle, there's only two ways to report errors back. One is statically, with comments. These cannot contain any runtime information, so a plain string with an assert message is all that can be done here. The situation is basically the same with ARC-56, which is again just a linking of PC failures to static strings. A standard set of error codes would be something best done outside the compiler core, as a simple user library.

The other way could be through ARC-28 events, which can contain runtime information. We currently have arc4.emit which supports this. User code can already use this, and then immediately op.err() to reject the transaction - perhaps we could combine these with an emit+err function, arc4.error perhaps? (Edit: although this too could and perhaps should be done in a user library instead.)

We don't yet support inheritance of ARC4 Struct types either, but if we did, any "community standards" for ARC-28 events acting as error "classes", would best be defined in a user library rather than the core compiler also.