use crate::util::{load_signer, load_sponsor};
use localharness::cut_guard;
use localharness::registry::{self, FacetCut};
use localharness::soliditylite::compile;
use localharness::tempo_tx::TempoCall;
use localharness::wallet;
const GUARDED_CUT_FACET: &str = "0xa4c8a030607090e0C8602311F104471381E94eb1";
const LOUPE_FACET: &str = "0x28577026cDEeAb9b9E723666e16c530b94c9EED3";
const OWN_FACET: &str = "0x9D157FaAEB76956986aAc1b96afCE9Efe0D1CEc4";
const USAGE: &str = "usage:\n localharness facet deploy [--as <me>] <name> <src.sol>\n localharness facet diamond [--as <me>]\n localharness facet cut [--as <me>] <diamond-addr> <facet-addr> <src.sol>";
pub(crate) async fn facet(caller: Option<&str>, args: &[String]) -> i32 {
match args.first().map(String::as_str) {
Some("deploy") => facet_deploy(caller, &args[1..]).await,
Some("diamond") => facet_diamond(caller).await,
Some("cut") => facet_cut(caller, &args[1..]).await,
_ => {
eprintln!("{USAGE}");
2
}
}
}
async fn facet_deploy(caller: Option<&str>, rest: &[String]) -> i32 {
let (name, path) = match (rest.first(), rest.get(1)) {
(Some(n), Some(p)) => (n.as_str(), p.as_str()),
_ => {
eprintln!("{USAGE}");
return 2;
}
};
let art = match read_and_compile(path) {
Ok(a) => a,
Err(c) => return c,
};
let gas = 2_000_000 + art.init_code.len() as u128 * 6_000;
println!("compiled '{name}': {}-byte runtime, {} selector(s) → deploying via sponsored CREATE …", art.runtime.len(), art.selectors.len());
let signer = match load_signer(caller) {
Ok(p) => p,
Err(c) => return c,
};
let sponsor = match load_sponsor() {
Ok(s) => s,
Err(c) => return c,
};
match registry::create_sponsored(&signer, &sponsor, art.init_code, registry::ALPHA_USD_ADDRESS(), gas).await {
Ok(addr) => {
println!("✓ deployed '{name}' facet at {addr}");
for s in &art.selectors {
println!(" selector 0x{}", hex(s));
}
println!(" cut it in: localharness facet cut <your-diamond> {addr} {path}");
0
}
Err(e) => {
eprintln!("deploy failed: {e}");
1
}
}
}
async fn facet_diamond(caller: Option<&str>) -> i32 {
let bytecode = std::fs::read_to_string("contracts/out/Diamond.sol/Diamond.json")
.ok()
.and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
.and_then(|j| j["bytecode"]["object"].as_str().map(str::to_string))
.unwrap_or_else(|| crate::diamond_bytecode::DIAMOND_INIT_HEX.to_string());
let mut init = match hex_decode(bytecode.trim_start_matches("0x")) {
Ok(b) => b,
Err(e) => {
eprintln!("bad Diamond bytecode: {e}");
return 1;
}
};
let signer = match load_signer(caller) {
Ok(p) => p,
Err(c) => return c,
};
let owner = wallet::address(&signer);
let gcuts = vec![
FacetCut { facet: addr20(GUARDED_CUT_FACET), action: 0, selectors: vec![[0x1f, 0x93, 0x1c, 0x1c]] },
FacetCut { facet: addr20(LOUPE_FACET), action: 0, selectors: vec![[0x7a,0x0e,0xd6,0x27],[0xad,0xfc,0xa1,0x5e],[0x52,0xef,0x6b,0x2c],[0xcd,0xff,0xac,0xc6],[0x01,0xff,0xc9,0xa7]] },
FacetCut { facet: addr20(OWN_FACET), action: 0, selectors: vec![[0xf2,0xfd,0xe3,0x8b],[0x8d,0xa5,0xcb,0x5b]] },
];
init.extend_from_slice(®istry::encode_diamond_constructor_args(&owner, &gcuts));
println!("genesis-ing a child diamond owned by you (0x{}), guarded cut facet, via sponsored CREATE …", hex(&owner));
let sponsor = match load_sponsor() {
Ok(s) => s,
Err(c) => return c,
};
match registry::create_sponsored(&signer, &sponsor, init, registry::ALPHA_USD_ADDRESS(), 25_000_000).await {
Ok(addr) => {
println!("✓ your diamond: {addr}");
println!(" cut facets into it: localharness facet cut {addr} <facet-addr> <src.sol>");
0
}
Err(e) => {
eprintln!("genesis failed: {e}");
1
}
}
}
async fn facet_cut(caller: Option<&str>, rest: &[String]) -> i32 {
let (diamond, facet_addr, path) = match (rest.first(), rest.get(1), rest.get(2)) {
(Some(d), Some(f), Some(p)) => (d.as_str(), f.as_str(), p.as_str()),
_ => {
eprintln!("{USAGE}");
return 2;
}
};
let art = match read_and_compile(path) {
Ok(a) => a,
Err(c) => return c,
};
let mut present: Vec<[u8; 4]> = Vec::new();
for s in &art.selectors {
if let Ok(Some(_)) = registry::facet_address_of(diamond, *s).await {
present.push(*s);
}
}
if let Err(reasons) = cut_guard::check_cut(&art.selectors, &present, true) {
eprintln!("✗ cut rejected by safety lint ({} issue(s)):", reasons.len());
for r in &reasons {
eprintln!(" - {r}");
}
return 1;
}
let n = art.selectors.len();
let cut = FacetCut { facet: addr20(facet_addr), action: 0, selectors: art.selectors };
let calldata = registry::encode_diamond_cut(&[cut], &[0u8; 20], &[]);
let signer = match load_signer(caller) {
Ok(p) => p,
Err(c) => return c,
};
println!("cutting {n} selector(s) of {facet_addr} into {diamond} (safety-lint OK; diamondCut, sponsored) …");
match registry::sponsored_batch(
&signer,
vec![TempoCall { to: addr20(diamond), value_wei: 0, input: calldata }],
12_000_000,
)
.await
{
Ok(tx) => {
println!("✓ cut into {diamond} (tx {tx}) — the facet's functions are now live behind the diamond");
0
}
Err(e) => {
eprintln!("cut failed: {e}");
1
}
}
}
fn read_and_compile(path: &str) -> Result<localharness::soliditylite::CompiledArtifact, i32> {
let src = std::fs::read_to_string(path).map_err(|e| {
eprintln!("read {path}: {e}");
1
})?;
compile(&src).map_err(|e| {
eprintln!("compile error:\n{}", e.render(&src));
1
})
}
fn addr20(h: &str) -> [u8; 20] {
let mut out = [0u8; 20];
if let Ok(b) = hex_decode(h.trim().trim_start_matches("0x")) {
if b.len() == 20 {
out.copy_from_slice(&b);
}
}
out
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
fn hex_decode(s: &str) -> Result<Vec<u8>, String> {
if s.len() % 2 != 0 {
return Err("odd-length hex".into());
}
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string()))
.collect()
}