alloy-rs / alloy

Transports, Middleware, and Networks for the Alloy project
https://alloy.rs
Apache License 2.0
639 stars 232 forks source link

[Feature] Suggestion to allow inter-compatible extensions for alloy #829

Open NicolasWent opened 5 months ago

NicolasWent commented 5 months ago

Component

provider, pubsub

Describe the feature you would like

The goal of my suggestion would be to allow people to easily build extensions of alloy.

I know that there is already the provider trait which can be implemented. It is already amazing for customization, but my suggestion would go way further and allow to solve the following problem:

Example

Imagine that I build a huge application in alloy, it access blocks and logs frequently. I realize that the application is pretty slow, so I am thinking of ways to improve the speed.

I find out that someone built a crate that allow to have block caching in alloy (this crate does not exist currently, but I imagine that someday, someone may build that).

This extension was perfect and solved the bottleneck of blocks here, but I still have slowness in logs. So I am looking for another crate, and I found out that someone built a crate to cache logs also.

But now here is the problem: I think that with the current implementation, we can only make one implementation of the Provider trait which means that in my case when I need a crate to cache blocks and another to cache logs, I can only use one provider that for one of them (or I have to create a custom one that do both)

My suggestion would allow something like this:

ProviderBuilder::new().add_extension(BlockCachingExtensions::new()).add_extension(LogCaching::new()).on_http(...);

Like just with these easy lines, it optimize the full application instead of having to create new custom providers, or caching manually around.

My belief is that at some point, people will make extensions of alloy that will be available on crate.io and users would have a base of extensions to choose from.

Ideas of possible extensions

And user would be able to use as many extensions as they want easily and also use multiple extensions on the same provider easily.

Implementation suggestion

I think that allowing this would require to have a trait for every RPC method.

This way we can create a provider that looks something like this:

pub struct CustomizableProvider {
     get_block: Box<dyn GetBlock>,
     get_logs: Box<dyn GetLogs>,
     ...
}

#[async_trait]
impl Provider for CustomizableProvider {
     async fn get_block(&self, id: BlockId, full: bool) -> TransportResult<Option<Block>> {
           self.get_block.get_block(id, full).await
     }

     ...
}

Then we have another trait to register an extension that will know what to modify

#[async_trait]
pub trait ExtensionBuilder {
     // async and Result, because during the build of the extension, the user may create some operation
     // like for example pre-caching blocks
     async fn build_extension(&self) -> Result<AlloyExtension>;
}

AlloyExtension may look like this:

pub struct AlloyExtension {
      // (optional this way when None it means that the extension is not overriding anything)
      pub get_block_implementation: Option<Box<dyn GetBlock>>,
      pub log_implementation: Option<Box<dyn GetLogs>>,
      ...
}

Additionally, I think it is possible to keep the current Provider trait, and maybe create a new "CustomizableProvider" implementation that allow this provider.

I think that my implementation idea is not perfect and there is probably a way better way to do it but I just wanted to give an overall view of how it would be possible to achieve something like that and how I view the end-user interaction.

Conclusion

I think that this feature would allow a way more powerful ecosystem for alloy in which people can easily create and use extensions to improve their Blockchain developing experience.

prestwich commented 5 months ago

Can you describe how this is different from the ProviderLayer system?