contract_verification_migrator/lib.rs
1//! Copy contract verification from one block-explorer to another
2//!
3//! This crate allows you to easily copy the verfied source code from one block-explorer to another.
4//! It reads the source code / metadata from the source_url and submits it to the target_url.
5//! This assumes that both block-explorers are compatible with the the etherscan api specification
6//!
7//! ```rust
8//! let results = contract_verification_migrator::copy_etherscan_verification(
9//! vec!["0xE592427A0AEce92De3Edee1F18E0157C05861564".to_string()],
10//! "<YOUR_ETHERSCAN_API_KEY>".to_string(),
11//! "https://api.etherscan.io/api".to_string(),
12//! "<YOUR_BLOCKSCOUT_API_KEY>".to_string(),
13//! "https://eth.blockscout.com/api".to_string(),
14//! true,
15//! );
16//! ```
17#![warn(missing_docs)]
18
19use eyre::Result;
20use futures::future::FutureExt;
21
22mod verification;
23pub use verification::{copy_etherscan_verification_for_contract, VerificationResult};
24mod progress_bar;
25use progress_bar::{initialize_multi_progress, initialize_progress_bar, update_progress_bar};
26
27/// Copy contract verification of multiple contracts from one block-explorer to another
28///
29/// # Arguments
30/// - `contract_addresses` - Vector of contract addresses for which to copy the contract
31/// verification
32/// - `source_api_key` - The api key for the source block-explorer's api
33/// - `source_url` - The url of the source block-explorer's api
34/// - `target_api_key` - The api key for the target block-explorer's api
35/// - `target_url` - The url of the target block-explorer's api
36/// - `progress_bar` - Boolean indicating wether or not to display progress bars for the individual
37/// requests
38///
39/// # Examples
40///
41/// ```rust
42/// let results = contract_verification_migrator::copy_etherscan_verification(
43/// vec!["0xE592427A0AEce92De3Edee1F18E0157C05861564".to_string()],
44/// "<YOUR_ETHERSCAN_API_KEY>".to_string(),
45/// "https://api.etherscan.io/api".to_string(),
46/// "<YOUR_BLOCKSCOUT_API_KEY>".to_string(),
47/// "https://eth.blockscout.com/api".to_string(),
48/// true,
49/// );
50///
51/// ```
52pub async fn copy_etherscan_verification(
53 contract_addresses: Vec<String>,
54 source_api_key: String,
55 source_url: String,
56 target_api_key: String,
57 target_url: String,
58 progress_bar: bool,
59) -> Vec<Result<VerificationResult>> {
60 let mp = initialize_multi_progress(progress_bar);
61 let tasks: Vec<_> = contract_addresses
62 .into_iter()
63 .map(move |contract_address| {
64 let pb = initialize_progress_bar(mp.clone(), &contract_address);
65 copy_etherscan_verification_for_contract(
66 contract_address.clone(),
67 source_api_key.clone(),
68 source_url.clone(),
69 target_api_key.clone(),
70 target_url.clone(),
71 )
72 .then(move |result| {
73 update_progress_bar(pb, &result);
74 futures::future::ready(result)
75 })
76 })
77 .collect();
78 futures::future::join_all(tasks).await
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[tokio::test]
86 async fn test_copy_verification_from_etherscan_to_blockscout() {
87 let results = copy_etherscan_verification(
88 contract_addresses(),
89 etherscan_api_key(),
90 etherscan_url(),
91 blockscout_api_key(),
92 blockscout_url(),
93 false,
94 )
95 .await;
96 assert!(!results.into_iter().any(|result| result.is_err()));
97 }
98
99 #[tokio::test]
100 // TODO: Re-enable test once the fix in foundry-block-explorers is pushed to crates.io
101 // See: https://github.com/foundry-rs/block-explorers/commit/472118dafcbeb9bfdcd8df1c11ef28a5c51884d9
102 #[ignore]
103 async fn test_copy_verification_from_blockscout_to_etherscan() {
104 let results = copy_etherscan_verification(
105 contract_addresses(),
106 blockscout_api_key(),
107 blockscout_url(),
108 etherscan_api_key(),
109 etherscan_url(),
110 false,
111 )
112 .await;
113 assert!(!results.into_iter().any(|result| result.is_err()));
114 }
115
116 // Complex contract verified in "standard-solidity-json" format (non-flattened)
117 const UNI_V3_ROUTER: &str = "0xE592427A0AEce92De3Edee1F18E0157C05861564";
118 // Complex contract verified in flattened format on etherscan
119 const ICETH_TOKEN: &str = "0x7C07F7aBe10CE8e33DC6C5aD68FE033085256A84";
120 // Proxy contract - note that this should only attempt to migrate / copy the proxy itself
121 // and not the implementation contract
122 const BLUR: &str = "0x000000000000Ad05Ccc4F10045630fb830B95127";
123
124 fn contract_addresses() -> Vec<String> {
125 vec![
126 UNI_V3_ROUTER.to_string(),
127 ICETH_TOKEN.to_string(),
128 BLUR.to_string(),
129 ]
130 }
131 fn etherscan_url() -> String {
132 "https://api.etherscan.io/api".to_string()
133 }
134 fn etherscan_api_key() -> String {
135 std::env::var("ETHERSCAN_API_KEY").expect("ETHERSCAN_API_KEY not set")
136 }
137
138 fn blockscout_url() -> String {
139 "https://eth.blockscout.com/api".to_string()
140 }
141
142 fn blockscout_api_key() -> String {
143 std::env::var("BLOCKSCOUT_API_KEY").expect("BLOCKSCOUT_API_KEY not set")
144 }
145}