tempo icon indicating copy to clipboard operation
tempo copied to clipboard

feat(precompiles)!: migrate to new API

Open 0xrusowsky opened this issue 3 weeks ago • 3 comments

Motivation

Improve the devex by introducing an API that feels like native Rust and, at the same time, closer to the usual Solidity patterns.

This PR closes #860 and achieves the final goal of #856

Solution

The type system changes of this PR have been proposed in its own PR #986 and have already been extensively reviewed. The feedback from the several review rounds has been incorporated and re-reviewed.

However, PR #986 can't be directly merged into main because it uses a new storage access pattern, and it also changes the precompiles API. These breaking changes made it impossible to merge the storage type system changes without a full migration.

Reviewing this PR

If you haven't been involved in the storage type system PR review, you are more than welcome (even encouraged) to have a look at the files under crates/precompiles/src/storage/ or at the #986 PR (which is around 8k diff, although most of it are tests).

With that being said, the main focus of this PR review should be the migration of the precompiles to the new API, as well as their integration within the rest of the crates in the monorepo.

Out of the ~10k diff (excluding the storage type system changes), most of the diff are because of tests.

Please review super carefully the implementation changes, as they should preserve the logic untouched.

Using the new storage API

unlike previously, where we had to hold a mutable reference to the storage provider (type implementing trait PrecompileStorageProvider), we now use the scoped_tls crate to generate a scoped thread-local variable. In practice, this means that our storage interactions will be done using the zero-sized StorageContext need to use the following pattern:

let mut provider = EvmPrecompileStorageProvider::new();

// we must always "enter" to setup the storage provider as a thread-local variable
StorageContext::enter(&mut provider, || {
    // inside the closure, we can access all the `trait PrecompileStorageProvider` fns via `StorageContext`
    // which performs them atomically. Thus, precompiles don't need to hold storage refs, and we won't have
    // mutable ref borrow conflicts anymore.

    let current_time = StorageContext.timestamp();
    let spec = StorageContext.sepc();

    let path_usd = PathUSD::new();
    path_usd.grant_role_internal(admin, *ISSUER_ROLE)?;
        
    let token = TIP20Token::new(1);
    token.grant_role_internal(admin, *ISSUER_ROLE)?;
});

Using the new precompiles API

precompiles now use handlers for interacting with storage, rather than low-level helper functions like before. In pratice, this means that we can consume them with a solidity-like devex:

#[contract]
pub struct TIP20Token {
    // ...
    total_supply: U256,
    balances: Mapping<Address, U256>,
    allowances: Mapping<Address, Mapping<Address, U256>>,
    // ...
}

impl TIP20Token {
    fn get_allowance(&self, owner: Address, spender: Address) -> Result<U256> {
        self.allowances.at(owner).at(spender).read()
    }

    fn set_allowance(&mut self, owner: Address, spender: Address, amount: U256) -> Result<()> {
        self.allowances.at(owner).at(spender).write(amount)
    }
}

handlers have powerful APIs provide us better qol:

 // Base handler for all storage operations. Other handlers delegate to `Slot`.
 struct Slot<T> {
     pub fn read(&self) -> Result<T>
     pub fn write(&mut self, value: T) -> Result<()>
     pub fn delete(&mut self) -> Result<()>
 }
 // Returns a handler for the given key.
 // For nested mappings (Mapping<K1, Mapping<K2, V>>), returns another Mapping.
 struct Mapping<K, V> {
     pub fn at(&self, key: K) -> <V as StorableType>::Handler
 }
 // Generated by `#[derive(Storable)]`, its fields are handlers.
 // This means that we can load each field individually
 struct MyStructHandler {
     pub foo: Slot<Address>,
     pub bar: Mapping<u64, U256>,
 }
 impl Handler<MyStruct> for MyStructHandler {
     pub fn read(&self) -> Result<MyStruct>
     pub fn write(&mut self, value: MyStruct) -> Result<()>
     pub fn delete(&mut self) -> Result<()>
 }
 // Fixed-size array. Element access is checked (returns an option).
 struct ArrayHandler<T, N> {
     pub fn read(&self) -> Result<[T; N]>
     pub fn write(&mut self, value: [T; N]) -> Result<()>
     pub fn delete(&mut self) -> Result<()>
     pub fn at(&self, index: usize) -> Option<<T as StorableType>::Handler>
     pub const fn len(&self) -> usize
 }
 // Dynamic array with stack-like ops. Supports multi-slot element types.
 struct VecHandler<T> {
     pub fn read(&self) -> Result<Vec<T>>
     pub fn write(&mut self, value: Vec<T>) -> Result<()>
     pub fn delete(&mut self) -> Result<()>
     pub fn at(&self, index: usize) -> <T as StorableType>::Handler
     pub fn len(&self) -> Result<usize>
     pub fn push(&self, value: T) -> Result<()>
     pub fn pop(&self) -> Result<Option<T>>
 }

TODO

  • [x] migrate precompiles to the new storage API
  • [X] update other crates to the new storage API
  • [x] ensure all tests pass
  • [ ] ensure gas snapshot tests pass

0xrusowsky avatar Dec 02 '25 17:12 0xrusowsky

@0xrusowsky must be a member of the Tempo team on Vercel to deploy. - Click here to add @0xrusowsky to the team. - If you initiated this build, request access.

Learn more about collaboration on Vercel and other options here.

vercel[bot] avatar Dec 04 '25 17:12 vercel[bot]

@klkvr must be a member of the Tempo team on Vercel to deploy. - Click here to add @klkvr to the team. - If you initiated this build, request access. - If you're already a member of the Tempo team, make sure that your Vercel account is connected to your GitHub account.

Learn more about collaboration on Vercel and other options here.

vercel[bot] avatar Dec 07 '25 23:12 vercel[bot]

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
tempo-docs Ignored Ignored Preview Dec 11, 2025 10:10pm

vercel[bot] avatar Dec 10 '25 09:12 vercel[bot]