use actix_web::{web, HttpResponse};
use alloy::primitives::{Address, U256};
use alloy::signers::local::PrivateKeySigner;
use x402_gateway::error::GatewayError;
use crate::state::NodeState;
#[derive(serde::Deserialize)]
pub struct SetupRequest {
pub private_key: String,
}
pub async fn setup_wallet(
body: web::Json<SetupRequest>,
node: web::Data<NodeState>,
) -> Result<HttpResponse, GatewayError> {
let signer: PrivateKeySigner = body
.private_key
.parse()
.map_err(|e| GatewayError::Internal(format!("invalid private key: {e}")))?;
let wallet_address = signer.address();
let facilitator_address: Address = node
.gateway
.facilitator
.as_ref()
.map(|f| f.facilitator.facilitator_address())
.ok_or_else(|| GatewayError::Internal("no embedded facilitator configured".to_string()))?;
let rpc_url = node.gateway.config.rpc_url.clone();
let token = x402::constants::DEFAULT_TOKEN;
let funded = match x402_identity::request_faucet_funds(&rpc_url, wallet_address).await {
Ok(()) => {
tracing::info!(address = %wallet_address, "Wallet funded via faucet");
true
}
Err(e) => {
tracing::warn!(address = %wallet_address, error = %e, "Faucet funding failed");
false
}
};
let provider = alloy::providers::ProviderBuilder::new()
.wallet(alloy::network::EthereumWallet::from(signer))
.connect_http(
rpc_url
.parse()
.map_err(|e| GatewayError::Internal(format!("invalid RPC URL: {e}")))?,
);
let current_allowance =
x402::tip20::allowance(&provider, token, wallet_address, facilitator_address)
.await
.unwrap_or(U256::ZERO);
let needs_approve = current_allowance < U256::from(1_000_000_000_000_000u64);
let approved = if needs_approve {
match x402::tip20::approve(&provider, token, facilitator_address, U256::MAX).await {
Ok(tx_hash) => {
tracing::info!(
address = %wallet_address,
facilitator = %facilitator_address,
tx = %tx_hash,
"Facilitator approved for pathUSD"
);
true
}
Err(e) => {
tracing::warn!(
address = %wallet_address,
error = %e,
"Approval failed (wallet may lack gas — retry after faucet)"
);
false
}
}
} else {
true };
let balance = x402::tip20::balance_of(&provider, token, wallet_address)
.await
.unwrap_or(U256::ZERO);
Ok(HttpResponse::Ok().json(serde_json::json!({
"address": format!("{:#x}", wallet_address),
"facilitator": format!("{:#x}", facilitator_address),
"funded": funded,
"approved": approved,
"balance": balance.to_string(),
"allowance": if needs_approve { "max".to_string() } else { current_allowance.to_string() },
})))
}
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.route("/wallet/setup", web::post().to(setup_wallet));
}