ethabi
ethabi copied to clipboard
Signed integer parameters are decoded as unsigned integers
Consider the following function. In this context data
is the data from the smart contract event logs and ethabi::decode
is being used to decode this data.
fn decode_log(data: Vec<u8>) -> i128 {
use ethabi::{ParamType, Token};
let params = ethabi::decode(&[ParamType::Int(128)], data).unwrap();
if let [Token::Int(change)] = params.as_slice() {
change.as_i128()
} else {
panic!("Invalid decoded parameters: {:?}", params);
}
}
This is supposed to parse the data of an event of the following signature:
event EventName(int128 change)
This code fails to compile because, counterintuitively, change
is of the type U256
, which is an unsigned type. This stems from the following line:
https://github.com/rust-ethereum/ethabi/blob/d862110b8c64fdff9bf68cf291ef8481801e859f/ethabi/src/lib.rs#L91
which seems completely wrong.
Is this done on purpose and, if so, how should one decode signed parameters? There seems to be no mention of it in the docs.
@nlordell this seems important for negative value logs right? should we add an I256 type to ethabi (either the one from ethers-core, and we can remove it from ethers, or the one you built in the past?) and use that as pub type Int
?
should we add an I256 type to ethabi
My favourite place to add it would be ethereum-types
or primitive-types
crate alongside the U256
type. The argument being that signed 256-bit math is part of the EVM so it makes the most sense to me there.
That being said, I think including it here also makes a lot of sense and I just marginally prefer having it in the aforementioned crates over ethabi
. Maybe we can start by moving it here and simultaneously asking around in those projects if they would accept including signed integers there?
Yep, supportive of importing to this package, and then seeing if they'd be willing to upstream.
Need this fixed !
Just solved this kind of problem. In case someone needs it:
fn convert_int256_to_f64(value: U256) -> Option<f64> {
let half_of_max_u256 = U256::from(u128::MAX);
let is_negative = value > half_of_max_u256;
let value = if is_negative {
let amount = U256::MAX - value;
str::parse::<f64>(&format!("-{amount}")).unwrap()
} else {
str::parse::<f64>(&value.to_string()).unwrap()
};
Some(value)
}