libid_contracts/
artifacts.rs1use std::{
11 collections::BTreeMap,
12 path::PathBuf,
13};
14
15use alloy::{
16 hex,
17 primitives::Bytes,
18};
19use include_dir::{
20 include_dir,
21 Dir,
22};
23
24use crate::error::{
25 Error,
26 Result,
27};
28
29static EMBEDDED: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/artifacts");
31
32pub const COVERED: &[(&str, &str)] = &[
36 ("NotaryService", "NotaryService"),
38 ("CeremonyProofVerifier", "CeremonyProofVerifier"),
39 ("ERC1967Proxy", "ERC1967Proxy"),
40 ("GoogleJwtRoots", "GoogleJwtRoots"),
41 ("XPlatformVerifier", "XPlatformVerifier"),
43 ("GitHubPlatformVerifier", "GitHubPlatformVerifier"),
44 ("GooglePlatformVerifier", "GooglePlatformVerifier"),
45 ("BearerLinkHonkVerifier", "BearerLinkHonkVerifier"),
48 ("OidcGoogleHonkVerifier", "OidcGoogleHonkVerifier"),
49 ("IdentityRegistry", "IdentityRegistry"),
51 ("HandleResolver", "HandleResolver"),
53 ("HandleEscrow", "HandleEscrow"),
55 ("LibidFactory", "LibidFactory"),
57 ("WTIA9", "WTIA9"),
58];
59
60enum Source {
61 Embedded,
62 Dir(PathBuf),
63}
64
65pub struct Artifacts {
67 source: Source,
68}
69
70impl Artifacts {
71 pub const fn embedded() -> Self {
74 Self {
75 source: Source::Embedded,
76 }
77 }
78
79 pub fn from_dir(dir: impl Into<PathBuf>) -> Self {
82 Self {
83 source: Source::Dir(dir.into()),
84 }
85 }
86
87 pub fn raw(&self, file: &str, contract: &str) -> Result<serde_json::Value> {
89 self.read_json(&format!("{file}.sol/{contract}.json"))
90 }
91
92 pub(crate) fn read_json(&self, rel: &str) -> Result<serde_json::Value> {
95 let contents = match &self.source {
96 Source::Embedded => EMBEDDED
97 .get_file(rel)
98 .and_then(|f| f.contents_utf8())
99 .map(str::to_owned)
100 .ok_or_else(|| Error::Artifact {
101 detail: format!("no embedded artifact {rel}"),
102 })?,
103 Source::Dir(dir) => {
104 let path = dir.join(rel);
105 std::fs::read_to_string(&path).map_err(|e| Error::Artifact {
106 detail: format!("failed to read artifact {}: {e}", path.display()),
107 })?
108 }
109 };
110 serde_json::from_str(&contents).map_err(|e| Error::Artifact {
111 detail: format!("failed to parse artifact {rel}: {e}"),
112 })
113 }
114
115 pub fn bytecode(&self, contract: &str) -> Result<Bytes> {
118 self.bytecode_named(contract, contract)
119 }
120
121 pub fn bytecode_named(&self, file: &str, contract: &str) -> Result<Bytes> {
124 let hex_str = self.bytecode_hex(file, contract)?;
125 let bytes = hex::decode(&hex_str).map_err(|e| Error::Artifact {
126 detail: format!("invalid bytecode hex for {file}.sol:{contract}: {e}"),
127 })?;
128 Ok(Bytes::from(bytes))
129 }
130
131 pub fn method_identifiers(&self, contract: &str) -> Result<BTreeMap<String, String>> {
134 let json = self.raw(contract, contract)?;
135 let methods =
136 json["methodIdentifiers"]
137 .as_object()
138 .ok_or_else(|| Error::Artifact {
139 detail: format!(
140 "no methodIdentifiers in {contract}.sol/{contract}.json"
141 ),
142 })?;
143 methods
144 .iter()
145 .map(|(sig, value)| {
146 let sel = value.as_str().ok_or_else(|| Error::Artifact {
147 detail: format!("non-string selector for {sig} in {contract}"),
148 })?;
149 Ok((sig.clone(), sel.to_owned()))
150 })
151 .collect()
152 }
153
154 pub(crate) fn bytecode_hex(&self, file: &str, contract: &str) -> Result<String> {
156 let json = self.raw(file, contract)?;
157 let raw = json["bytecode"]["object"]
158 .as_str()
159 .ok_or_else(|| Error::Artifact {
160 detail: format!("no bytecode.object in {file}.sol/{contract}.json"),
161 })?;
162 Ok(raw.strip_prefix("0x").unwrap_or(raw).to_owned())
163 }
164}
165
166impl Default for Artifacts {
167 fn default() -> Self {
168 Self::embedded()
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
180 fn every_covered_contract_has_deployable_bytecode() {
181 let artifacts = Artifacts::embedded();
182 for &(file, contract) in COVERED {
183 let bytecode = artifacts
184 .bytecode_named(file, contract)
185 .unwrap_or_else(|e| panic!("{file}.sol:{contract}: {e}"));
186 assert!(
187 !bytecode.is_empty(),
188 "{file}.sol:{contract} has empty bytecode"
189 );
190 }
191 }
192}