-
Notifications
You must be signed in to change notification settings - Fork 248
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
Standardize errors #1165
base: master
Are you sure you want to change the base?
Standardize errors #1165
Changes from 24 commits
7260447
9ec4723
d5d5298
f4a27e0
1243da8
5d166e7
56e5549
9c22faa
b005c35
53ef6fd
3a2a41a
1d4ec49
b79ece8
8cfa0da
1d2b594
c2b397a
47ad490
0b37e7c
eceb288
d35d1cf
7ecede5
4ee2aba
7a14556
48576b2
966d400
7ec7ac4
9f96ec1
52a426c
8935303
aea83c7
b427177
74b54ea
81c9559
8409206
352a873
8167824
26c2620
160651e
d497ccd
c646470
52e1041
adbffbc
080250e
745fe09
ca84208
8e3a59c
17df48c
64cf673
6e39ba3
2f837cc
63ec033
bd96f6d
2dfa359
14e2b94
0ed933e
67230f9
ed3e2c9
acee075
556e18d
a82fc23
43a0360
43ac3ef
a49df2d
410d621
3564b20
93c47e2
c0f4a90
47aec44
9f2f4f0
7e5574f
89d8552
b0d0d5e
70e219b
ee90f67
b8e388b
7b31261
f5e9db6
9d2c9db
2e90e0f
fb636c1
fe9fd69
6e4349a
b9a2282
ba588e5
eb386b4
6a0096a
666eb9f
873627c
65a23af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[target.wasm32-unknown-unknown] | ||
rustflags = ["-C", "link-arg=-s"] | ||
|
||
[build] | ||
target-dir = "../../target" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[package] | ||
name = "error-handling" | ||
version = "0.1.0" | ||
authors = ["Near Inc <[email protected]>"] | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
near-sdk = { path = "../../near-sdk" } | ||
|
||
[dev-dependencies] | ||
near-workspaces = { version = "0.9.0", default-features = false, features = ["install"] } | ||
test-case = "2.0" | ||
tokio = { version = "1.14", features = ["full"] } | ||
anyhow = "1.0" | ||
|
||
[profile.release] | ||
codegen-units = 1 | ||
# Tell `rustc` to optimize for small code size. | ||
opt-level = "z" | ||
lto = true | ||
debug = false | ||
panic = "abort" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# error-handling | ||
|
||
cargo-near-new-project-description | ||
|
||
## How to Build Locally? | ||
|
||
Install [`cargo-near`](https://github.com/near/cargo-near) and run: | ||
|
||
```bash | ||
cargo near build | ||
``` | ||
|
||
## How to Test Locally? | ||
|
||
```bash | ||
cargo test | ||
``` | ||
|
||
## How to Deploy? | ||
|
||
Deployment is automated with GitHub Actions CI/CD pipeline. | ||
To deploy manually, install [`cargo-near`](https://github.com/near/cargo-near) and run: | ||
|
||
```bash | ||
cargo near deploy <account-id> | ||
``` | ||
|
||
## Useful Links | ||
|
||
- [cargo-near](https://github.com/near/cargo-near) - NEAR smart contract development toolkit for Rust | ||
- [near CLI](https://near.cli.rs) - Iteract with NEAR blockchain from command line | ||
- [NEAR Rust SDK Documentation](https://docs.near.org/sdk/rust/introduction) | ||
- [NEAR Documentation](https://docs.near.org) | ||
- [NEAR StackOverflow](https://stackoverflow.com/questions/tagged/nearprotocol) | ||
- [NEAR Discord](https://near.chat) | ||
- [NEAR Telegram Developers Community Group](https://t.me/neardev) | ||
- NEAR DevHub: [Telegram](https://t.me/neardevhub), [Twitter](https://twitter.com/neardevhub) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/bin/bash | ||
TARGET="${CARGO_TARGET_DIR:-../../target}" | ||
set -e | ||
cd "$(dirname $0)" | ||
|
||
cargo build --all --target wasm32-unknown-unknown --release | ||
cp $TARGET/wasm32-unknown-unknown/release/error_handling.wasm ./res/ |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,66 @@ | ||||||||||||||
// Find all our documentation at https://docs.near.org | ||||||||||||||
use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; | ||||||||||||||
use near_sdk::contract_error; | ||||||||||||||
use near_sdk::near; | ||||||||||||||
use near_sdk::FunctionError; | ||||||||||||||
|
||||||||||||||
#[contract_error] | ||||||||||||||
enum MyErrorEnum { | ||||||||||||||
X, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[contract_error] | ||||||||||||||
struct MyErrorStruct { | ||||||||||||||
x: u32, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[near(contract_state)] | ||||||||||||||
#[derive(Default)] | ||||||||||||||
pub struct Contract { | ||||||||||||||
value: u32, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[near] | ||||||||||||||
impl Contract { | ||||||||||||||
#[handle_result] | ||||||||||||||
pub fn inc_handle_result(&mut self, is_error: bool) -> Result<u32, &'static str> { | ||||||||||||||
self.value += 1; | ||||||||||||||
if is_error { | ||||||||||||||
return Err("error in inc_handle_result"); | ||||||||||||||
} else { | ||||||||||||||
return Ok(self.value); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's assume that readers of the examples are familiar with the fact that the last statement in Rust block is the return value, and don't explicitly use
Suggested change
P.S. Apply it in all other occurences |
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[persist_on_error] | ||||||||||||||
pub fn inc_persist_on_err(&mut self, is_error: bool) -> Result<u32, MyErrorEnum> { | ||||||||||||||
self.value += 1; | ||||||||||||||
if is_error { | ||||||||||||||
return Err(MyErrorEnum::X); | ||||||||||||||
} else { | ||||||||||||||
return Ok(self.value); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn inc_just_result(&mut self, is_error: bool) -> Result<u32, MyErrorStruct> { | ||||||||||||||
self.value += 1; | ||||||||||||||
if is_error { | ||||||||||||||
return Err(MyErrorStruct { x: 5 }); | ||||||||||||||
} else { | ||||||||||||||
return Ok(self.value); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn inc_just_simple(&mut self, is_error: bool) -> u32 { | ||||||||||||||
self.value += 1; | ||||||||||||||
if is_error { | ||||||||||||||
::near_sdk::env::panic_str("Error"); | ||||||||||||||
} else { | ||||||||||||||
return self.value; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn get_value(&self) -> u32 { | ||||||||||||||
self.value | ||||||||||||||
} | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use near_sdk::serde_json; | ||
use near_workspaces::Contract; | ||
use serde_json::json; | ||
use test_case::test_case; | ||
|
||
async fn get_value(contract: &Contract) -> anyhow::Result<u64> { | ||
let get_value: serde_json::Value = | ||
contract.call("get_value").args_json(json!({})).view().await?.json()?; | ||
|
||
println!("get_value: {:?}", get_value); | ||
|
||
get_value.as_u64().ok_or_else(|| anyhow::anyhow!("get_value is not a u64")) | ||
} | ||
|
||
async fn check_call( | ||
contract: &Contract, | ||
method: &str, | ||
is_error: bool, | ||
expected_value: u64, | ||
expected_error: Option<String>, | ||
) { | ||
let res = contract | ||
.call(method) | ||
.args_json(json!({ "is_error": is_error })) | ||
.max_gas() | ||
.transact() | ||
.await | ||
.unwrap(); | ||
if is_error { | ||
assert!(res.is_failure()); | ||
if let Some(expected_error) = expected_error { | ||
let string_error = | ||
format!("{:?}", res.failures()[0].clone().into_result().unwrap_err()); | ||
assert_eq!(string_error, expected_error); | ||
} | ||
} else { | ||
assert!(res.is_success()); | ||
} | ||
assert_eq!(get_value(&contract).await.unwrap(), expected_value); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_factorial() -> anyhow::Result<()> { | ||
let worker = near_workspaces::sandbox().await?; | ||
let contract = | ||
worker.dev_deploy(&std::fs::read(format!("res/{}.wasm", "error_handling"))?).await?; | ||
|
||
check_call(&contract, "inc_handle_result", false, 1, None).await; | ||
check_call(&contract, "inc_persist_on_err", false, 2, None).await; | ||
check_call(&contract, "inc_just_result", false, 3, None).await; | ||
check_call(&contract, "inc_just_simple", false, 4, None).await; | ||
check_call(&contract, "inc_handle_result", true, 4, None).await; | ||
check_call(&contract, "inc_persist_on_err", true, 5, Some("Error { repr: Custom { kind: Execution, error: ActionError(ActionError { index: Some(0), kind: FunctionCallError(ExecutionError(\"Smart contract panicked: {\\\"error\\\":{\\\"error_type\\\":\\\"error_handling::MyErrorEnum\\\",\\\"value\\\":\\\"X\\\"}}\")) }) } }".to_string())).await; | ||
check_call(&contract, "inc_just_result", true, 5, Some("Error { repr: Custom { kind: Execution, error: ActionError(ActionError { index: Some(0), kind: FunctionCallError(ExecutionError(\"Smart contract panicked: {\\\"error\\\":{\\\"error_type\\\":\\\"error_handling::MyErrorStruct\\\",\\\"value\\\":{\\\"x\\\":5}}}\")) }) } }".to_string())).await; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I talked with some engineers on nearcore side and it seems that there is no capacity on their side to address this error handling on their side :-( We will probably have to contribute it ourselves. I will guide you there once we will get to it. |
||
check_call(&contract, "inc_just_simple", true, 5, None).await; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's provide doc strings that illustrate the potential return values from the RPC response point of view.
P.S. Apply the same reasoning below