fix(abigen): correctly parse params in human readable abi (#194)

* fix(abigen): correctly parse params in human readable abi

* chore: make clippy happy

* test: remove unwrap

* chore: make clippy happy again

* chore: fix contract.rs example

* chore: rename to contract using human readable format

* examples: add abigen example with path to abi

* fix: pin funty version to fix bitvec error

* chore: remove unused import

* chore: fix deps
This commit is contained in:
Georgios Konstantopoulos 2021-02-16 19:10:26 +02:00 committed by GitHub
parent 457c646511
commit a43299c838
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 110 additions and 41 deletions

1
Cargo.lock generated
View File

@ -681,6 +681,7 @@ dependencies = [
"ethabi-next",
"ethereum-types",
"ethers",
"funty",
"generic-array",
"glob",
"hex",

View File

@ -107,7 +107,7 @@ impl Context {
// heuristic for parsing the human readable format
// replace bad chars
let abi_str = abi_str.replace('[', "").replace(']', "").replace(',', "");
let abi_str = abi_str.replace('[', "").replace(']', "");
// split lines and get only the non-empty things
let split: Vec<&str> = abi_str
.split('\n')

View File

@ -36,7 +36,7 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) ->
} else {
quote! {
pub static #abi_name: Lazy<Abi> = Lazy::new(|| {
let abi_str = #abi.replace('[', "").replace(']', "").replace(',', "");
let abi_str = #abi.replace('[', "").replace(']', "");
// split lines and get only the non-empty things
let split: Vec<&str> = abi_str
.split("\n")

View File

@ -30,7 +30,7 @@ impl Context {
.abi
.events()
.map(|event| expand_filter(event))
.collect::<Result<Vec<_>>>()?;
.collect::<Vec<_>>();
if data_types.is_empty() {
return Ok(quote! {});
@ -43,7 +43,7 @@ impl Context {
}
/// Expands into a single method for contracting an event stream.
fn expand_filter(event: &Event) -> Result<TokenStream> {
fn expand_filter(event: &Event) -> TokenStream {
// append `filter` to disambiguate with potentially conflicting
// function names
let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case()));
@ -53,13 +53,13 @@ fn expand_filter(event: &Event) -> Result<TokenStream> {
let ev_name = Literal::string(&event.name);
let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
Ok(quote! {
quote! {
#doc
pub fn #name(&self) -> Event<M, #result> {
self.0.event(#ev_name).expect("event not found (this should never happen)")
}
})
}
}
/// Expands an ABI event into a single event data type. This can expand either
@ -317,7 +317,7 @@ mod tests {
anonymous: false,
};
assert_quote!(expand_filter(&event).unwrap(), {
assert_quote!(expand_filter(&event), {
#[doc = "Gets the contract's `Transfer` event"]
pub fn transfer_filter(&self) -> Event<M, TransferFilter> {
self.0

View File

@ -32,12 +32,17 @@ glob = { version = "0.3.0", default-features = false }
bytes = { version = "1.0.1", features = ["serde"] }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
# bitvec compilation issue
# https://github.com/bitvecto-rs/bitvec/issues/105#issuecomment-778570981
funty = "=1.1.0"
[dev-dependencies]
ethers = { version = "0.2", path = "../ethers" }
serde_json = { version = "1.0.62", default-features = false }
bincode = { version = "1.2.1", default-features = false }
once_cell = { version = "1.5.2", default-features = false }
once_cell = { version = "1.5.2" }
[features]

View File

@ -248,8 +248,7 @@ where
mut tx: TransactionRequest,
block: Option<BlockNumber>,
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
if let Some(ref to) = tx.to {
if let NameOrAddress::Name(ens_name) = to {
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
let addr = self
.inner
.resolve_name(&ens_name)
@ -257,7 +256,6 @@ where
.map_err(SignerMiddlewareError::MiddlewareError)?;
tx.to = Some(addr.into())
}
}
// fill any missing fields
self.fill_transaction(&mut tx, block).await?;

View File

@ -59,8 +59,7 @@ where
block: Option<BlockNumber>,
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
// resolve the to field if that's an ENS name.
if let Some(ref to) = tx.to {
if let NameOrAddress::Name(ens_name) = to {
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
let addr = self
.inner
.resolve_name(&ens_name)
@ -68,7 +67,6 @@ where
.map_err(TransformerMiddlewareError::MiddlewareError)?;
tx.to = Some(addr.into())
}
}
// construct the appropriate proxy tx.
let proxy_tx = self.transformer.transform(tx)?;

View File

@ -73,15 +73,11 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
ctx.waker().wake_by_ref();
}
PendingTxState::GettingReceipt(fut) => {
if let Ok(receipt) = futures_util::ready!(fut.as_mut().poll(ctx)) {
if let Some(receipt) = receipt {
if let Ok(Some(receipt)) = futures_util::ready!(fut.as_mut().poll(ctx)) {
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
} else {
*this.state = PendingTxState::PausedGettingReceipt
}
} else {
*this.state = PendingTxState::PausedGettingReceipt
}
ctx.waker().wake_by_ref();
}
PendingTxState::CheckingReceipt(receipt) => {

View File

@ -293,15 +293,13 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
tx.gas = Some(self.estimate_gas(&tx).await?);
}
if let Some(ref to) = tx.to {
if let NameOrAddress::Name(ens_name) = to {
if let Some(NameOrAddress::Name(ref ens_name)) = tx.to {
// resolve to an address
let addr = self.resolve_name(&ens_name).await?;
// set the value
tx.to = Some(addr.into())
}
}
let tx_hash = self.request("eth_sendTransaction", [tx]).await?;

View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}]

View File

@ -12,7 +12,7 @@ abigen!(
r#"[
function setValue(string)
function getValue() external view (string)
event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue)
event ValueChanged(address indexed author, string oldValue, string newValue)
]"#,
event_derives(serde::Deserialize, serde::Serialize)
);
@ -20,7 +20,7 @@ abigen!(
#[tokio::main]
async fn main() -> Result<()> {
// 1. compile the contract (note this requires that you are inside the `ethers/examples` directory)
let compiled = Solc::new("./contract.sol").build()?;
let compiled = Solc::new("**/contract.sol").build()?;
let contract = compiled
.get("SimpleStorage")
.expect("could not find contract");

View File

@ -0,0 +1,72 @@
use anyhow::Result;
use ethers::{
prelude::*,
utils::{Ganache, Solc},
};
use std::{convert::TryFrom, sync::Arc, time::Duration};
// Generate the type-safe contract bindings by providing the ABI
// definition in human readable format
abigen!(
SimpleContract,
"./ethers/examples/contract_abi.json",
event_derives(serde::Deserialize, serde::Serialize)
);
#[tokio::main]
async fn main() -> Result<()> {
// 1. compile the contract (note this requires that you are inside the `ethers/examples` directory)
let compiled = Solc::new("**/contract.sol").build()?;
let contract = compiled
.get("SimpleStorage")
.expect("could not find contract");
dbg!("OK");
// 2. launch ganache
let ganache = Ganache::new().spawn();
// 3. instantiate our wallet
let wallet: LocalWallet = ganache.keys()[0].clone().into();
// 4. connect to the network
let provider =
Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
// 5. instantiate the client with the wallet
let client = SignerMiddleware::new(provider, wallet);
let client = Arc::new(client);
// 6. create a factory which will be used to deploy instances of the contract
let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
client.clone(),
);
// 7. deploy it with the constructor arguments
let contract = factory.deploy("initial value".to_string())?.send().await?;
// 8. get the contract's address
let addr = contract.address();
// 9. instantiate the contract
let contract = SimpleContract::new(addr, client.clone());
// 10. call the `setValue` method
// (first `await` returns a PendingTransaction, second one waits for it to be mined)
let _receipt = contract.set_value("hi".to_owned()).send().await?.await?;
// 11. get all events
let logs = contract
.value_changed_filter()
.from_block(0u64)
.query()
.await?;
// 12. get the new value
let value = contract.get_value().call().await?;
println!("Value: {}. Logs: {}", value, serde_json::to_string(&logs)?);
Ok(())
}