WIP add contract abi
This commit is contained in:
parent
548b24e518
commit
1dd3c3dc89
|
@ -189,6 +189,20 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethabi"
|
||||
version = "12.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052a565e3de82944527d6d10a465697e6bb92476b772ca7141080c901f6a63c6"
|
||||
dependencies = [
|
||||
"ethereum-types",
|
||||
"rustc-hex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tiny-keccak 1.5.0",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethbloom"
|
||||
version = "0.9.2"
|
||||
|
@ -199,7 +213,7 @@ dependencies = [
|
|||
"fixed-hash",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"tiny-keccak",
|
||||
"tiny-keccak 2.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -221,6 +235,7 @@ name = "ethers"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"ethabi",
|
||||
"ethereum-types",
|
||||
"failure",
|
||||
"rand 0.5.6",
|
||||
|
@ -232,7 +247,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"solc",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"tiny-keccak 2.0.2",
|
||||
"tokio",
|
||||
"url",
|
||||
"zeroize",
|
||||
|
@ -1226,6 +1241,15 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
|
|
|
@ -20,6 +20,7 @@ tiny-keccak = { version = "2.0.2", default-features = false }
|
|||
|
||||
solc = { git = "https://github.com/paritytech/rust_solc "}
|
||||
rlp = "0.4.5"
|
||||
ethabi = "12.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.21", features = ["macros"] }
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
use ethers::{
|
||||
types::{Address, Filter},
|
||||
Contract, HttpProvider, MainnetWallet,
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), failure::Error> {
|
||||
// connect to the network
|
||||
let provider = HttpProvider::try_from("http://localhost:8545")?;
|
||||
|
||||
// create a wallet and connect it to the provider
|
||||
let client = "15c42bf2987d5a8a73804a8ea72fb4149f88adf73e98fc3f8a8ce9f24fcb7774"
|
||||
.parse::<MainnetWallet>()?
|
||||
.connect(&provider);
|
||||
|
||||
// Contract should take both provider or a signer
|
||||
|
||||
let contract = Contract::new(
|
||||
"f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse::<Address>()?,
|
||||
abi,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
//! This module implements extensions to the `ethabi` API.
|
||||
//! Taken from: https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs
|
||||
|
||||
use ethabi::{Event, Function, ParamType};
|
||||
use crate::{utils::id, types::Selector};
|
||||
|
||||
/// Extension trait for `ethabi::Function`.
|
||||
pub trait FunctionExt {
|
||||
/// Compute the method signature in the standard ABI format. This does not
|
||||
/// include the output types.
|
||||
fn abi_signature(&self) -> String;
|
||||
|
||||
/// Compute the Keccak256 function selector used by contract ABIs.
|
||||
fn selector(&self) -> Selector;
|
||||
}
|
||||
|
||||
impl FunctionExt for Function {
|
||||
fn abi_signature(&self) -> String {
|
||||
let mut full_signature = self.signature();
|
||||
if let Some(colon) = full_signature.find(':') {
|
||||
full_signature.truncate(colon);
|
||||
}
|
||||
|
||||
full_signature
|
||||
}
|
||||
|
||||
fn selector(&self) -> Selector {
|
||||
id(self.abi_signature())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for `ethabi::Event`.
|
||||
pub trait EventExt {
|
||||
/// Compute the event signature in human-readable format. The `keccak256`
|
||||
/// hash of this value is the actual event signature that is used as topic0
|
||||
/// in the transaction logs.
|
||||
fn abi_signature(&self) -> String;
|
||||
}
|
||||
|
||||
impl EventExt for Event {
|
||||
fn abi_signature(&self) -> String {
|
||||
format!(
|
||||
"{}({}){}",
|
||||
self.name,
|
||||
self.inputs
|
||||
.iter()
|
||||
.map(|input| input.kind.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(","),
|
||||
if self.anonymous { " anonymous" } else { "" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn format_function_signature() {
|
||||
for (f, expected) in &[
|
||||
(r#"{"name":"foo","inputs":[],"outputs":[]}"#, "foo()"),
|
||||
(
|
||||
r#"{"name":"bar","inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}],"outputs":[]}"#,
|
||||
"bar(uint256,bool)",
|
||||
),
|
||||
(
|
||||
r#"{"name":"baz","inputs":[{"name":"a","type":"uint256"}],"outputs":[{"name":"b","type":"bool"}]}"#,
|
||||
"baz(uint256)",
|
||||
),
|
||||
(
|
||||
r#"{"name":"bax","inputs":[],"outputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}]}"#,
|
||||
"bax()",
|
||||
),
|
||||
] {
|
||||
let function: Function = serde_json::from_str(f).expect("invalid function JSON");
|
||||
let signature = function.abi_signature();
|
||||
assert_eq!(signature, *expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_event_signature() {
|
||||
for (e, expected) in &[
|
||||
(r#"{"name":"foo","inputs":[],"anonymous":false}"#, "foo()"),
|
||||
(
|
||||
r#"{"name":"bar","inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}],"anonymous":false}"#,
|
||||
"bar(uint256,bool)",
|
||||
),
|
||||
(
|
||||
r#"{"name":"baz","inputs":[{"name":"a","type":"uint256"}],"anonymous":true}"#,
|
||||
"baz(uint256) anonymous",
|
||||
),
|
||||
(
|
||||
r#"{"name":"bax","inputs":[],"anonymous":true}"#,
|
||||
"bax() anonymous",
|
||||
),
|
||||
] {
|
||||
let event: Event = serde_json::from_str(e).expect("invalid event JSON");
|
||||
let signature = event.abi_signature();
|
||||
assert_eq!(signature, *expected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use crate::types::Address;
|
||||
|
||||
mod abi;
|
||||
|
||||
pub struct Contract<ABI> {
|
||||
pub address: Address,
|
||||
pub abi: ABI,
|
||||
}
|
||||
|
||||
impl<ABI> Contract<ABI> {
|
||||
pub fn new<A: Into<Address>>(address: A, abi: ABI) -> Self {
|
||||
Self {
|
||||
address: address.into(),
|
||||
abi,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,8 +18,10 @@
|
|||
pub mod providers;
|
||||
pub use providers::HttpProvider;
|
||||
|
||||
pub mod signers;
|
||||
mod contract;
|
||||
pub use contract::Contract;
|
||||
|
||||
mod signers;
|
||||
pub use signers::{AnyWallet, MainnetWallet, Signer};
|
||||
|
||||
/// Ethereum related datatypes
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Various Ethereum Related Datatypes
|
||||
|
||||
pub type Selector = [u8; 4];
|
||||
|
||||
// Re-export common ethereum datatypes with more specific names
|
||||
pub use ethereum_types::H256 as TxHash;
|
||||
pub use ethereum_types::{Address, Bloom, H256, U256, U64};
|
||||
|
@ -21,3 +23,5 @@ pub use block::{Block, BlockId, BlockNumber};
|
|||
|
||||
mod log;
|
||||
pub use log::{Filter, Log};
|
||||
|
||||
|
||||
|
|
22
src/utils.rs
22
src/utils.rs
|
@ -1,5 +1,5 @@
|
|||
//! Various utilities for manipulating Ethereum related dat
|
||||
use crate::types::H256;
|
||||
use crate::types::{H256, Selector};
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
||||
|
@ -31,12 +31,14 @@ pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
|
|||
output
|
||||
}
|
||||
|
||||
/// Gets the first 4 bytes
|
||||
pub fn id(name: &str) -> [u8; 4] {
|
||||
/// Calculate the function selector as per the contract ABI specification. This
|
||||
/// is defined as the first 4 bytes of the Keccak256 hash of the function
|
||||
/// signature.
|
||||
pub fn id<S: AsRef<str>>(signature: S) -> Selector {
|
||||
let mut output = [0u8; 4];
|
||||
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(name.as_bytes());
|
||||
hasher.update(signature.as_ref().as_bytes());
|
||||
hasher.finalize(&mut output);
|
||||
|
||||
output
|
||||
|
@ -64,4 +66,16 @@ mod tests {
|
|||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_function_signature() {
|
||||
// test vector retrieved from
|
||||
// https://web3js.readthedocs.io/en/v1.2.4/web3-eth-abi.html#encodefunctionsignature
|
||||
assert_eq!(id("myMethod(uint256,string)"), [0x24, 0xee, 0x00, 0x97],);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revert_function_signature() {
|
||||
assert_eq!(id("Error(string)"), [0x08, 0xc3, 0x79, 0xa0]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue