-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(evm): improve error handling (#463)
* handle errors more gracefully * setup initial tests * ci * add try catch * style: resolve style guide violations * fix --------- Co-authored-by: oXtxNt9U <[email protected]>
- Loading branch information
Showing
12 changed files
with
584 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use napi::{JsBuffer, JsString}; | ||
use napi_derive::napi; | ||
use revm::primitives::{Address, Bytes}; | ||
|
||
use crate::utils; | ||
|
||
#[napi(object)] | ||
pub struct JsTransactionContext { | ||
pub caller: JsString, | ||
/// Omit recipient when deploying a contract | ||
pub recipient: Option<JsString>, | ||
pub data: JsBuffer, | ||
} | ||
|
||
pub struct TxContext { | ||
pub caller: Address, | ||
/// Omit recipient when deploying a contract | ||
pub recipient: Option<Address>, | ||
pub data: Bytes, | ||
} | ||
|
||
impl TryFrom<JsTransactionContext> for TxContext { | ||
type Error = anyhow::Error; | ||
|
||
fn try_from(value: JsTransactionContext) -> std::result::Result<Self, Self::Error> { | ||
let buf = value.data.into_value()?; | ||
|
||
let recipient = if let Some(recipient) = value.recipient { | ||
Some(utils::create_address_from_js_string(recipient)?) | ||
} else { | ||
None | ||
}; | ||
|
||
let tx_ctx = TxContext { | ||
recipient, | ||
caller: utils::create_address_from_js_string(value.caller)?, | ||
data: Bytes::from(buf.as_ref().to_owned()), | ||
}; | ||
|
||
Ok(tx_ctx) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use napi::{JsBigInt, JsBuffer, JsString}; | ||
use napi_derive::napi; | ||
use revm::primitives::{Bytes, Log}; | ||
|
||
#[napi(object)] | ||
pub struct JsTransactionResult { | ||
pub gas_used: JsBigInt, | ||
pub gas_refunded: JsBigInt, | ||
pub success: bool, | ||
pub deployed_contract_address: Option<JsString>, | ||
|
||
// TODO: typing | ||
pub logs: serde_json::Value, | ||
pub output: Option<JsBuffer>, | ||
} | ||
|
||
pub struct TxResult { | ||
pub gas_used: u64, | ||
pub gas_refunded: u64, | ||
pub success: bool, | ||
// TODO: expose additional data needed to JS | ||
pub deployed_contract_address: Option<String>, | ||
pub logs: Option<Vec<Log>>, | ||
pub output: Option<Bytes>, | ||
} | ||
|
||
impl JsTransactionResult { | ||
pub fn new(node_env: napi::Env, result: TxResult) -> anyhow::Result<Self> { | ||
let deployed_contract_address = | ||
if let Some(contract_address) = result.deployed_contract_address { | ||
Some(node_env.create_string_from_std(contract_address)?) | ||
} else { | ||
None | ||
}; | ||
|
||
let result = JsTransactionResult { | ||
gas_used: node_env.create_bigint_from_u64(result.gas_used)?, | ||
gas_refunded: node_env.create_bigint_from_u64(result.gas_refunded)?, | ||
success: result.success, | ||
deployed_contract_address, | ||
logs: result | ||
.logs | ||
.map(|l| serde_json::to_value(l).unwrap()) | ||
.unwrap_or_else(|| serde_json::Value::Null), | ||
output: result.output.map(|o| { | ||
node_env | ||
.create_buffer_with_data(Into::<Vec<u8>>::into(o)) | ||
.unwrap() | ||
.into_raw() | ||
}), | ||
}; | ||
|
||
Ok(result) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use std::str::FromStr; | ||
|
||
use anyhow; | ||
use napi::JsString; | ||
use revm::primitives::Address; | ||
|
||
pub(crate) fn create_address_from_js_string(js_str: JsString) -> anyhow::Result<Address> { | ||
let js_str = js_str.into_utf8()?; | ||
let slice = js_str.as_str()?; | ||
Ok(Address::from_str(slice)?) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { Contracts } from "@mainsail/contracts"; | ||
|
||
import { describe, Sandbox } from "../../test-framework"; | ||
import { bytecode } from "../test/fixtures/MainsailERC20.json"; | ||
import { wallets } from "../test/fixtures/wallets"; | ||
import { prepareSandbox } from "../test/helpers/prepare-sandbox"; | ||
import { Instance } from "./instance"; | ||
|
||
describe<{ | ||
sandbox: Sandbox; | ||
instance: Contracts.Evm.Instance; | ||
}>("Instance", ({ it, assert, beforeEach }) => { | ||
beforeEach(async (context) => { | ||
await prepareSandbox(context); | ||
|
||
context.instance = context.sandbox.app.resolve<Contracts.Evm.Instance>(Instance); | ||
}); | ||
|
||
it("should deploy contract successfully", async ({ instance }) => { | ||
const [sender] = wallets; | ||
|
||
const result = await instance.transact({ | ||
caller: sender.address, | ||
data: Buffer.from(bytecode.slice(2), "hex"), | ||
}); | ||
|
||
assert.true(result.success); | ||
assert.equal(result.gasUsed, 964_156n); | ||
assert.equal(result.deployedContractAddress, "0x0c2485e7d05894BC4f4413c52B080b6D1eca122a"); | ||
}); | ||
|
||
it("should revert on invalid call", async ({ instance }) => { | ||
const [sender] = wallets; | ||
|
||
let result = await instance.transact({ | ||
caller: sender.address, | ||
data: Buffer.from(bytecode.slice(2), "hex"), | ||
}); | ||
|
||
const contractAddress = result.deployedContractAddress; | ||
assert.defined(contractAddress); | ||
|
||
result = await instance.transact({ | ||
caller: sender.address, | ||
data: Buffer.from("0xdead", "hex"), | ||
recipient: contractAddress, | ||
}); | ||
|
||
assert.false(result.success); | ||
assert.equal(result.gasUsed, 21_070n); | ||
}); | ||
|
||
it("should throw on invalid tx context caller", async ({ instance }) => { | ||
await assert.rejects( | ||
async () => | ||
await instance.transact({ | ||
caller: "badsender_", | ||
data: Buffer.from(bytecode.slice(2), "hex"), | ||
}), | ||
); | ||
}); | ||
}); |
Oops, something went wrong.