add support for polygonscan and snowtrace for abigen (#666)

This commit is contained in:
Ivan Porto Carrero 2021-12-10 08:05:45 -08:00 committed by GitHub
parent ab949f7858
commit a8b0885c25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 10 deletions

View File

@ -15,6 +15,11 @@
- Fix `fee_history` to first try with `block_count` encoded as a hex `QUANTITY`.
[#668](https://github.com/gakonst/ethers-rs/pull/668)
## ethers-contract-abigen
- Implement snowtrace and polygonscan on par with the etherscan integration
[#666](https://github.com/gakonst/ethers-rs/pull/666).
## ethers-solc
### Unreleased

View File

@ -23,6 +23,12 @@ pub enum Source {
/// An address of a mainnet contract that has been verified on Etherscan.io.
Etherscan(Address),
/// An address of a mainnet contract that has been verified on Polygonscan.com.
Polygonscan(Address),
/// An address of a mainnet contract that has been verified on snowtrace.io.
Snowtrace(Address),
/// The package identifier of an npm package with a path to a Truffle
/// artifact or ABI to be retrieved from `unpkg.io`.
Npm(String),
@ -91,7 +97,7 @@ impl Source {
let url = base.join(source)?;
match url.scheme() {
"file" => Ok(Source::local(source.to_string())),
"file" => Ok(Source::local(source)),
"http" | "https" => match url.host_str() {
Some("etherscan.io") => Source::etherscan(
url.path()
@ -99,9 +105,23 @@ impl Source {
.next()
.ok_or_else(|| anyhow!("HTTP URL does not have a path"))?,
),
Some("polygonscan.com") => Source::polygonscan(
url.path()
.rsplit('/')
.next()
.ok_or_else(|| anyhow!("HTTP URL does not have a path"))?,
),
Some("snowtrace.io") => Source::snowtrace(
url.path()
.rsplit('/')
.next()
.ok_or_else(|| anyhow!("HTTP URL does not have a path"))?,
),
_ => Ok(Source::Http(url)),
},
"etherscan" => Source::etherscan(url.path()),
"polygonscan" => Source::polygonscan(url.path()),
"snowtrace" => Source::snowtrace(url.path()),
"npm" => Ok(Source::npm(url.path())),
_ => Err(anyhow!("unsupported URL '{}'", url)),
}
@ -130,6 +150,26 @@ impl Source {
Ok(Source::Etherscan(address))
}
/// Creates an Polygonscan source from an address string.
pub fn polygonscan<S>(address: S) -> Result<Self>
where
S: AsRef<str>,
{
let address = util::parse_address(address)
.context("failed to parse address for Polygonscan source")?;
Ok(Source::Polygonscan(address))
}
/// Creates an Snowtrace source from an address string.
pub fn snowtrace<S>(address: S) -> Result<Self>
where
S: AsRef<str>,
{
let address =
util::parse_address(address).context("failed to parse address for Snowtrace source")?;
Ok(Source::Snowtrace(address))
}
/// Creates an Etherscan source from an address string.
pub fn npm<S>(package_path: S) -> Self
where
@ -148,6 +188,8 @@ impl Source {
Source::Local(path) => get_local_contract(path),
Source::Http(_) => panic!("Http abi location are not supported for wasm"),
Source::Etherscan(_) => panic!("Etherscan abi location are not supported for wasm"),
Source::Polygonscan(_) => panic!("Polygonscan abi location are not supported for wasm"),
Source::Snowtrace(_) => panic!("Snowtrace abi location are not supported for wasm"),
Source::Npm(_) => panic!("npm abi location are not supported for wasm"),
Source::String(abi) => Ok(abi.clone()),
}
@ -155,7 +197,9 @@ impl Source {
match self {
Source::Local(path) => get_local_contract(path),
Source::Http(url) => get_http_contract(url),
Source::Etherscan(address) => get_etherscan_contract(*address),
Source::Etherscan(address) => get_etherscan_contract(*address, "etherscan.io"),
Source::Polygonscan(address) => get_etherscan_contract(*address, "polygonscan.com"),
Source::Snowtrace(address) => get_etherscan_contract(*address, "snowtrace.io"),
Source::Npm(package) => get_npm_contract(package),
Source::String(abi) => Ok(abi.clone()),
}
@ -211,20 +255,27 @@ fn get_http_contract(url: &Url) -> Result<String> {
/// Retrieves a contract ABI from the Etherscan HTTP API and wraps it in an
/// artifact JSON for compatibility with the code generation facilities.
#[cfg(not(target_arch = "wasm32"))]
fn get_etherscan_contract(address: Address) -> Result<String> {
fn get_etherscan_contract(address: Address, domain: &str) -> Result<String> {
// NOTE: We do not retrieve the bytecode since deploying contracts with the
// same bytecode is unreliable as the libraries have already linked and
// probably don't reference anything when deploying on other networks.
let api_key =
env::var("ETHERSCAN_API_KEY").map(|key| format!("&apikey={}", key)).unwrap_or_default();
let api_key = {
let key_res = match domain {
"etherscan.io" => env::var("ETHERSCAN_API_KEY").ok(),
"polygonscan.com" => env::var("POLYGONSCAN_API_KEY").ok(),
"snowtrace.io" => env::var("SNOWTRACE_API_KEY").ok(),
_ => None,
};
key_res.map(|key| format!("&apikey={}", key)).unwrap_or_default()
};
let abi_url = format!(
"http://api.etherscan.io/api\
?module=contract&action=getabi&address={:?}&format=raw{}",
address, api_key,
"http://api.{}/api?module=contract&action=getabi&address={:?}&format=raw{}",
domain, address, api_key,
);
let abi = util::http_get(&abi_url).context("failed to retrieve ABI from Etherscan.io")?;
let abi =
util::http_get(&abi_url).context(format!("failed to retrieve ABI from {}", domain))?;
Ok(abi)
}
@ -256,10 +307,26 @@ mod tests {
"etherscan:0x0001020304050607080910111213141516171819",
Source::etherscan("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"polygonscan:0x0001020304050607080910111213141516171819",
Source::polygonscan("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"snowtrace:0x0001020304050607080910111213141516171819",
Source::snowtrace("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"https://etherscan.io/address/0x0001020304050607080910111213141516171819",
Source::etherscan("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"https://polygonscan.com/address/0x0001020304050607080910111213141516171819",
Source::polygonscan("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"https://snowtrace.io/address/0x0001020304050607080910111213141516171819",
Source::snowtrace("0x0001020304050607080910111213141516171819").unwrap(),
),
(
"npm:@openzeppelin/contracts@2.5.0/build/contracts/IERC20.json",
Source::npm("@openzeppelin/contracts@2.5.0/build/contracts/IERC20.json"),