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`. - Fix `fee_history` to first try with `block_count` encoded as a hex `QUANTITY`.
[#668](https://github.com/gakonst/ethers-rs/pull/668) [#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 ## ethers-solc
### Unreleased ### Unreleased

View File

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