fix(abigen): ensure correct ABI in `From` impl (#2036)

* fix(abigen): ensure correct ABI in `From` impl

`new` creates a `Contract` with the correct ABI, but the `From` impl
simply wraps the source `Contract`, which could have a completely
different ABI. In effect this was an unsafe cast, and indeed was
observed to trigger a "method not found (this should never happen)"
panic for subsequent method lookups. Implementing `From` in terms of
`new` fixes this, at the cost of an extra `Arc::clone()` (which I can't
see how to eliminate without piercing the `ethers::contract::Contract`
public API).

* Remove unnecessary `async` from test

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
This commit is contained in:
Nick 2023-01-13 18:18:17 +00:00 committed by GitHub
parent 79f27e2366
commit 9e559aefeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 2 deletions

View File

@ -150,7 +150,7 @@ impl Context {
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
/// object
pub fn new<T: Into<#ethers_core::types::Address>>(address: T, client: ::std::sync::Arc<M>) -> Self {
#ethers_contract::Contract::new(address.into(), #abi_name.clone(), client).into()
Self(#ethers_contract::Contract::new(address.into(), #abi_name.clone(), client))
}
#deployment_methods
@ -163,7 +163,7 @@ impl Context {
impl<M : #ethers_providers::Middleware> From<#ethers_contract::Contract<M>> for #name<M> {
fn from(contract: #ethers_contract::Contract<M>) -> Self {
Self(contract)
Self::new(contract.address(), contract.client())
}
}
};

View File

@ -750,3 +750,21 @@ fn can_handle_overloaded_function_with_array() {
]"#,
);
}
#[test]
fn convert_uses_correct_abi() {
abigen!(
Foo, r#"[function foo()]"#;
Bar, r#"[function bar()]"#;
);
let provider = Arc::new(Provider::new(MockProvider::new()));
let foo = Foo::new(Address::default(), Arc::clone(&provider));
let contract: &ethers_contract::Contract<_> = &foo;
let bar: Bar<Provider<MockProvider>> = contract.clone().into();
// Ensure that `bar` is using the `Bar` ABI internally (this method lookup will panic if `bar`
// is incorrectly using the `Foo` ABI internally).
bar.bar().call();
}