ant_core/data/client/
payment.rs1use crate::data::client::batch::SingleNodeQuotePayment;
7use crate::data::client::quote::median_paid_quote_issuer;
8use crate::data::client::Client;
9use crate::data::error::{Error, Result};
10use ant_protocol::evm::{EncodedPeerId, ProofOfPayment, Wallet};
11use ant_protocol::payment::{serialize_single_node_proof, PaymentProof};
12use ant_protocol::transport::{MultiAddr, PeerId};
13use std::sync::Arc;
14use tracing::{debug, info};
15
16pub(crate) use crate::payment_policy::SINGLE_NODE_PAYMENT_MULTIPLIER;
17
18impl Client {
19 pub(crate) fn require_wallet(&self) -> Result<&Arc<Wallet>> {
21 self.wallet().ok_or_else(|| {
22 Error::Payment("Wallet not configured — call with_wallet() first".to_string())
23 })
24 }
25
26 pub async fn pay_for_storage(
43 &self,
44 address: &[u8; 32],
45 data_size: u64,
46 data_type: u32,
47 ) -> Result<(Vec<u8>, Vec<(PeerId, Vec<MultiAddr>)>)> {
48 if let Some(refusal) = self.corroborated_settlement_refusal() {
52 return Err(Error::ClientUpdateRequired(refusal));
53 }
54
55 let wallet = self.require_wallet()?;
58
59 debug!("Collecting quotes for address {}", hex::encode(address));
60
61 let quote_plan = self
63 .get_store_quote_plan(address, data_size, data_type)
64 .await?;
65 let quotes_with_peers = quote_plan.quotes;
66 let median_quote_issuer =
67 median_paid_quote_issuer("es_with_peers).ok_or_else(|| {
68 Error::Payment(
69 "Failed to select median quote issuer from witnessed quotes".to_string(),
70 )
71 })?;
72
73 let quoted_peers = quote_plan.put_peers;
76
77 let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len());
80 let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len());
81 let mut commitment_sidecars = Vec::new();
87
88 for (peer_id, _addrs, quote, _price, commitment) in quotes_with_peers {
89 let encoded = peer_id_to_encoded(&peer_id)?;
90 peer_quotes.push((encoded, quote.clone()));
91 quotes_for_payment.push(quote);
92 if let Some(sidecar) = commitment {
93 commitment_sidecars.push(sidecar);
94 }
95 }
96
97 let payment = SingleNodeQuotePayment::from_quotes(quotes_for_payment)
99 .map_err(|e| Error::Payment(format!("Failed to create payment: {e}")))?;
100
101 info!(
102 "Selected SNP median paid quote issuer {} for address {} (median price: {})",
103 median_quote_issuer.0,
104 hex::encode(address),
105 median_quote_issuer.1
106 );
107 info!("Payment total: {} atto", payment.total_amount());
108
109 let tx_hashes = payment
111 .pay(wallet)
112 .await
113 .map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))?;
114
115 info!(
116 "On-chain payment succeeded: {} transactions",
117 tx_hashes.len()
118 );
119
120 let proof = PaymentProof {
122 proof_of_payment: ProofOfPayment { peer_quotes },
123 tx_hashes,
124 commitment_sidecars,
125 };
126
127 let proof_bytes = serialize_single_node_proof(&proof)
128 .map_err(|e| Error::Serialization(format!("Failed to serialize payment proof: {e}")))?;
129
130 Ok((proof_bytes, quoted_peers))
131 }
132
133 pub async fn approve_token_spend(&self) -> Result<()> {
142 let wallet = self.require_wallet()?;
143 let evm_network = self.require_evm_network()?;
144
145 let vault_address = evm_network.payment_vault_address();
146 wallet
147 .approve_to_spend_tokens(*vault_address, ant_protocol::evm::U256::MAX)
148 .await
149 .map_err(|e| Error::Payment(format!("Token approval failed: {e}")))?;
150 info!("Token spend approved for payment vault contract");
151
152 Ok(())
153 }
154}
155
156pub(crate) fn peer_id_to_encoded(peer_id: &PeerId) -> Result<EncodedPeerId> {
158 Ok(EncodedPeerId::new(*peer_id.as_bytes()))
159}