Crate solabi

source ·
Expand description

Solidity ABI encoding and decoding implementation.

At a high level, this crate provides Solidity ABI encode()-ing and decode()-ing implementations for a collection of primitive types, as well as generic implementations for various collections and tuple types.

let encoded = solabi::encode(&(42_i32, true, Bytes::borrowed(&[0, 1, 2, 3])));
let (a, b, c): (i32, bool, Bytes<Vec<u8>>) = solabi::decode(&encoded).unwrap();
assert_eq!(a, 42);
assert_eq!(b, true);
assert_eq!(c.as_bytes(), [0, 1, 2, 3]);

Encoders

Furthermore, this library provides encoders for various Solidity ABI items:

These encoders provide a type-safe interface for Solidity encoding and decoding their parameters.

const TRANSFER: FunctionEncoder<(Address, U256), (bool,)> =
    FunctionEncoder::new(selector!("transfer(address,uint256)"));

let call = TRANSFER.encode_params(&(
    address!("0x0101010101010101010101010101010101010101"),
    uint!("4_200_000_000_000_000_000"),
));

Dynamic Values

The value::Value type provides dynamic Solidity values. This allows Solidity ABI encoding and decoding when types are not known at compile-time.

let event = abi::EventDescriptor::parse_declaration(
    "event Transfer(address indexed to, address indexed from, uint256 value)",
)
.unwrap();
let encoder = value::EventEncoder::new(&event).unwrap();
let log = encoder
    .encode(&[
        Value::Address(address!("0x0101010101010101010101010101010101010101")),
        Value::Address(address!("0x0202020202020202020202020202020202020202")),
        Value::Uint(value::Uint::new(256, uint!("4_200_000_000_000_000_000")).unwrap()),
    ])
    .unwrap();

Custom Encoding Implementation

It can be useful to define custom types that encode and decode to the Solidity ABI. This can be done by implementing the encode::Encode and decode::Decode traits.

use solabi::{
    encode::{Encode, Encoder, Size},
    decode::{Decode, DecodeError, Decoder},
};

#[derive(Debug, Eq, PartialEq)]
struct MyStruct {
    a: u64,
    b: String,
}

impl Encode for MyStruct {
    fn size(&self) -> Size {
        (self.a, self.b.as_str()).size()
    }

    fn encode(&self, encoder: &mut Encoder) {
        (self.a, self.b.as_str()).encode(encoder);
    }
}

impl Decode for MyStruct {
    fn is_dynamic() -> bool {
        <(u64, String)>::is_dynamic()
    }

    fn decode(decoder: &mut Decoder) -> Result<Self, DecodeError> {
        let (a, b) = Decode::decode(decoder)?;
        Ok(Self { a, b })
    }
}

let my_struct = MyStruct {
    a: 42,
    b: "The Answer to Life the Universe and Everything".to_string(),
};

let encoded = solabi::encode(&my_struct);
let decoded = solabi::decode(&encoded).unwrap();

assert_eq!(my_struct, decoded);

There are plans to provide Encode and Decode procedural macros to automatically implement these traits in the future.

Re-exports

Modules

  • Solidity ABI descriptors.
  • Solidity bytes type.
  • Solidity ABI constructor encoding.
  • Solidity ABI decoding.
  • Module implementing ABI encoding.
  • Solidity ABI error encoding.
  • Module containing Solidity event related traits and logic.
  • Solidity ABI function pointer type.
  • A const fn Keccak-256 implementation.
  • Module implementing an EVM log datatype.
  • The solabi prelude.
  • Solidity primitive type trait and implementations.
  • Module containing dynamic Solidity value.

Macros

  • Macro to create Ethereum public address values from string literals that get verified at compile time. A compiler error will be generated if an invalid address is specified.
  • Macro to create Ethereum digest values from string literals that get parsed at compile time. A compiler error will be generated if an invalid digest is specified.
  • Macro for 256-bit signed integer literal.
  • Macro to create Ethereum digest values for compile-time hashed input.
  • Macro for creating a compile-time computed function selector.
  • Macro for 256-bit unsigned integer literal.