Skip to main content

r402_stellar/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(
3    test,
4    allow(
5        unknown_lints,
6        clippy::unused_async_trait_impl,
7        reason = "in-crate mock impls of AFIT traits have no .await"
8    )
9)]
10
11//! Stellar chain support for the x402 payment protocol.
12//!
13//! This crate implements the x402 `"exact"` scheme for Stellar: the buyer
14//! signs Soroban authorization entries for one SEP-41 `transfer`, and the
15//! facilitator is the transaction source / fee sponsor.
16//!
17//! # Features
18//!
19//! - **CAIP-2 Addressing**: `stellar:pubnet` and `stellar:testnet`
20//! - **SEP-41 Payments**: exact `transfer` of a Soroban token (USDC by default)
21//! - **Auth-entry signing**: the client never spends a sequence number
22//! - **In-process facilitator**: RPC verify, rebuild, submit, and confirm
23//!
24//! # Feature Flags
25//!
26//! - `server` — server-side price tag generation
27//! - `client` — client-side authorization-entry signing
28//! - `facilitator` — facilitator-side payment verification and settlement
29//! - `telemetry` — `tracing` instrumentation
30
31#[cfg(feature = "telemetry")]
32use tracing_core as _;
33
34/// Default USDC decimal precision on Stellar (SEP-41).
35pub const DEFAULT_TOKEN_DECIMALS: u8 = 7;
36
37/// Inclusion buffer in stroops (`BASE_FEE` in the official Stellar SDK).
38pub const BASE_FEE_STROOPS: u32 = 100;
39
40/// Safety ceiling for simulation-derived settlement fees (stroops).
41pub const DEFAULT_MAX_TRANSACTION_FEE_STROOPS: u32 = 50_000;
42
43/// Fallback ledger close time when Horizon is unavailable.
44pub const DEFAULT_ESTIMATED_LEDGER_SECONDS: u64 = 5;
45
46/// Ledger-skew tolerance applied to auth-entry expiration.
47pub const SIGNATURE_EXPIRATION_LEDGER_TOLERANCE: u32 = 2;
48
49/// Default `maxTimeoutSeconds` when the requirement omits it.
50pub const DEFAULT_TIMEOUT_SECONDS: u64 = 60;
51
52/// Number of recent Horizon ledgers sampled for close-time estimation.
53pub const HORIZON_LEDGERS_SAMPLE_SIZE: u32 = 20;
54
55/// Dummy source used by the client so only auth entries are signed.
56///
57/// Matches `@stellar/stellar-sdk` `NULL_ACCOUNT`.
58pub const NULL_ACCOUNT: &str = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
59
60/// SEP-41 transfer method name.
61pub const TRANSFER_FUNCTION: &str = "transfer";
62
63pub mod chain;
64pub mod exact;
65
66mod networks;
67#[cfg(any(feature = "client", feature = "facilitator"))]
68pub use chain::StellarJsonRpc;
69#[cfg(feature = "facilitator")]
70pub use chain::{StellarChainProvider, StellarFacilitatorError};
71#[cfg(any(feature = "client", feature = "facilitator"))]
72pub use chain::{StellarRpc, StellarRpcError, StellarSigner};
73pub use exact::StellarExact;
74#[cfg(feature = "client")]
75pub use exact::client::StellarExactClient;
76pub use networks::*;
77
78/// `ledgerTimeout = ceil(maxTimeoutSeconds / estimatedLedgerSeconds)`.
79#[must_use]
80pub fn timeout_ledgers(max_timeout_seconds: u64, estimated_ledger_seconds: u64) -> u32 {
81    let estimated = estimated_ledger_seconds.max(1);
82    let ledgers = max_timeout_seconds.div_ceil(estimated);
83    u32::try_from(ledgers).unwrap_or(u32::MAX)
84}
85
86/// SHA-256 of the network passphrase (Stellar network id).
87#[cfg(any(feature = "client", feature = "facilitator"))]
88#[must_use]
89pub fn network_id(passphrase: &str) -> [u8; 32] {
90    use sha2::{Digest, Sha256};
91    Sha256::digest(passphrase.as_bytes()).into()
92}