o402 0.1.5

OpenAI-compatible gateway, paid with x402.
//! solana exact accepts.

use r402_protocol::network::ChainId;
use r402_protocol::payment::PriceTag;

#[cfg(not(feature = "svm"))]
use super::feature_missing;
#[cfg(feature = "svm")]
use super::{
    accept_custom_decimals, amount_u64, custom_asset_address, invalid_asset_address,
    invalid_network, invalid_pay_to, named_ticker, no_deployment, reject_evm_fields, require_exact,
};
use crate::config::{AcceptConfig, ConfigError};

#[cfg(not(feature = "svm"))]
pub(super) fn build(
    _accept: &AcceptConfig,
    _network: &ChainId,
    _amount: u128,
    _pay_to: &str,
) -> Result<PriceTag, ConfigError> {
    Err(feature_missing("solana", "svm"))
}

#[cfg(not(feature = "svm"))]
pub(super) fn decimals(_accept: &AcceptConfig, _network: &ChainId) -> Result<u8, ConfigError> {
    Err(feature_missing("solana", "svm"))
}

#[cfg(feature = "svm")]
pub(super) fn decimals(accept: &AcceptConfig, network: &ChainId) -> Result<u8, ConfigError> {
    Ok(token(accept, network)?.decimals)
}

#[cfg(feature = "svm")]
pub(super) fn build(
    accept: &AcceptConfig,
    network: &ChainId,
    amount: u128,
    pay_to: &str,
) -> Result<PriceTag, ConfigError> {
    use std::str::FromStr as _;

    use r402_svm::SolanaExact;
    use r402_svm::chain::Address;
    use solana_pubkey::Pubkey;

    require_exact(accept)?;
    reject_evm_fields(accept, "solana")?;
    let amount = amount_u64(amount, "solana")?;
    let pay_to =
        Pubkey::from_str(pay_to).map_err(|error| invalid_pay_to("Solana", pay_to, error))?;
    let token = token(accept, network)?;
    Ok(SolanaExact::price_tag(
        Address::from(pay_to),
        token.amount(amount),
    ))
}

#[cfg(feature = "svm")]
fn token(
    accept: &AcceptConfig,
    network: &ChainId,
) -> Result<r402_svm::chain::SolanaTokenDeployment, ConfigError> {
    use std::str::FromStr as _;

    use r402_svm::USDC;
    use r402_svm::chain::{Address, SolanaChainReference, SolanaTokenDeployment, TOKEN_PROGRAM_ID};
    use solana_pubkey::Pubkey;

    let chain = SolanaChainReference::try_from(network.clone())
        .map_err(|error| invalid_network("solana", &accept.network, error))?;
    if named_ticker(accept, &["usdc"])?.is_some() {
        return USDC::on(&chain)
            .copied()
            .ok_or_else(|| no_deployment("USDC", &accept.network));
    }
    let mint = custom_asset_address(accept)?;
    let mint = Pubkey::from_str(mint).map_err(|error| invalid_asset_address(mint, error))?;
    let decimals = accept_custom_decimals(accept)?;
    Ok(SolanaTokenDeployment::new(
        chain,
        Address::from(mint),
        decimals,
        TOKEN_PROGRAM_ID,
    ))
}