Aborting Execution
A transaction Move can either succeed or fail. When execution succeeds, all modifications to on‑chain data are applied, and the transaction is committed to the blockchain. If execution aborts, none of the changes are preserved. The abort keyword and revert function from the Stylus Framework are used to terminate a transaction and revert any modifications that were made.
Note
It is important to understand that Move does not provide a catch mechanism. When a transaction aborts, all changes performed up to that point are rolled back, and the transaction is marked as failed.
Abort
The abort keyword is used to terminate execution immediately. It must be used with an error code. The abort code is a u64 value.
let user_has_access = false;
// Abort with error code 1
if (!user_has_access) {
abort 1;
}
Error Constants
Defining error constants is a good practice for making error codes more descriptive. These constants are declared using const and are typically prefixed with E followed by a UpperCamelCase name. Error constants behave like any other constants and do not receive special treatment. Their main purpose is to enhance code readability and make abort scenarios easier to interpret.
const EUserNotAuthorized: u64 = 1;
let user_has_access = false;
// Abort with error code 1
if (!user_has_access) {
abort EUserNotAuthorized;
}
assert!
The assert! macro is a convenient way to check a condition and abort execution if the condition is not met. It takes a boolean expression and an optional error code. If the expression evaluates to false, the transaction aborts with the specified error code (or a default code if none is provided).
let user_has_access = false;
// Assert that the user has access, aborting with error code 2 if not
assert!(user_has_access, 2);
Custom error structs
Move allows you to define custom structures to represent errors. This approach provides more context about the error and can include additional information beyond a simple error code. The errors raised using these structs follows the Solidity’s errors ABI, meanning that they can be docoded by any external tools that understand it.
To be able to use a struct as an error, it must be annotated with the #[ext(abi_error)] attribute. This attribute indicates that the struct is intended to be used as an external ABI error.
#[ext(abi_error)]
public struct CustomError has copy, drop {
error_message: String,
error_code: u64,
}
public fun revert_custom_error(s: String, code: u64) {
revert( CustomError { error_message: s, error_code: code });
}
Clever Errors
Clever errors are a feature that allows for more informative error messages when an assertion fails or an abort is raised. They are a source feature and compile to a u64 abort code value that contains the information needed to access the constant representing the error.
Clever Abort Codes
Clever abort codes allow you to use non-u64 constants as abort codes as long as the constants are annotated with the #[error] attribute. They can be used both in assertions, and as codes to abort.
module 0x42::a_module;
#[error]
const EIsThree: vector<u8> = b"The value is three";
// Will abort with `EIsThree` if `x` is 3
public fun double_except_three(x: u64): u64 {
assert!(x != 3, EIsThree);
x * x
}
// Will always abort with `EIsThree`
public fun clever_abort() {
abort EIsThree
}
In this example, the EIsThree constant is a vector<u8>, which is not a u64. However, the #[error] attribute allows the constant to be used as an abort code, and will at runtime produce a u64 abort code that tells the compiler where to find the constant with the error message.
Assertions with no Abort Codes
Assertions and abort statements without an abort code will automatically derive an abort code from the source line number and will be encoded in the clever error format with the constant name and constant value information will be filled with sentinel values of 0xffff each. For example, in the following snipet the abort codes in both functions will be set to the sentinel values.
module 0x42::a_module;
#[test]
fun assert_false(x: bool) {
assert!(false);
}
#[test]
fun abort_no_code() {
abort
}