-
Notifications
You must be signed in to change notification settings - Fork 151
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
Prestwich/dyn sol error #551
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d844146
feature: DynSolError
prestwich 33bf721
fix: proper Panic decoding
prestwich 5081f44
refactor: constants
prestwich db8645d
fix: no_std
prestwich aa9b86d
lint: clippy
prestwich 5a2e00e
fix: doc link
prestwich 87c6d75
refactor: selector declarations
prestwich 346b534
feature: body getter
prestwich 07c67cc
nit: linebreak
prestwich d18b51c
refactor: instantiation safety for event and error
prestwich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,125 @@ | ||
use crate::{DynSolType, DynSolValue, Error, Result}; | ||
use alloc::vec::Vec; | ||
use alloy_primitives::{keccak256, Selector}; | ||
use alloy_sol_types::SolError; | ||
|
||
/// See [alloy_sol_types::Panic] for signature details. | ||
const PANIC_SELECTOR: Selector = Selector::new(alloy_sol_types::Panic::SELECTOR); | ||
/// See [alloy_sol_types::Revert] for signature details. | ||
const REVERT_SELECTOR: Selector = Selector::new(alloy_sol_types::Revert::SELECTOR); | ||
|
||
/// A dynamic ABI error. | ||
/// | ||
/// This is a representation of a Solidity error, which can be used to decode | ||
/// error events. | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct DynSolError { | ||
/// Error selector. | ||
pub(crate) selector: Selector, | ||
/// Error body types. MUST be a tuple. | ||
pub(crate) body: DynSolType, | ||
} | ||
|
||
impl DynSolError { | ||
/// Represents a standard Solidity revert. These are thrown by | ||
/// `revert(reason)` or `require(condition, reason)` statements in Solidity. | ||
/// | ||
/// **Note**: Usage of this instantiator is not recommended. It is better to | ||
/// use [alloy_sol_types::Revert] in almost all cases. | ||
pub fn revert() -> Self { | ||
Self { selector: REVERT_SELECTOR, body: DynSolType::Tuple(vec![DynSolType::String]) } | ||
} | ||
|
||
/// A [Solidity panic]. | ||
/// | ||
/// **Note**: Usage of this instantiator is not recommended. It is better to | ||
/// use [alloy_sol_types::Panic] in almost all cases. | ||
/// | ||
/// These are thrown by `assert(condition)` and by internal Solidity checks, | ||
/// such as arithmetic overflow or array bounds checks. | ||
/// | ||
/// The list of all known panic codes can be found in the [PanicKind] enum. | ||
/// | ||
/// [Solidity panic]: https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require | ||
/// [PanicKind]: alloy_sol_types::PanicKind | ||
pub fn panic() -> Self { | ||
Self { selector: PANIC_SELECTOR, body: DynSolType::Tuple(vec![DynSolType::Uint(256)]) } | ||
} | ||
|
||
/// Creates a new error, without length-checking the body. This allows | ||
/// creation of invalid errors. | ||
pub const fn new_unchecked(selector: Selector, body: DynSolType) -> Self { | ||
Self { selector, body } | ||
} | ||
|
||
/// Creates a new error from a selector. | ||
pub fn new(selector: Selector, body: DynSolType) -> Option<Self> { | ||
let _ = body.as_tuple()?; | ||
Some(Self::new_unchecked(selector, body)) | ||
} | ||
|
||
/// Error selector is the first 4 bytes of the keccak256 hash of the error | ||
/// declaration. | ||
pub const fn selector(&self) -> Selector { | ||
self.selector | ||
} | ||
|
||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// Error body types. | ||
pub fn body(&self) -> &[DynSolType] { | ||
self.body.as_tuple().expect("body is a tuple") | ||
} | ||
|
||
/// Decode the error from the given data, which must already be stripped of | ||
/// its selector. | ||
fn decode_error_body(&self, data: &[u8]) -> Result<DecodedError> { | ||
let body = self.body.abi_decode_sequence(data)?.into_fixed_seq().expect("body is a tuple"); | ||
Ok(DecodedError { body }) | ||
} | ||
|
||
/// Decode the error from the given data. | ||
pub fn decode_error(&self, data: &[u8]) -> Result<DecodedError> { | ||
// Check selector validity. | ||
if !data.starts_with(self.selector.as_slice()) { | ||
return Err(Error::SelectorMismatch { | ||
expected: self.selector, | ||
actual: Selector::from_slice(&data[0..4]), | ||
}); | ||
} | ||
|
||
// will not panic, as we've already checked the length with starts_with | ||
let data = data.split_at(4).1; | ||
self.decode_error_body(data) | ||
} | ||
} | ||
|
||
/// A decoded dynamic ABI error. | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct DecodedError { | ||
/// The decoded error body. | ||
pub body: Vec<DynSolValue>, | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::DynSolError; | ||
use crate::DynSolValue; | ||
use alloy_primitives::hex; | ||
|
||
#[test] | ||
fn decode_revert_message() { | ||
let error = DynSolError::revert(); | ||
let data = hex!("08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000042020202000000000000000000000000000000000000000000000000000000000"); | ||
|
||
let decoded = error.decode_error(&data).unwrap(); | ||
assert_eq!(decoded.body, vec!(DynSolValue::String(" ".into()))); | ||
} | ||
|
||
#[test] | ||
fn decode_panic() { | ||
let error = DynSolError::panic(); | ||
let data = hex!("4e487b710000000000000000000000000000000000000000000000000000000000000001"); | ||
|
||
let decoded = error.decode_error(&data).unwrap(); | ||
assert_eq!(decoded.body, vec![DynSolValue::Uint(alloy_primitives::Uint::from(1), 256)]); | ||
} | ||
} |
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,14 @@ | ||
mod error; | ||
pub use error::{DecodedError, DynSolError}; | ||
|
||
mod event; | ||
pub use event::{DecodedEvent, DynSolEvent}; | ||
|
||
pub(crate) mod ty; | ||
pub use ty::DynSolType; | ||
|
||
mod token; | ||
pub use token::DynToken; | ||
|
||
mod value; | ||
pub use value::DynSolValue; |
File renamed without changes.
File renamed without changes.
File renamed without changes.
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,36 @@ | ||
use crate::{DynSolError, Specifier}; | ||
use alloc::vec::Vec; | ||
use alloy_json_abi::Error; | ||
use alloy_primitives::{keccak256, Selector}; | ||
|
||
mod sealed { | ||
pub trait Sealed {} | ||
impl Sealed for alloy_json_abi::Error {} | ||
} | ||
use sealed::Sealed; | ||
|
||
impl Specifier<DynSolError> for Error { | ||
fn resolve(&self) -> crate::Result<DynSolError> { | ||
let signature = self.signature(); | ||
let selector = Selector::from_slice(&keccak256(signature)[0..4]); | ||
|
||
let mut body = Vec::with_capacity(self.inputs.len()); | ||
for param in &self.inputs { | ||
body.push(param.resolve()?); | ||
} | ||
|
||
Ok(DynSolError::new_unchecked(selector, crate::DynSolType::Tuple(body))) | ||
} | ||
} | ||
|
||
/// Provides error encoding and decoding for the [`Error`] type. | ||
pub trait ErrorExt: sealed::Sealed { | ||
/// Decode the error from the given data. | ||
fn decode_error(&self, data: &[u8]) -> crate::Result<crate::DecodedError>; | ||
} | ||
|
||
impl ErrorExt for alloy_json_abi::Error { | ||
fn decode_error(&self, data: &[u8]) -> crate::Result<crate::DecodedError> { | ||
self.resolve()?.decode_error(data) | ||
} | ||
} |
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 |
---|---|---|
|
@@ -3,3 +3,6 @@ pub use abi::{FunctionExt, JsonAbiExt}; | |
|
||
mod event; | ||
pub use event::EventExt; | ||
|
||
mod error; | ||
pub use error::ErrorExt; |
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
File renamed without changes.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Vec<DynSolType>
?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.
DynSolEvent
usesDynSolType
for body. could change both?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.
i'd rather leave as-is for now