use crate::registry;
const REMOTE_CALL_TIMEOUT_MS: u32 = 120_000;
fn parse_addr(s: &str) -> Result<[u8; 20], String> {
let t = s.trim().trim_start_matches("0x");
if t.len() != 40 {
return Err(format!("bad address length: {s}"));
}
crate::encoding::hex_to_bytes(t)?
.try_into()
.map_err(|_| format!("bad address: {s}"))
}
pub(crate) async fn ask_via_proxy(target: &str, message: &str) -> Result<String, String> {
let (signer, from) = super::chat::credit_signer()
.await
.ok_or_else(|| "no identity to pay from".to_string())?;
let from_hex = crate::encoding::bytes_to_hex_str(&from);
let to_hex = registry::tba_of_name(target)
.await
.map_err(|e| format!("payee lookup: {e}"))?
.ok_or_else(|| format!("'{target}' is not a registered agent"))?;
let to = parse_addr(&to_hex)?;
let token_id = registry::id_of_name(target)
.await
.map_err(|e| format!("price lookup: {e}"))?;
let advertised = registry::x402_price_of(token_id)
.await
.map_err(|e| format!("price lookup: {e}"))?;
let pay_wei = registry::auto_pay_amount(
advertised,
registry::REMOTE_CALL_MAX_AUTO_PAY_WEI,
)
.map_err(|over_cap_wei| {
format!(
"'{target}' charges {} $LH per call — above the {} $LH auto-pay cap; \
call it yourself if you accept the price",
crate::app::format_wei_as_test_eth(over_cap_wei),
crate::app::format_wei_as_test_eth(registry::REMOTE_CALL_MAX_AUTO_PAY_WEI),
)
})?;
if let Ok(wallet_wei) = registry::token_balance_of(&from_hex).await {
if wallet_wei < pay_wei {
let shortfall = pay_wei - wallet_wei;
let meter_wei = registry::credit_balance_of(&from_hex).await.unwrap_or(0);
if meter_wei >= shortfall {
let sponsor = super::sponsor::signer()?;
registry::withdraw_credits_sponsored(
&signer,
&sponsor,
shortfall,
registry::ALPHA_USD_ADDRESS,
)
.await
.map_err(|e| format!("credit withdraw (meter -> wallet): {e}"))?;
} else {
return Err(format!(
"calling '{target}' costs {} $LH but your wallet holds {} \
$LH and your chat meter {} $LH — fund up with a redeem \
code, an invite, or a $LH transfer, then retry",
crate::app::format_wei_as_test_eth(pay_wei),
crate::app::format_wei_as_test_eth(wallet_wei),
crate::app::format_wei_as_test_eth(meter_wei),
));
}
}
}
match registry::lh_allowance(&from_hex, registry::REGISTRY_ADDRESS).await {
Ok(allowance) if allowance >= pay_wei => {}
Ok(_) => {
let sponsor = super::sponsor::signer()?;
registry::approve_lh_sponsored(
&signer,
&sponsor,
registry::REGISTRY_ADDRESS,
u128::MAX,
registry::ALPHA_USD_ADDRESS,
)
.await
.map_err(|e| format!("$LH approve: {e}"))?;
}
Err(_) => {}
}
let now = (js_sys::Date::now() / 1000.0) as u64;
let valid_before = now + 3600;
let nonce = registry::random_x402_nonce();
let signature = registry::sign_x402(
&signer,
&from,
&to,
pay_wei,
0,
valid_before,
&nonce,
)?;
let header = registry::x402_authorization_json(
&from_hex,
&to_hex,
pay_wei,
0,
valid_before,
&nonce,
&signature,
);
let body = registry::x402_ask_agent_body(target, message);
let json = super::net::with_timeout(REMOTE_CALL_TIMEOUT_MS, async {
let resp = reqwest::Client::new()
.post(registry::mcp_endpoint_url())
.header("content-type", "application/json")
.header("x-x402-authorization", header.to_string())
.json(&body)
.send()
.await
.map_err(|e| format!("proxy request: {e}"))?;
resp.json::<serde_json::Value>()
.await
.map_err(|e| format!("proxy response decode: {e}"))
})
.await
.map_err(|_| format!("proxy call timed out after {}s", REMOTE_CALL_TIMEOUT_MS / 1000))??;
registry::parse_mcp_tool_reply(&json)
}