Interoperability with solidity `bytesN` types
- Dominant language
- Rust
- Stars
- 1.7k
- Forks
- 218
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 6
Description
### What is wrong?
Fe doesn't have direct support for solidity's `bytesN` types (eg `bytes4`), so it's not possible to define a public function that mimics a solidity function that takes a `bytesN`. For example, it's not possible to implement the POS deposit contract's `deposit` function:
```
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
```
The closest we can get in fe is to replace the `bytes32` with `u256:
```
pub fn deposit(
pubkey: Array,
withdrawal_credentials: Array,
signature: Array,
deposit_data_root: u256,
)
```
This would work fine, except that the function selector hash of the fe function won't match that of the solidity function, because the selector hash includes the types of the arguments, and there's no way to override that in fe.
Note that the function selector hash *does not* include the return type, so using `u256` as the return type when implementing a solidiy interface function that should return eg `bytes4` is fine. (But note that the 4 bytes of the `bytes4` should be the leftmost bytes in the u256, eg `[1, 2, 3, 4]` => `0x010203040000..0`)
### How can it be fixed
Tentative thoughts from a chat on Nov 12:
We could require that people define a `__call__` function (#559) that manually does solidity function selector stuff, but that's not a great user interface for what should be a simple problem.
Or, we could provide some way to override the function selector hash for a fe function to match that of the solidity function to be mimicked:
```
#[selector_id = "0xfefacade"]
pub fn deposit(..., deposit_data_root: u256)
```
Though that selector hash doesn't seem to be written out in the abi json, so maybe that's hard to find. Maybe something like:
```
#[abi_signature = "deposit(bytes,bytes,bytes,bytes32)"]
```
(which of course requires knowledge of solidity's signature encoding scheme).
Or, we could provide `bytesN` types in the standard library that would match the semantics and abi behavior of solidity's types:
```
use std::solidity::types::bytes32
pub fn deposit(..., deposit_data_root: bytes32):
...
```
Ideally, these types could be defined in fe itself, rather than being implemented in the compiler. To do so, we need:
1) A way to define a new value type, eg `newtype bytes32 = u256` (doesn't have to be the actual syntax).
Right now, structs are always reference types, so
```
struct bytes32:
bytes: u256
# OR
struct bytes32:
bytes: Array
```
would work, but would be needlessly expensive. Perhaps fe will someday gain explicit reference semantics, in which case structs and arrays could be values. We could use `type bytes32 = u256`, but that's just a simple alias. Note that every `bytesN` type would just be a `u256` value in disguise, with the useful `N` bytes on the "left" side (ie zero-padded on the right out to 32 bytes).
2) A way to specify how a type should be abi-encoded and decoded:
```
trait Abi:
fn encode(self) -> u256
# Ideally the return value here could be more than 32 bytes somehow,
# but a single u256 would suffice for this use-case
fn decode(data: CallDataSlice<32>) -> Self
# dunno what the arg type should be here.
# also dunno how to handle decoding failure. `Result` someday?
impl Abi for bytes32:
fn encode(self) -> u256:
# How does one turn a newtype wrapper into its underlying type?
...
```
We also need a string version of the type name to compute the function selector hash; in this example "bytes32" is exactly the string we need to match solidity's type, so we could just naively stringify the fe type name for now.
3) A way to define functions on a type:
```
impl bytes32:
pub fn from_array(a: Array) -> bytes32: ...
pub fn as_array(self) -> Array): ...
pub fn as_u256(self) -> u256: ...
```
These could be implementations of `From` and `To` traits, but we don't really need that level of sophistication for this functionality.
Contributor guide
No contributing guide indexed for this repository
Research direction
Read related issue #559 and compare the selector-override, bytesN-type, and ABI-trait proposals, using the POS deposit signature as the concrete interoperability case. Done means the chosen design specifies Solidity-compatible bytesN ABI encoding and decoding, matching function selectors, and conversion semantics for the example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, solidity
- Domain
- blockchain, compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100