use num_bigint::{BigInt, Sign};
use num_traits::Zero;
use crate::hash::keccak256_once;
use crate::out::Out;
fn two_pow_256() -> BigInt {
BigInt::from(1) << 256
}
#[derive(Debug, Clone)]
pub enum AbiValue {
Uint(BigInt),
Int(i64),
Uint64(u64),
Bool(bool),
Bytes(Vec<u8>),
Str(String),
Out(Out),
}
struct AbiString {
offset: usize,
data: Vec<u8>,
}
#[derive(Default)]
pub struct AbiBuffer {
buf: Vec<u8>,
str: Vec<AbiString>,
}
impl AbiBuffer {
pub fn new(buf: Vec<u8>) -> AbiBuffer {
AbiBuffer {
buf,
str: Vec::new(),
}
}
pub fn encode_auto(&mut self, params: &[AbiValue]) -> Result<(), String> {
for p in params {
match p {
AbiValue::Int(o) => self.append_big_int(&BigInt::from(*o))?,
AbiValue::Uint64(o) => self.append_big_int(&BigInt::from(*o))?,
AbiValue::Uint(o) => self.append_big_int(o)?,
AbiValue::Bool(b) => self.append_big_int(&BigInt::from(*b as u8))?,
AbiValue::Bytes(o) => self.append_bytes(o),
AbiValue::Str(s) => self.append_bytes(s.as_bytes()),
AbiValue::Out(o) => {
if o.name == "evm" || o.name == "eth" {
self.append_big_int(&BigInt::from_bytes_be(Sign::Plus, o.bytes()))?;
} else {
return Err(format!("unsupported value type {} for EVM", o.name));
}
}
}
}
Ok(())
}
pub fn encode_abi(&mut self, abi: &str, params: &[AbiValue]) -> Result<(), String> {
let pos = abi
.find('(')
.ok_or("invalid abi format (could not locate start of parameters)")?;
if !abi.ends_with(')') {
return Err("invalid abi format (does not end with a closing parenthesis)".into());
}
let inner = &abi[pos + 1..abi.len() - 1];
let types: Vec<&str> = if inner.is_empty() {
Vec::new()
} else {
inner.split(',').collect()
};
self.encode_types(&types, params)
}
pub fn encode_types(&mut self, types: &[&str], params: &[AbiValue]) -> Result<(), String> {
if types.len() != params.len() {
return Err("wrong number of arguments".into());
}
for (t, p) in types.iter().zip(params.iter()) {
match *t {
"uint" | "uint8" | "uint16" | "uint32" | "uint64" | "uint256" | "bytes4"
| "bytes32" => self.append_uint256_any(p)?,
"address" => self.append_address_any(p)?,
"bytes" | "string" => self.append_buffer_any(p)?,
other => return Err(format!("unsupported type: {other}")),
}
}
Ok(())
}
fn append_uint256_any(&mut self, v: &AbiValue) -> Result<(), String> {
match v {
AbiValue::Bool(b) => self.append_big_int(&BigInt::from(*b as u8)),
AbiValue::Int(o) => self.append_big_int(&BigInt::from(*o)),
AbiValue::Uint64(o) => self.append_big_int(&BigInt::from(*o)),
AbiValue::Uint(o) => self.append_big_int(o),
other => Err(format!(
"unsupported type {other:?} for evm abi uint256-style type"
)),
}
}
fn append_address_any(&mut self, v: &AbiValue) -> Result<(), String> {
Err(format!("unsupported type {v:?} for evm abi type address"))
}
fn append_buffer_any(&mut self, v: &AbiValue) -> Result<(), String> {
match v {
AbiValue::Bytes(o) => {
self.append_bytes(o);
Ok(())
}
AbiValue::Str(s) => {
self.append_bytes(s.as_bytes());
Ok(())
}
other => Err(format!(
"unsupported type {other:?} for evm abi buffer type"
)),
}
}
pub fn append_big_int(&mut self, v: &BigInt) -> Result<(), String> {
let mut val = v.clone();
if val.sign() == Sign::Minus {
val = two_pow_256() - val;
if val.sign() != Sign::Plus {
return Err("big.Int value exceeds negative 256 bits".into());
}
}
if val >= two_pow_256() {
return Err("big.Int value exceeds 256 bits".into());
}
let mut inbuf = [0u8; 32];
let (_, bytes) = val.to_bytes_be();
inbuf[32 - bytes.len()..].copy_from_slice(&bytes);
self.buf.extend_from_slice(&inbuf);
Ok(())
}
pub fn append_bytes(&mut self, v: &[u8]) {
let pos = self.buf.len();
self.buf.extend_from_slice(&[0u8; 32]);
let mut len_buf = [0u8; 32];
let len_bytes = (v.len() as u64).to_be_bytes();
len_buf[24..].copy_from_slice(&len_bytes);
let mut data = len_buf.to_vec();
data.extend_from_slice(v);
self.str.push(AbiString { offset: pos, data });
}
pub fn bytes(&self) -> Vec<u8> {
let mut res = self.buf.clone();
for s in &self.str {
let mut in_ = s.data.clone();
let x = in_.len() % 32;
if x != 0 {
in_.extend(std::iter::repeat_n(0u8, 32 - x));
}
let pos = res.len() as u64;
let mut pos_buf = [0u8; 32];
pos_buf[24..].copy_from_slice(&pos.to_be_bytes());
res[s.offset..s.offset + 32].copy_from_slice(&pos_buf);
res.extend_from_slice(&in_);
}
res
}
pub fn call(&self, method: &str) -> Vec<u8> {
let hash = keccak256_once(method.as_bytes());
let mut out = hash[..4].to_vec();
out.extend_from_slice(&self.bytes());
out
}
}
pub fn evm_call(method: &str, params: &[AbiValue]) -> Result<Vec<u8>, String> {
let mut buf = AbiBuffer::default();
buf.encode_abi(method, params)?;
Ok(buf.call(method))
}
#[allow(dead_code)]
fn _zero() -> BigInt {
BigInt::zero()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::address::parse_evm_address;
#[test]
fn transfer_encode_auto() {
let mut buf = AbiBuffer::default();
let addr = parse_evm_address("0x5Fb84129AD9E7818F099966de975ff41213F028d").unwrap();
buf.encode_auto(&[
AbiValue::Out(addr),
AbiValue::Uint(BigInt::from(123456789123456789u64)),
])
.unwrap();
let call = buf.call("transfer(address,uint256)");
assert_eq!(
hex::encode(call),
"a9059cbb0000000000000000000000005fb84129ad9e7818f099966de975ff41213f028d00000000000000000000000000000000000000000000000001b69b4bacd05f15"
);
}
#[test]
fn cast_vote_with_reason() {
let call = evm_call(
"castVoteWithReason(uint256,uint8,string)",
&[
AbiValue::Int(123456789123456789),
AbiValue::Int(1),
AbiValue::Str("this is a test".into()),
],
)
.unwrap();
assert_eq!(
hex::encode(call),
"7b3c71d300000000000000000000000000000000000000000000000001b69b4bacd05f1500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000e7468697320697320612074657374000000000000000000000000000000000000"
);
}
}