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) const SINGLE_NODE_PAYMENT_MULTIPLIER: u64 = 3;
18
19impl Client {
20 pub(crate) fn require_wallet(&self) -> Result<&Arc<Wallet>> {
22 self.wallet().ok_or_else(|| {
23 Error::Payment("Wallet not configured — call with_wallet() first".to_string())
24 })
25 }
26
27 pub async fn pay_for_storage(
44 &self,
45 address: &[u8; 32],
46 data_size: u64,
47 data_type: u32,
48 ) -> Result<(Vec<u8>, Vec<(PeerId, Vec<MultiAddr>)>)> {
49 let wallet = self.require_wallet()?;
52
53 debug!("Collecting quotes for address {}", hex::encode(address));
54
55 let quote_plan = self
57 .get_store_quote_plan(address, data_size, data_type)
58 .await?;
59 let quotes_with_peers = quote_plan.quotes;
60 let median_quote_issuer =
61 median_paid_quote_issuer("es_with_peers).ok_or_else(|| {
62 Error::Payment(
63 "Failed to select median quote issuer from witnessed quotes".to_string(),
64 )
65 })?;
66
67 let quoted_peers = quote_plan.put_peers;
70
71 let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len());
74 let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len());
75 let mut commitment_sidecars = Vec::new();
81
82 for (peer_id, _addrs, quote, _price, commitment) in quotes_with_peers {
83 let encoded = peer_id_to_encoded(&peer_id)?;
84 peer_quotes.push((encoded, quote.clone()));
85 quotes_for_payment.push(quote);
86 if let Some(sidecar) = commitment {
87 commitment_sidecars.push(sidecar);
88 }
89 }
90
91 let payment = SingleNodeQuotePayment::from_quotes(quotes_for_payment)
93 .map_err(|e| Error::Payment(format!("Failed to create payment: {e}")))?;
94
95 info!(
96 "Selected SNP median paid quote issuer {} for address {} (median price: {})",
97 median_quote_issuer.0,
98 hex::encode(address),
99 median_quote_issuer.1
100 );
101 info!("Payment total: {} atto", payment.total_amount());
102
103 let tx_hashes = payment
105 .pay(wallet)
106 .await
107 .map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))?;
108
109 info!(
110 "On-chain payment succeeded: {} transactions",
111 tx_hashes.len()
112 );
113
114 let proof = PaymentProof {
116 proof_of_payment: ProofOfPayment { peer_quotes },
117 tx_hashes,
118 commitment_sidecars,
119 };
120
121 let proof_bytes = serialize_single_node_proof(&proof)
122 .map_err(|e| Error::Serialization(format!("Failed to serialize payment proof: {e}")))?;
123
124 Ok((proof_bytes, quoted_peers))
125 }
126
127 pub async fn approve_token_spend(&self) -> Result<()> {
136 let wallet = self.require_wallet()?;
137 let evm_network = self.require_evm_network()?;
138
139 let vault_address = evm_network.payment_vault_address();
140 wallet
141 .approve_to_spend_tokens(*vault_address, ant_protocol::evm::U256::MAX)
142 .await
143 .map_err(|e| Error::Payment(format!("Token approval failed: {e}")))?;
144 info!("Token spend approved for payment vault contract");
145
146 Ok(())
147 }
148}
149
150pub(crate) fn peer_id_to_encoded(peer_id: &PeerId) -> Result<EncodedPeerId> {
152 Ok(EncodedPeerId::new(*peer_id.as_bytes()))
153}