Example: encoding_dyn_abi

Example

To run this example:

  • Clone the examples repository: git clone git@github.com:alloy-rs/examples.git
  • Run: cargo run --example encoding_dyn_abi
//! [EIP712](https://eips.ethereum.org/EIPS/eip-712) encoding and decoding via `dyn_abi`

use alloy::{
    dyn_abi::{DynSolType, DynSolValue},
    hex,
    primitives::{keccak256, Address, U256},
    signers::{local::PrivateKeySigner, Signer},
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // EIP-712 domain
    let domain_type = DynSolType::Tuple(vec![
        DynSolType::String,    // name
        DynSolType::String,    // version
        DynSolType::Uint(256), // chainId
        DynSolType::Address,   // verifyingContract
    ]);

    let domain_value = DynSolValue::Tuple(vec![
        DynSolValue::String("Alloy".to_string()),
        DynSolValue::String("1.0.1".to_string()),
        DynSolValue::Uint(U256::from(1), 256),
        DynSolValue::Address(Address::from([0x42; 20])),
    ]);

    // Message type (sample message)
    let message_type = DynSolType::Tuple(vec![
        DynSolType::Address, // from
        DynSolType::Address, // to
        DynSolType::String,  // contents
    ]);

    // Random values
    let message_value = DynSolValue::Tuple(vec![
        DynSolValue::Address(Address::from([0x11; 20])),
        DynSolValue::Address(Address::from([0x22; 20])),
        DynSolValue::String("EIP-712 encoding".to_string()),
    ]);

    // Encode the domain and message
    let encoded_domain = domain_value.abi_encode();
    let encoded_message = message_value.abi_encode();

    println!("Encoded domain: 0x{}", hex::encode(&encoded_domain));
    println!("Encoded message: 0x{}", hex::encode(&encoded_message));

    // Decode the domain and message
    let decoded_domain = domain_type.abi_decode(&encoded_domain)?;
    let decoded_message = message_type.abi_decode(&encoded_message)?;

    println!("\nDecoded domain:");
    print_tuple(&decoded_domain, &["name", "version", "chainId", "verifyingContract"]);

    println!("\nDecoded message:");
    print_tuple(&decoded_message, &["from", "to", "contents"]);

    // Calculate EIP-712 hash
    let domain_separator = keccak256(&encoded_domain);
    let message_hash = keccak256(&encoded_message);
    let eip712_hash = keccak256([&[0x19, 0x01], &domain_separator[..], &message_hash[..]].concat());

    println!("\nEIP-712 hash: 0x{}", hex::encode(eip712_hash));

    // Signing the hash via random signer
    // Ref: examples/wallets/examples/sign_message.rs

    // Create a signer
    let wallet = PrivateKeySigner::random();
    println!("\nSigner address: {}", wallet.address());

    // Sign the EIP-712 hash
    let signature = wallet.sign_hash(&eip712_hash).await?;
    println!("Signature: 0x{}", hex::encode(signature.as_bytes()));

    // Verify the signature
    let recovered_address = signature.recover_address_from_prehash(&eip712_hash)?;
    println!("Recovered address: {}", recovered_address);

    assert_eq!(recovered_address, wallet.address(), "Signature verification failed");
    println!("Signature verified successfully!");

    Ok(())
}

/// Utility function to print the decoded data.
fn print_tuple(value: &DynSolValue, field_names: &[&str]) {
    if let DynSolValue::Tuple(values) = value {
        for (value, name) in values.iter().zip(field_names.iter()) {
            println!("  {}: {:?}", name, value);
        }
    }
}

Find the source code on Github here.