Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat(contract, abigen): support Hardhat ABI format #478

Merged
merged 1 commit into from
Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ethers-contract/ethers-contract-abigen/src/contract/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ mod tests {
let params = vec![Param {
name: "arg_a".to_string(),
kind: ParamType::Address,
internal_type: None,
}];
let token_stream = expand_inputs_call_arg(&params);
assert_eq!(token_stream.to_string(), "arg_a");
Expand All @@ -214,10 +215,12 @@ mod tests {
Param {
name: "arg_a".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param {
name: "arg_b".to_string(),
kind: ParamType::Uint(256usize),
internal_type: None,
},
];
let token_stream = expand_inputs_call_arg(&params);
Expand All @@ -228,14 +231,17 @@ mod tests {
Param {
name: "arg_a".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param {
name: "arg_b".to_string(),
kind: ParamType::Uint(128usize),
internal_type: None,
},
Param {
name: "arg_c".to_string(),
kind: ParamType::Bool,
internal_type: None,
},
];
let token_stream = expand_inputs_call_arg(&params);
Expand All @@ -256,10 +262,12 @@ mod tests {
Param {
name: "a".to_string(),
kind: ParamType::Bool,
internal_type: None,
},
Param {
name: "b".to_string(),
kind: ParamType::Address,
internal_type: None,
},
],
)
Expand All @@ -279,6 +287,7 @@ mod tests {
expand_fn_outputs(&[Param {
name: "a".to_string(),
kind: ParamType::Bool,
internal_type: None,
}])
.unwrap(),
{ bool },
Expand All @@ -292,10 +301,12 @@ mod tests {
Param {
name: "a".to_string(),
kind: ParamType::Bool,
internal_type: None,
},
Param {
name: "b".to_string(),
kind: ParamType::Address,
internal_type: None,
},
],)
.unwrap(),
Expand Down
74 changes: 72 additions & 2 deletions ethers-contract/ethers-contract-abigen/src/rawabi.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,72 @@
//! This is a basic representation of a contract ABI that does no post processing but contains the raw content of the ABI.

#![allow(missing_docs)]
use serde::{Deserialize, Serialize};
use serde::{
de::{EnumAccess, Error, MapAccess, SeqAccess, Visitor},
Deserialize, Deserializer, Serialize,
};

/// Contract ABI as a list of items where each item can be a function, constructor or event
pub type RawAbi = Vec<Item>;
pub struct RawAbi(Vec<Item>);

impl IntoIterator for RawAbi {
type Item = Item;
type IntoIter = std::vec::IntoIter<Self::Item>;

#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

struct RawAbiVisitor;

impl<'de> Visitor<'de> for RawAbiVisitor {
type Value = RawAbi;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence or map with `abi` key")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut vec = Vec::new();

while let Some(element) = seq.next_element()? {
vec.push(element);
}

Ok(RawAbi(vec))
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut vec = None;

while let Some(key) = map.next_key::<String>()? {
if key == "abi" {
vec = Some(RawAbi(map.next_value::<Vec<Item>>()?));
} else {
map.next_value::<serde::de::IgnoredAny>()?;
}
}

vec.ok_or_else(|| serde::de::Error::missing_field("abi"))
}
}

impl<'de> Deserialize<'de> for RawAbi {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(RawAbiVisitor)
}
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -40,9 +102,17 @@ pub struct Component {
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn can_parse_raw_abi() {
const VERIFIER_ABI: &str = include_str!("../../tests/solidity-contracts/verifier_abi.json");
let _ = serde_json::from_str::<RawAbi>(VERIFIER_ABI).unwrap();
}

#[test]
fn can_parse_hardhat_raw_abi() {
const VERIFIER_ABI: &str =
include_str!("../../tests/solidity-contracts/verifier_abi_hardhat.json");
let _ = serde_json::from_str::<RawAbi>(VERIFIER_ABI).unwrap();
}
}
9 changes: 8 additions & 1 deletion ethers-contract/ethers-contract-abigen/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl Source {
S: AsRef<str>,
{
let source = source.as_ref();
if source.starts_with('[') {
if matches!(source.chars().next(), Some('[' | '{')) {
return Ok(Source::String(source.to_owned()));
}
let root = env::current_dir()?.canonicalize()?;
Expand Down Expand Up @@ -283,5 +283,12 @@ mod tests {
let src = r#"[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"name","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"symbol","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"decimals","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"totalSupply","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]"#;
let parsed = Source::parse(src).unwrap();
assert_eq!(parsed, Source::String(src.to_owned()));

let hardhat_src = format!(
r#"{{"_format": "hh-sol-artifact-1", "contractName": "Verifier", "sourceName": "contracts/verifier.sol", "abi": {}, "bytecode": "0x", "deployedBytecode": "0x", "linkReferences": {{}}, "deployedLinkReferences": {{}}}}"#,
src,
);
let hardhat_parsed = Source::parse(&hardhat_src).unwrap();
assert_eq!(hardhat_parsed, Source::String(hardhat_src));
}
}
185 changes: 185 additions & 0 deletions ethers-contract/tests/solidity-contracts/verifier_abi_hardhat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "Verifier",
"sourceName": "contracts/verifier.sol",
"abi": [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "uint256[]",
"name": "input",
"type": "uint256[]"
},
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct Pairing.G1Point",
"name": "A",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct Pairing.G2Point",
"name": "B",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct Pairing.G1Point",
"name": "C",
"type": "tuple"
}
],
"internalType": "struct Verifier.Proof",
"name": "proof",
"type": "tuple"
},
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct Pairing.G1Point",
"name": "alfa1",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct Pairing.G2Point",
"name": "beta2",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct Pairing.G2Point",
"name": "gamma2",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct Pairing.G2Point",
"name": "delta2",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct Pairing.G1Point[]",
"name": "IC",
"type": "tuple[]"
}
],
"internalType": "struct Verifier.VerifyingKey",
"name": "vk",
"type": "tuple"
}
],
"name": "verify",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x",
"deployedBytecode": "0x",
"linkReferences": {},
"deployedLinkReferences": {}
}