From beeebca7af9beca6c28ab016e9f13644e78490e8 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 21 Feb 2022 11:16:34 +0100 Subject: [PATCH] adapt book to error rework (#14) --- src/chapter_3/errors.md | 20 +++++++++++-------- src/chapter_3/high-level_overview.md | 2 +- .../milestone_project_tic-tac-toe.md | 16 +++++++-------- src/chapter_3/the_accounts_struct.md | 4 ++-- src/chapter_3/the_program_module.md | 6 +++--- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/chapter_3/errors.md b/src/chapter_3/errors.md index 56a9727..17c9787 100644 --- a/src/chapter_3/errors.md +++ b/src/chapter_3/errors.md @@ -7,23 +7,25 @@ The autogenerated clients can automatically parse Anchor Internal Errors and Cus ## Anchor Internal Errors -> [Anchor Internal Error Code Reference](https://docs.rs/anchor-lang/latest/anchor_lang/__private/enum.ErrorCode.html) +> [Anchor Internal Error Code Reference](https://docs.rs/anchor-lang/latest/anchor_lang/error/enum.ErrorCode.html) Anchor has many different internal error codes. These are not meant to be used by users, but it's useful to study the reference to learn about the mappings between codes and their causes. They are, for example, thrown when a constraint has been violated, e.g. when an account is marked with `mut` but its `is_writable` property is `false`. ## Custom Errors -You can add errors that are unique to your program by using the error attribute. +You can add errors that are unique to your program by using the `error_code` attribute. -Simply add it to an enum with a name of your choice. You can then use the variants of the enum as errors in your program. Additionally, you can add a message attribute to the individual variants. Clients will then display this error message if the error occurs. Custom Error code numbers start at the [custom error offset](https://docs.rs/anchor-lang/latest/anchor_lang/__private/constant.ERROR_CODE_OFFSET.html). +Simply add it to an enum with a name of your choice. You can then use the variants of the enum as errors in your program. Additionally, you can add a message attribute to the individual variants. Clients will then display this error message if the error occurs. Custom Error code numbers start at the [custom error offset](https://docs.rs/anchor-lang/latest/anchor_lang/error/constant.ERROR_CODE_OFFSET.html). + +To actually throw an error use the [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/macro.err.html) or the [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) macro. These add file and line information to the error that is then logged by anchor. ```rust,ignore #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { + pub fn set_data(ctx: Context, data: MyAccount) -> Result<()> { if data.data >= 100 { - return Err(MyError::DataTooLarge.into()); + return err!(MyError::DataTooLarge); } ctx.accounts.my_account.set_inner(data); Ok(()) @@ -31,19 +33,21 @@ mod hello_anchor { } -#[error] +#[error_code] pub enum MyError { #[msg("MyAccount may only hold data below 100")] DataTooLarge } ``` +### require! + You can use the [`require`](https://docs.rs/anchor-lang/latest/anchor_lang/macro.require.html) macro to simplify writing errors. The code above can be simplified to this (Note that the `>=` flips to `<`): ```rust,ignore #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { + pub fn set_data(ctx: Context, data: MyAccount) -> Result<()> { require!(data.data < 100, MyError::DataTooLarge); ctx.accounts.my_account.set_inner(data); Ok(()) @@ -51,7 +55,7 @@ mod hello_anchor { } -#[error] +#[error_code] pub enum MyError { #[msg("MyAccount may only hold data below 100")] DataTooLarge diff --git a/src/chapter_3/high-level_overview.md b/src/chapter_3/high-level_overview.md index bbb4670..e2434c8 100644 --- a/src/chapter_3/high-level_overview.md +++ b/src/chapter_3/high-level_overview.md @@ -13,7 +13,7 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); #[program] mod hello_anchor { use super::*; - pub fn initialize(_ctx: Context) -> ProgramResult { + pub fn initialize(_ctx: Context) -> Result<()> { Ok(()) } } diff --git a/src/chapter_3/milestone_project_tic-tac-toe.md b/src/chapter_3/milestone_project_tic-tac-toe.md index 140b067..0a38594 100644 --- a/src/chapter_3/milestone_project_tic-tac-toe.md +++ b/src/chapter_3/milestone_project_tic-tac-toe.md @@ -109,7 +109,7 @@ And with this, `SetupGame` is complete and we can move on to the `setup_game` fu Let's start by adding an argument to the `setup_game` function. ```rust,ignore -pub fn setup_game(ctx: Context, player_two: Pubkey) -> ProgramResult { +pub fn setup_game(ctx: Context, player_two: Pubkey) -> Result<()> { Ok(()) } ``` @@ -117,7 +117,7 @@ Why didn't we just add `player_two` as an account in the accounts struct? There Finish the instruction function by setting the game to its initial values: ```rust,ignore -pub fn setup_game(ctx: Context, player_two: Pubkey) -> ProgramResult { +pub fn setup_game(ctx: Context, player_two: Pubkey) -> Result<()> { let game = &mut ctx.accounts.game; game.players = [ctx.accounts.player_one.key(), player_two]; game.turn = 1; @@ -202,9 +202,9 @@ impl Game { self.players[self.current_player_index()] } - pub fn play(&mut self, tile: &Tile) -> ProgramResult { + pub fn play(&mut self, tile: &Tile) -> Result<()> { if !self.is_active() { - return Err(TicTacToeError::GameAlreadyOver.into()); + return err!(TicTacToeError::GameAlreadyOver); } match tile { tile @@ -212,13 +212,13 @@ impl Game { row: 0..=2, column: 0..=2, } => match self.board[tile.row as usize][tile.column as usize] { - Some(_) => return Err(TicTacToeError::TileAlreadySet.into()), + Some(_) => return err!(TicTacToeError::TileAlreadySet), None => { self.board[tile.row as usize][tile.column as usize] = Some(Sign::from_usize(self.current_player_index()).unwrap()); } }, - _ => return Err(TicTacToeError::TileOutOfBounds.into()), + _ => return err!(TicTacToeError::TileOutOfBounds), } self.update_state(); @@ -302,7 +302,7 @@ pub struct Tile { and the `TicTacToeError` type. ```rust,ignore -#[error] +#[error_code] pub enum TicTacToeError { TileOutOfBounds, TileAlreadySet, @@ -313,7 +313,7 @@ pub enum TicTacToeError { Finally, we can add the `play` function inside the program module. ```rust,ignore -pub fn play(ctx: Context, tile: Tile) -> ProgramResult { +pub fn play(ctx: Context, tile: Tile) -> Result<()> { let game = &mut ctx.accounts.game; require!( diff --git a/src/chapter_3/the_accounts_struct.md b/src/chapter_3/the_accounts_struct.md index 0aab020..f92418f 100644 --- a/src/chapter_3/the_accounts_struct.md +++ b/src/chapter_3/the_accounts_struct.md @@ -22,7 +22,7 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: u64) -> ProgramResult { + pub fn set_data(ctx: Context, data: u64) -> Result<()> { ctx.accounts.my_account.data = data; Ok(()) } @@ -57,7 +57,7 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: u64) -> ProgramResult { + pub fn set_data(ctx: Context, data: u64) -> Result<()> { if ctx.accounts.token_account.amount > 0 { ctx.accounts.my_account.data = data; } diff --git a/src/chapter_3/the_program_module.md b/src/chapter_3/the_program_module.md index 19c6150..fe954e6 100644 --- a/src/chapter_3/the_program_module.md +++ b/src/chapter_3/the_program_module.md @@ -5,7 +5,7 @@ The program module is where you define your business logic. You do so by writing #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: u64) -> ProgramResult { + pub fn set_data(ctx: Context, data: u64) -> Result<()> { if ctx.accounts.token_account.amount > 0 { ctx.accounts.my_account.data = data; } @@ -30,7 +30,7 @@ If your function requires instruction data, you can add it by adding arguments t #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: Data) -> ProgramResult { + pub fn set_data(ctx: Context, data: Data) -> Result<()> { ctx.accounts.my_account.data = data.data; ctx.accounts.my_account.age = data.age; Ok(()) @@ -61,7 +61,7 @@ Conveniently, `#[account]` implements `Anchor(De)Serialize` for `MyAccount`, so #[program] mod hello_anchor { use super::*; - pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { + pub fn set_data(ctx: Context, data: MyAccount) -> Result<()> { ctx.accounts.my_account.set_inner(data); Ok(()) }