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 {
83 self.optimized()
84 } else {
85 self.not_optimized()
86 }
87 }
88
89 pub fn optimized(mut self) -> Self {
90 self.optimization_used = Some("1".to_string());
91 self
92 }
93
94 pub fn not_optimized(mut self) -> Self {
95 self.optimization_used = Some("0".to_string());
96 self
97 }
98
99 pub fn code_format(mut self, code_format: CodeFormat) -> Self {
100 self.code_format = code_format;
101 self
102 }
103
104 pub fn evm_version(mut self, evm_version: impl Into<String>) -> Self {
105 self.evm_version = Some(evm_version.into());
106 self
107 }
108
109 pub fn via_ir(mut self, via_ir: bool) -> Self {
110 self.via_ir = Some(via_ir);
111 self
112 }
113
114 pub fn constructor_arguments(
115 mut self,
116 constructor_arguments: Option<impl Into<String>>,
117 ) -> Self {
118 let constructor_args = constructor_arguments.map(|s| {
119 s.into()
120 .trim()
121 .trim_start_matches("0x")
123 .to_string()
124 });
125 self.constructor_arguments.clone_from(&constructor_args);
126 self.blockscout_constructor_arguments = constructor_args;
127 self
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133#[allow(missing_copy_implementations)]
134pub struct VerifyProxyContract {
135 pub address: Address,
137 #[serde(default, rename = "expectedimplementation", skip_serializing_if = "Option::is_none")]
139 pub expected_impl: Option<Address>,
140}
141
142#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
143pub enum CodeFormat {
144 #[serde(rename = "solidity-single-file")]
145 SingleFile,
146
147 #[default]
148 #[serde(rename = "solidity-standard-json-input")]
149 StandardJsonInput,
150
151 #[serde(rename = "vyper-json")]
152 VyperJson,
153}
154
155impl AsRef<str> for CodeFormat {
156 fn as_ref(&self) -> &str {
157 match self {
158 CodeFormat::SingleFile => "solidity-single-file",
159 CodeFormat::StandardJsonInput => "solidity-standard-json-input",
160 CodeFormat::VyperJson => "vyper-json",
161 }
162 }
163}
164
165impl Client {
166 pub async fn submit_contract_verification(
168 &self,
169 contract: &VerifyContract,
170 ) -> Result<Response<String>> {
171 let body = self.create_query("contract", "verifysourcecode", contract);
172 self.post_form(&body).await
173 }
174
175 pub async fn check_contract_verification_status(
178 &self,
179 guid: impl AsRef<str>,
180 ) -> Result<Response<String>> {
181 let body = self.create_query(
182 "contract",
183 "checkverifystatus",
184 HashMap::from([("guid", guid.as_ref())]),
185 );
186 self.post_form(&body).await
187 }
188
189 pub async fn submit_proxy_contract_verification(
191 &self,
192 contract: &VerifyProxyContract,
193 ) -> Result<Response<String>> {
194 let body = self.create_query("contract", "verifyproxycontract", contract);
195 self.post_form(&body).await
196 }
197
198 pub async fn check_proxy_contract_verification_status(
201 &self,
202 guid: impl AsRef<str>,
203 ) -> Result<Response<String>> {
204 let body = self.create_query(
205 "contract",
206 "checkproxyverification",
207 HashMap::from([("guid", guid.as_ref())]),
208 );
209 self.post_form(&body).await
210 }
211}