Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export some kind of general Contract struct #513

Open
bh2smith opened this issue May 18, 2021 · 2 comments
Open

Export some kind of general Contract struct #513

bh2smith opened this issue May 18, 2021 · 2 comments

Comments

@bh2smith
Copy link
Contributor

bh2smith commented May 18, 2021

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()
    }
}
@nlordell
Copy link
Contributor

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:

  1. 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) { /* ... */ };
  1. Have a better way forward for mock contracts:
let instance: MockContract<MyContractAbi>;
  1. Be able to encode function data without needing a contract instance:
let calldata = MyContractEvent::encode(MyContractFunction::Foo { value, owner });

@bh2smith
Copy link
Contributor Author

bh2smith commented Jun 12, 2021

Trying to make this general deployment block fetcher

async fn get_deployment_block<T: Transport>(contract: &ethcontract::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,
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants