alloy-rs / core

High-performance, well-tested & documented core libraries for Ethereum, in Rust
https://alloy.rs
Apache License 2.0
763 stars 137 forks source link

abi encoding of tuples doesn’t seem right #733

Closed jayshrivastava closed 1 week ago

jayshrivastava commented 1 week ago

Component

sol-types

What version of Alloy are you on?

├── alloy-sol-types v0.8.0 │ ├── alloy-primitives v0.8.0 │ ├── alloy-sol-macro v0.8.0 (proc-macro) │ │ ├── alloy-sol-macro-expander v0.8.0 │ │ │ ├── alloy-sol-macro-input v0.8.0 │ │ ├── alloy-sol-macro-input v0.8.0 (*)

Operating System

macOS (Apple Silicon)

Describe the bug

Example 1 It looks like there's a leading 0x20 in the abi encoding of dynamic tuples:

let data: (u32, bool, Vec<u8>) = (1u32, true, vec![1u8]);
println!("A {:x?}", data.abi_encode());

The output of the above is:

A [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

I think that the output shows a bug because the first 32 bytes with 0x20 should not be there. Notice that the offset for the byte array 0x60 is 96 but with the given encoding, the vec length starts 128 bytes from the beginning. The correct encoding should be this:

// integer, left padded
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 
// bool, left padded
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
//offset (96 bytes from the beggining)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60,
//length
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 
// u8 data, right padded
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

This is the output you get using the ethers.js encoder as well:

import { defaultAbiCoder } from '@ethersproject/abi';
console.log(
  'all',
  defaultAbiCoder.encode(['uint', 'bool', 'bytes'], [1, true, '0x01'])
);
all 0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000

Example 2 I copied this example from these docs:

let data: (u128, Vec<u32>, [u8;10], Vec<u8>) = (0x123, vec![0x456, 0x789], <[u8; 10]>::try_from("1234567890".as_bytes().to_vec()).unwrap(), "Hello, world!".as_bytes().to_vec());
let encoded = data.abi_encode();
let mut encoded_s = vec![];
for b in encoded.iter() {
    encoded_s.push(format!("{:02x}", b));
}
println!("entire tuple v1 {:x?}", encoded_s.join(""));

The output has a leading 0x20 for some reason. Otherwise, the output matches the spec

000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000

Spec:

image
DaniPopes commented 1 week ago

Please use the encode_params method; see docs in the abi module

Also please use hex::encode since the default hex debug not that readable