use crate::data::client::batch::SingleNodeQuotePayment;
use crate::data::client::quote::median_paid_quote_issuer;
use crate::data::client::Client;
use crate::data::error::{Error, Result};
use ant_protocol::evm::{EncodedPeerId, ProofOfPayment, Wallet};
use ant_protocol::payment::{serialize_single_node_proof, PaymentProof};
use ant_protocol::transport::{MultiAddr, PeerId};
use std::sync::Arc;
use tracing::{debug, info};
pub(crate) const SINGLE_NODE_PAYMENT_MULTIPLIER: u64 = 3;
impl Client {
pub(crate) fn require_wallet(&self) -> Result<&Arc<Wallet>> {
self.wallet().ok_or_else(|| {
Error::Payment("Wallet not configured — call with_wallet() first".to_string())
})
}
pub async fn pay_for_storage(
&self,
address: &[u8; 32],
data_size: u64,
data_type: u32,
) -> Result<(Vec<u8>, Vec<(PeerId, Vec<MultiAddr>)>)> {
let wallet = self.require_wallet()?;
debug!("Collecting quotes for address {}", hex::encode(address));
let quote_plan = self
.get_store_quote_plan(address, data_size, data_type)
.await?;
let quotes_with_peers = quote_plan.quotes;
let median_quote_issuer =
median_paid_quote_issuer("es_with_peers).ok_or_else(|| {
Error::Payment(
"Failed to select median quote issuer from witnessed quotes".to_string(),
)
})?;
let quoted_peers = quote_plan.put_peers;
let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len());
let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len());
let mut commitment_sidecars = Vec::new();
for (peer_id, _addrs, quote, _price, commitment) in quotes_with_peers {
let encoded = peer_id_to_encoded(&peer_id)?;
peer_quotes.push((encoded, quote.clone()));
quotes_for_payment.push(quote);
if let Some(sidecar) = commitment {
commitment_sidecars.push(sidecar);
}
}
let payment = SingleNodeQuotePayment::from_quotes(quotes_for_payment)
.map_err(|e| Error::Payment(format!("Failed to create payment: {e}")))?;
info!(
"Selected SNP median paid quote issuer {} for address {} (median price: {})",
median_quote_issuer.0,
hex::encode(address),
median_quote_issuer.1
);
info!("Payment total: {} atto", payment.total_amount());
let tx_hashes = payment
.pay(wallet)
.await
.map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))?;
info!(
"On-chain payment succeeded: {} transactions",
tx_hashes.len()
);
let proof = PaymentProof {
proof_of_payment: ProofOfPayment { peer_quotes },
tx_hashes,
commitment_sidecars,
};
let proof_bytes = serialize_single_node_proof(&proof)
.map_err(|e| Error::Serialization(format!("Failed to serialize payment proof: {e}")))?;
Ok((proof_bytes, quoted_peers))
}
pub async fn approve_token_spend(&self) -> Result<()> {
let wallet = self.require_wallet()?;
let evm_network = self.require_evm_network()?;
let vault_address = evm_network.payment_vault_address();
wallet
.approve_to_spend_tokens(*vault_address, ant_protocol::evm::U256::MAX)
.await
.map_err(|e| Error::Payment(format!("Token approval failed: {e}")))?;
info!("Token spend approved for payment vault contract");
Ok(())
}
}
pub(crate) fn peer_id_to_encoded(peer_id: &PeerId) -> Result<EncodedPeerId> {
Ok(EncodedPeerId::new(*peer_id.as_bytes()))
}