ant_core/data/client/
payment.rs1use crate::data::client::quote::median_paid_quote_issuer;
7use crate::data::client::Client;
8use crate::data::error::{Error, Result};
9use ant_protocol::evm::{EncodedPeerId, ProofOfPayment, Wallet};
10use ant_protocol::payment::{serialize_single_node_proof, PaymentProof, SingleNodePayment};
11use ant_protocol::transport::{MultiAddr, PeerId};
12use std::sync::Arc;
13use tracing::{debug, info};
14
15impl Client {
16 pub(crate) fn require_wallet(&self) -> Result<&Arc<Wallet>> {
18 self.wallet().ok_or_else(|| {
19 Error::Payment("Wallet not configured — call with_wallet() first".to_string())
20 })
21 }
22
23 pub async fn pay_for_storage(
40 &self,
41 address: &[u8; 32],
42 data_size: u64,
43 data_type: u32,
44 ) -> Result<(Vec<u8>, Vec<(PeerId, Vec<MultiAddr>)>)> {
45 let wallet = self.require_wallet()?;
48
49 debug!("Collecting quotes for address {}", hex::encode(address));
50
51 let quote_plan = self
53 .get_store_quote_plan(address, data_size, data_type)
54 .await?;
55 let quotes_with_peers = quote_plan.quotes;
56 let median_quote_issuer =
57 median_paid_quote_issuer("es_with_peers).ok_or_else(|| {
58 Error::Payment(
59 "Failed to select median quote issuer from witnessed quotes".to_string(),
60 )
61 })?;
62
63 let quoted_peers = quote_plan.put_peers;
66
67 let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len());
70 let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len());
71 let mut commitment_sidecars = Vec::new();
77
78 for (peer_id, _addrs, quote, price, commitment) in quotes_with_peers {
79 let encoded = peer_id_to_encoded(&peer_id)?;
80 peer_quotes.push((encoded, quote.clone()));
81 quotes_for_payment.push((quote, price));
82 if let Some(sidecar) = commitment {
83 commitment_sidecars.push(sidecar);
84 }
85 }
86
87 let payment = SingleNodePayment::from_quotes(quotes_for_payment)
89 .map_err(|e| Error::Payment(format!("Failed to create payment: {e}")))?;
90
91 info!(
92 "Selected SNP median paid quote issuer {} for address {} (median price: {})",
93 median_quote_issuer.0,
94 hex::encode(address),
95 median_quote_issuer.1
96 );
97 info!("Payment total: {} atto", payment.total_amount());
98
99 let tx_hashes = payment
101 .pay(wallet)
102 .await
103 .map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))?;
104
105 info!(
106 "On-chain payment succeeded: {} transactions",
107 tx_hashes.len()
108 );
109
110 let proof = PaymentProof {
112 proof_of_payment: ProofOfPayment { peer_quotes },
113 tx_hashes,
114 commitment_sidecars,
115 };
116
117 let proof_bytes = serialize_single_node_proof(&proof)
118 .map_err(|e| Error::Serialization(format!("Failed to serialize payment proof: {e}")))?;
119
120 Ok((proof_bytes, quoted_peers))
121 }
122
123 pub async fn approve_token_spend(&self) -> Result<()> {
132 let wallet = self.require_wallet()?;
133 let evm_network = self.require_evm_network()?;
134
135 let vault_address = evm_network.payment_vault_address();
136 wallet
137 .approve_to_spend_tokens(*vault_address, ant_protocol::evm::U256::MAX)
138 .await
139 .map_err(|e| Error::Payment(format!("Token approval failed: {e}")))?;
140 info!("Token spend approved for payment vault contract");
141
142 Ok(())
143 }
144}
145
146pub(crate) fn peer_id_to_encoded(peer_id: &PeerId) -> Result<EncodedPeerId> {
148 Ok(EncodedPeerId::new(*peer_id.as_bytes()))
149}