1use crate::{Client, Response, Result};
2use alloy_primitives::Address;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8#[must_use]
9pub struct VerifyContract {
10 #[serde(rename = "contractaddress")]
11 pub address: Address,
12 #[serde(rename = "sourceCode")]
13 pub source: String,
14 #[serde(rename = "codeformat")]
15 pub code_format: CodeFormat,
16 #[serde(rename = "contractname")]
19 pub contract_name: String,
20 #[serde(rename = "compilerversion")]
21 pub compiler_version: String,
22 #[serde(rename = "optimizationUsed", skip_serializing_if = "Option::is_none")]
24 pub optimization_used: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub runs: Option<String>,
28 #[serde(rename = "constructorArguements", skip_serializing_if = "Option::is_none")]
38 pub constructor_arguments: Option<String>,
39 #[serde(rename = "constructorArguments", skip_serializing_if = "Option::is_none")]
42 pub blockscout_constructor_arguments: Option<String>,
43 #[serde(rename = "evmversion", skip_serializing_if = "Option::is_none")]
45 pub evm_version: Option<String>,
46 #[serde(rename = "viaIR", skip_serializing_if = "Option::is_none")]
48 pub via_ir: Option<bool>,
49 #[serde(flatten)]
50 pub other: HashMap<String, String>,
51}
52
53impl VerifyContract {
54 pub fn new(
55 address: Address,
56 contract_name: String,
57 source: String,
58 compiler_version: String,
59 ) -> Self {
60 Self {
61 address,
62 source,
63 code_format: Default::default(),
64 contract_name,
65 compiler_version,
66 optimization_used: None,
67 runs: None,
68 constructor_arguments: None,
69 blockscout_constructor_arguments: None,
70 evm_version: None,
71 via_ir: None,
72 other: Default::default(),
73 }
74 }
75
76 pub fn runs(mut self, runs: u32) -> Self {
77 self.runs = Some(format!("{runs}"));
78 self
79 }
80
81 pub fn optimization(self, optimization: bool) -> Self {
82 if optimization { self.optimized() } else { self.not_optimized() }
83 }
84
85 pub fn optimized(mut self) -> Self {
86 self.optimization_used = Some("1".to_string());
87 self
88 }
89
90 pub fn not_optimized(mut self) -> Self {
91 self.optimization_used = Some("0".to_string());
92 self
93 }
94
95 pub fn code_format(mut self, code_format: CodeFormat) -> Self {
96 self.code_format = code_format;
97 self
98 }
99
100 pub fn evm_version(mut self, evm_version: impl Into<String>) -> Self {
101 self.evm_version = Some(evm_version.into());
102 self
103 }
104
105 pub fn via_ir(mut self, via_ir: bool) -> Self {
106 self.via_ir = Some(via_ir);
107 self
108 }
109
110 pub fn constructor_arguments(
111 mut self,
112 constructor_arguments: Option<impl Into<String>>,
113 ) -> Self {
114 let constructor_args = constructor_arguments.map(|s| {
115 s.into()
116 .trim()
117 .trim_start_matches("0x")
119 .to_string()
120 });
121 self.constructor_arguments.clone_from(&constructor_args);
122 self.blockscout_constructor_arguments = constructor_args;
123 self
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129#[allow(missing_copy_implementations)]
130pub struct VerifyProxyContract {
131 pub address: Address,
133 #[serde(default, rename = "expectedimplementation", skip_serializing_if = "Option::is_none")]
135 pub expected_impl: Option<Address>,
136}
137
138#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
139pub enum CodeFormat {
140 #[serde(rename = "solidity-single-file")]
141 SingleFile,
142
143 #[default]
144 #[serde(rename = "solidity-standard-json-input")]
145 StandardJsonInput,
146
147 #[serde(rename = "vyper-json")]
148 VyperJson,
149}
150
151impl AsRef<str> for CodeFormat {
152 fn as_ref(&self) -> &str {
153 match self {
154 CodeFormat::SingleFile => "solidity-single-file",
155 CodeFormat::StandardJsonInput => "solidity-standard-json-input",
156 CodeFormat::VyperJson => "vyper-json",
157 }
158 }
159}
160
161impl Client {
162 pub async fn submit_contract_verification(
164 &self,
165 contract: &VerifyContract,
166 ) -> Result<Response<String>> {
167 let body = self.create_query("contract", "verifysourcecode", contract);
168 self.post_form(&body).await
169 }
170
171 pub async fn check_contract_verification_status(
174 &self,
175 guid: impl AsRef<str>,
176 ) -> Result<Response<String>> {
177 let body = self.create_query(
178 "contract",
179 "checkverifystatus",
180 HashMap::from([("guid", guid.as_ref())]),
181 );
182 self.post_form(&body).await
183 }
184
185 pub async fn submit_proxy_contract_verification(
187 &self,
188 contract: &VerifyProxyContract,
189 ) -> Result<Response<String>> {
190 let body = self.create_query("contract", "verifyproxycontract", contract);
191 self.post_form(&body).await
192 }
193
194 pub async fn check_proxy_contract_verification_status(
197 &self,
198 guid: impl AsRef<str>,
199 ) -> Result<Response<String>> {
200 let body = self.create_query(
201 "contract",
202 "checkproxyverification",
203 HashMap::from([("guid", guid.as_ref())]),
204 );
205 self.post_form(&body).await
206 }
207}