Export some kind of general Contract struct
bh2smith opened this issue · 2 comments
Not exactly sure how to describe this, but I suppose it would be analogous to the Contract
object what we know and love from ethers
which can be instantiated as an interface from an ABI or as an instance when also provided with an address and web3Provider.
This would allow us to make generalizations for arbitrary contracts some common functionalities.
One particular place where this could come in handy is when implementing an Event Listener in place of the generic EventRetrieving
pub struct EventHandler<C: EventRetrieving, S: EventStoring<C::Event>> {
contract: C,
store: S,
last_handled_block: Option<u64>,
}
Where
pub trait EventRetrieving {
type Event: ParseLog;
fn get_events(&self) -> AllEventsBuilder<DynTransport, Self::Event>;
fn web3(&self) -> Web3<DynTransport>;
}
At the moment we need to implement EventRetrieving
in a very boiler plate kind of way for each contract we want to listen to.
impl EventRetrieving for GPv2SettlementContract {
type Event = ContractEvent;
fn get_events(&self) -> AllEventsBuilder<DynTransport, Self::Event> {
self.0.all_events()
}
fn web3(&self) -> Web3<DynTransport> {
self.0.raw_instance().web3()
}
}
So, I think there is a large change we can make to the ethcontract
binding generation. Specifically we can generate:
struct MyContractAbi;
implement Abi for MyContractAbi {
type Function: MyContractFunction;
type Event: MyContractEvent;
}
enum MyContractFunction {
Foo {
value: U256,
owner: Address,
},
Bar,
}
enum MyContractEvent {
// same as now.
}
We would then be able to create instances of type:
let instance: Contract<MyContractAbi>;
Some other advantages of this separation:
- We can define functions generic on some contract type:
fn do_something_to_contract<C: Abi>(instance: Contract<C>) { /* ... */ };
fn call_some_function<C: Abi>(instance: Contract<C>, function: C::Function) { /* ... */ };
- Have a better way forward for mock contracts:
let instance: MockContract<MyContractAbi>;
- Be able to encode function data without needing a contract instance:
let calldata = MyContractEvent::encode(MyContractFunction::Foo { value, owner });
Trying to make this general deployment block fetcher
async fn get_deployment_block<T: Transport>(contract: ðcontract::contract::Instance<T>) -> Option<u64> {
match contract.deployment_information() {
Some(DeploymentInformation::BlockNumber(block_number)) => Some(block_number),
Some(DeploymentInformation::TransactionHash(hash)) => Some(
contract
.raw_instance()
.web3()
.block_number_from_tx_hash(hash)
.await?,
),
None => None,
}
}
But when I give it a generated contract instance I get this:
error[E0308]: mismatched types
--> shared/src/balancer/event_handler.rs:286:71
|
286 | let deployment_block_two_token_factory = get_deployment_block(&two_token_pool_factory).await;
| ^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Instance`, found struct `BalancerV2WeightedPool2TokensFactory`
|
= note: expected reference `&Instance<_>`
found reference `&BalancerV2WeightedPool2TokensFactory`
Managed to work around it by passing the DeploymentInformation
instead of the contract like so
async fn get_deployment_block(
deployment_info: Option<DeploymentInformation>,
web3: &DynWeb3,
) -> Option<u64> {
match deployment_info {
Some(DeploymentInformation::BlockNumber(block_number)) => Some(block_number),
Some(DeploymentInformation::TransactionHash(hash)) => {
Some(web3.block_number_from_tx_hash(hash).await.ok()?)
}
None => None,
}
}