use r402_protocol::network::ChainId;
use r402_protocol::payment::PriceTag;
#[cfg(not(feature = "evm"))]
use super::feature_missing;
#[cfg(feature = "evm")]
use super::{
accept_custom_decimals, custom_asset_address, invalid_asset_address, invalid_network,
invalid_pay_to, named_ticker, no_deployment,
};
use crate::config::{AcceptConfig, ConfigError};
#[cfg(not(feature = "evm"))]
pub(super) fn build(
_accept: &AcceptConfig,
_network: &ChainId,
_amount: u128,
_pay_to: &str,
) -> Result<PriceTag, ConfigError> {
Err(feature_missing("eip155", "evm"))
}
#[cfg(not(feature = "evm"))]
pub(super) fn decimals(_accept: &AcceptConfig, _network: &ChainId) -> Result<u8, ConfigError> {
Err(feature_missing("eip155", "evm"))
}
#[cfg(feature = "evm")]
pub(super) fn decimals(accept: &AcceptConfig, network: &ChainId) -> Result<u8, ConfigError> {
Ok(token(accept, network)?.decimals)
}
#[cfg(feature = "evm")]
pub(super) fn build(
accept: &AcceptConfig,
network: &ChainId,
amount: u128,
pay_to: &str,
) -> Result<PriceTag, ConfigError> {
use std::str::FromStr as _;
use alloy_primitives::{Address, U256};
use r402_evm::chain::ChecksummedAddress;
use r402_evm::{Eip155Exact, Eip155Upto};
let pay_to = Address::from_str(pay_to).map_err(|error| invalid_pay_to("EVM", pay_to, error))?;
let token = token(accept, network)?;
let asset = token.amount(U256::from(amount));
match accept.scheme.as_str() {
"upto" => {
if accept.transfer_method.is_some() {
return Err(ConfigError::Validation(
"transfer_method is not valid on upto accepts".to_owned(),
));
}
Ok(Eip155Upto::price_tag(
ChecksummedAddress::from(pay_to),
asset,
))
}
"exact" => {
let method = parse_transfer_method(accept.transfer_method.as_deref())?;
Ok(Eip155Exact::price_tag(
ChecksummedAddress::from(pay_to),
asset,
method,
))
}
other => Err(ConfigError::Validation(format!(
"payment.accepts scheme '{other}' is not supported"
))),
}
}
#[cfg(feature = "evm")]
fn token(
accept: &AcceptConfig,
network: &ChainId,
) -> Result<r402_evm::chain::Eip155TokenDeployment, ConfigError> {
use std::str::FromStr as _;
use alloy_primitives::Address;
use r402_evm::USDC;
use r402_evm::chain::{Eip155ChainReference, Eip155TokenDeployment, TokenDeploymentEip712};
let chain = Eip155ChainReference::try_from(network)
.map_err(|error| invalid_network("eip155", &accept.network, error))?;
if named_ticker(accept, &["usdc"])?.is_some() {
return USDC::on(chain)
.cloned()
.ok_or_else(|| no_deployment("USDC", &accept.network));
}
let address = custom_asset_address(accept)?;
let address =
Address::from_str(address).map_err(|error| invalid_asset_address(address, error))?;
let decimals = accept_custom_decimals(accept)?;
let eip712 = match (
accept.eip712_name.as_deref(),
accept.eip712_version.as_deref(),
) {
(None, None) => None,
(Some(name), Some(version)) => Some(TokenDeploymentEip712 {
name: name.to_owned(),
version: version.to_owned(),
}),
_ => {
return Err(ConfigError::Validation(
"custom EVM accepts need both eip712_name and eip712_version".to_owned(),
));
}
};
Ok(Eip155TokenDeployment {
chain_reference: chain,
address,
decimals,
eip712,
})
}
#[cfg(feature = "evm")]
fn parse_transfer_method(
raw: Option<&str>,
) -> Result<Option<r402_evm::AssetTransferMethod>, ConfigError> {
match raw {
None | Some("eip3009") => Ok(None),
Some("permit2") => Ok(Some(r402_evm::AssetTransferMethod::Permit2)),
Some(other) => Err(ConfigError::Validation(format!(
"unsupported transfer_method '{other}'"
))),
}
}