use crate::config::Config;
#[cfg(any(
feature = "near",
feature = "xrpl",
feature = "hedera",
feature = "avm",
feature = "aptos",
feature = "keeta",
feature = "tvm",
feature = "stellar",
feature = "concordium",
feature = "experimental-tron",
))]
use crate::payment::price::ceiling_or_price;
#[cfg(any(
feature = "near",
feature = "xrpl",
feature = "hedera",
feature = "avm",
feature = "aptos",
feature = "keeta",
feature = "tvm",
feature = "stellar",
feature = "concordium",
feature = "experimental-tron",
))]
use crate::payment::tags::{price_tags, validate_payment};
fn paid(pay_to: &str, accepts: &str) -> String {
format!(
r#"
[server]
base_url = "https://o402.example.com"
[payment]
enabled = true
[payment.facilitator]
url = "http://127.0.0.1:9"
timeout_secs = 1
[payment.pay_to]
{pay_to}
{accepts}
[pricing.default]
scheme = "exact"
price = "0.001"
request_floor = "0.00001"
input_per_million = "0.15"
output_per_million = "0.60"
max_input_tokens = 128000
default_max_output_tokens = 16384
[[upstreams]]
name = "stub"
base_url = "http://127.0.0.1:9"
api_key = "sk-test"
[[models]]
id = "gpt-4o-mini"
upstream = "stub"
"#
)
}
fn exact_accept(network: &str, asset: &str) -> String {
format!(
r#"
[[payment.accepts]]
scheme = "exact"
network = "{network}"
asset = "{asset}"
"#
)
}
fn load(pay_to: &str, accepts: &str) -> Config {
Config::from_toml_str(&paid(pay_to, accepts)).expect("config")
}
#[cfg(any(
feature = "near",
feature = "xrpl",
feature = "hedera",
feature = "avm",
feature = "aptos",
feature = "keeta",
feature = "tvm",
feature = "stellar",
feature = "concordium",
feature = "experimental-tron",
))]
fn tag_amount(cfg: &Config) -> String {
drop(crate::http::app(cfg.clone()).expect("router"));
let rates = validate_payment(&cfg.payment, &cfg.models, cfg.pricing.as_ref()).expect("rates");
let mini = rates.models.get("gpt-4o-mini").expect("mini");
let atomic = ceiling_or_price(mini, 0, false);
let tags = price_tags(&cfg.payment, atomic).expect("tags");
let tag = tags.first().expect("tag");
assert_eq!(tag.requirements.scheme.as_str(), "exact", "scheme");
tag.requirements.amount.as_str().to_owned()
}
fn app_err(pay_to: &str, accepts: &str) -> String {
let cfg = load(pay_to, accepts);
crate::http::app(cfg).expect_err("startup").to_string()
}
fn upto_err(pay_to: &str, network: &str, asset: &str) -> String {
let accepts = format!(
r#"
[[payment.accepts]]
scheme = "upto"
network = "{network}"
asset = "{asset}"
"#
);
Config::from_toml_str(&paid(pay_to, &accepts))
.expect_err("upto")
.to_string()
}
#[cfg(any(feature = "hedera", feature = "stellar"))]
const EVM_PAY: &str = r#""eip155:*" = "0x0000000000000000000000000000000000000001""#;
const SOL_PAY: &str = r#""solana:*" = "11111111111111111111111111111111""#;
const NEAR_PAY: &str = r#""near:*" = "merchant.near""#;
const XRPL_PAY: &str = r#""xrpl:*" = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh""#;
const HEDERA_PAY: &str = r#""hedera:*" = "0.0.7001""#;
const ALGO_PAY: &str =
r#""algorand:*" = "AEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEA5RCDXMI""#;
const APTOS_PAY: &str =
r#""aptos:*" = "0x0000000000000000000000000000000000000000000000000000000000000001""#;
const KEETA_PAY: &str =
r#""keeta:*" = "keeta_amnkge74xitii5dsobstldatv3irmyimujfjotftx7plaaaseam4bntb7wnna""#;
const TVM_PAY: &str =
r#""tvm:*" = "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe""#;
const STELLAR_PAY: &str =
r#""stellar:*" = "GBBO4ZDDZTSM2IUKQYBAST3CFHNPFXECGEFTGWTA2WELR2BIWDK57UVE""#;
const CCD_PAY: &str = r#""ccd:*" = "2xBpaHottqhwFZURMZW4uZduQvpxNDSy46iXMYs9kceNGaPpZX""#;
const TRON_PAY: &str = r#""tron:*" = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t""#;
#[test]
fn unknown_namespace_is_unsupported() {
let err = app_err(
r#""casper:*" = "00fedcba""#,
&exact_accept("casper:casper-test", "usdc"),
);
assert!(
err.contains("network namespace 'casper' is not supported"),
"got {err}"
);
assert!(!err.contains("v1"), "got {err}");
}
#[test]
fn solana_rejects_transfer_method_and_eip712() {
let err = app_err(
SOL_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
asset = "usdc"
transfer_method = "permit2"
"#,
);
assert!(err.contains("transfer_method"), "got {err}");
let eip712_err = app_err(
SOL_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
asset = "usdc"
eip712_name = "USD Coin"
eip712_version = "2"
"#,
);
assert!(eip712_err.contains("eip712_name"), "got {eip712_err}");
}
#[test]
fn upto_on_solana_fails_startup() {
let err = upto_err(SOL_PAY, "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(not(feature = "near"))]
#[test]
fn near_requires_feature() {
let err = app_err(NEAR_PAY, &exact_accept("near:mainnet", "usdc"));
assert!(err.contains("cargo feature 'near'"), "got {err}");
}
#[cfg(not(feature = "xrpl"))]
#[test]
fn xrpl_requires_feature() {
let err = app_err(XRPL_PAY, &exact_accept("xrpl:0", "xrp"));
assert!(err.contains("cargo feature 'xrpl'"), "got {err}");
}
#[cfg(not(feature = "hedera"))]
#[test]
fn hedera_requires_feature() {
let err = app_err(HEDERA_PAY, &exact_accept("hedera:mainnet", "usdc"));
assert!(err.contains("cargo feature 'hedera'"), "got {err}");
}
#[cfg(not(feature = "avm"))]
#[test]
fn algorand_requires_feature() {
let err = app_err(
ALGO_PAY,
&exact_accept("algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k", "usdc"),
);
assert!(err.contains("cargo feature 'avm'"), "got {err}");
}
#[cfg(not(feature = "aptos"))]
#[test]
fn aptos_requires_feature() {
let err = app_err(APTOS_PAY, &exact_accept("aptos:1", "usdc"));
assert!(err.contains("cargo feature 'aptos'"), "got {err}");
}
#[cfg(not(feature = "keeta"))]
#[test]
fn keeta_requires_feature() {
let err = app_err(KEETA_PAY, &exact_accept("keeta:21378", "usdc"));
assert!(err.contains("cargo feature 'keeta'"), "got {err}");
}
#[cfg(not(feature = "tvm"))]
#[test]
fn tvm_requires_feature() {
let err = app_err(TVM_PAY, &exact_accept("tvm:-239", "usdt"));
assert!(err.contains("cargo feature 'tvm'"), "got {err}");
}
#[cfg(not(feature = "stellar"))]
#[test]
fn stellar_requires_feature() {
let err = app_err(STELLAR_PAY, &exact_accept("stellar:pubnet", "usdc"));
assert!(err.contains("cargo feature 'stellar'"), "got {err}");
}
#[cfg(not(feature = "concordium"))]
#[test]
fn ccd_requires_feature() {
let err = app_err(
CCD_PAY,
&exact_accept("ccd:9dd9ca4d19e9393877d2c44b70f89acb", "usdr"),
);
assert!(err.contains("cargo feature 'concordium'"), "got {err}");
}
#[cfg(not(feature = "experimental-tron"))]
#[test]
fn tron_requires_feature() {
let err = app_err(TRON_PAY, &exact_accept("tron:0x2b6653dc", "usdt"));
assert!(
err.contains("cargo feature 'experimental-tron'"),
"got {err}"
);
}
#[cfg(feature = "near")]
#[test]
fn near_named_usdc_tag() {
let cfg = load(NEAR_PAY, &exact_accept("near:mainnet", "usdc"));
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDC-6");
}
#[cfg(feature = "near")]
#[test]
fn near_upto_fails() {
let err = upto_err(NEAR_PAY, "near:mainnet", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "near")]
#[test]
fn near_unknown_ticker_fails() {
let err = app_err(NEAR_PAY, &exact_accept("near:mainnet", "usdt"));
assert!(err.contains("usdc"), "got {err}");
}
#[cfg(feature = "near")]
#[test]
fn near_custom_asset_address() {
let cfg = load(
NEAR_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "near:mainnet"
asset_address = "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom near");
}
#[cfg(feature = "xrpl")]
#[test]
fn xrpl_iou_formats_atomic_as_decimal() {
assert_eq!(
super::xrpl::xrpl_iou_amount_string(10u128.pow(12), 15).expect("12"),
"0.001",
"10^12 at 15 decimals"
);
assert_eq!(
super::xrpl::xrpl_iou_amount_string(10u128.pow(13), 15).expect("13"),
"0.01",
"10^13 at 15 decimals"
);
}
#[cfg(feature = "xrpl")]
#[test]
fn xrpl_xrp_drops_and_rlusd_decimal() {
let xrp = load(XRPL_PAY, &exact_accept("xrpl:0", "xrp"));
assert_eq!(tag_amount(&xrp), "1000", "0.001 XRP is 1000 drops");
let rlusd = load(XRPL_PAY, &exact_accept("xrpl:0", "rlusd"));
assert_eq!(tag_amount(&rlusd), "0.001", "RLUSD IOU decimal");
assert_ne!(tag_amount(&rlusd), "1000000000000", "not atomic integer");
}
#[cfg(feature = "xrpl")]
#[test]
fn xrpl_usdc_names_xrp_rlusd() {
let err = app_err(XRPL_PAY, &exact_accept("xrpl:0", "usdc"));
assert!(err.contains("xrp"), "got {err}");
assert!(err.contains("rlusd"), "got {err}");
}
#[cfg(feature = "xrpl")]
#[test]
fn xrpl_custom_asset_address_fails() {
let err = app_err(
XRPL_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "xrpl:0"
asset_address = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"
decimals = 15
"#,
);
assert!(err.contains("custom asset_address"), "got {err}");
}
#[cfg(feature = "xrpl")]
#[test]
fn xrpl_upto_fails() {
let err = upto_err(XRPL_PAY, "xrpl:0", "xrp");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "hedera")]
#[test]
fn hedera_named_usdc_tag() {
let cfg = load(HEDERA_PAY, &exact_accept("hedera:mainnet", "usdc"));
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDC-6");
}
#[cfg(feature = "hedera")]
#[test]
fn hedera_custom_asset_address() {
let cfg = load(
HEDERA_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "hedera:mainnet"
asset_address = "0.0.456858"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom hedera");
}
#[cfg(feature = "hedera")]
#[test]
fn hedera_upto_fails() {
let err = upto_err(HEDERA_PAY, "hedera:mainnet", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(all(feature = "hedera", feature = "evm"))]
#[test]
fn mixed_hbar_8_and_usdc_6_fails_decimals() {
let err = app_err(
&format!("{EVM_PAY}\n{HEDERA_PAY}"),
r#"
[[payment.accepts]]
scheme = "exact"
network = "eip155:8453"
asset = "usdc"
[[payment.accepts]]
scheme = "exact"
network = "hedera:mainnet"
asset = "hbar"
"#,
);
assert!(err.contains("decimals"), "got {err}");
assert!(err.contains('6') && err.contains('8'), "got {err}");
}
#[cfg(feature = "avm")]
#[test]
fn algorand_named_usdc_tag() {
let cfg = load(
ALGO_PAY,
&exact_accept("algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k", "usdc"),
);
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDC-6");
}
#[cfg(feature = "avm")]
#[test]
fn algorand_custom_asa_id() {
let cfg = load(
ALGO_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k"
asset_address = "31566704"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom ASA");
}
#[cfg(feature = "avm")]
#[test]
fn algorand_upto_fails() {
let err = upto_err(
ALGO_PAY,
"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k",
"usdc",
);
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "aptos")]
#[test]
fn aptos_named_usdc_tag() {
let cfg = load(APTOS_PAY, &exact_accept("aptos:1", "usdc"));
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDC-6");
}
#[cfg(feature = "aptos")]
#[test]
fn aptos_custom_asset_address() {
let cfg = load(
APTOS_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "aptos:1"
asset_address = "0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom aptos");
}
#[cfg(feature = "aptos")]
#[test]
fn aptos_upto_fails() {
let err = upto_err(APTOS_PAY, "aptos:1", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "keeta")]
#[test]
fn keeta_named_usdc_tag() {
let cfg = load(KEETA_PAY, &exact_accept("keeta:21378", "usdc"));
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDC-6");
}
#[cfg(feature = "keeta")]
#[test]
fn keeta_custom_asset_address() {
let cfg = load(
KEETA_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "keeta:21378"
asset_address = "keeta_apna75yhhvnv4ei7ape55hndk4yepno7a7i2mhtiwahiygixjcnmvswxhnmnk"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom keeta");
}
#[cfg(feature = "keeta")]
#[test]
fn keeta_upto_fails() {
let err = upto_err(KEETA_PAY, "keeta:21378", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "tvm")]
#[test]
fn tvm_named_usdt_tag() {
let cfg = load(TVM_PAY, &exact_accept("tvm:-239", "usdt"));
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDT-6");
}
#[cfg(feature = "tvm")]
#[test]
fn tvm_usdc_names_usdt() {
let err = app_err(TVM_PAY, &exact_accept("tvm:-239", "usdc"));
assert!(err.contains("usdt"), "got {err}");
}
#[cfg(feature = "tvm")]
#[test]
fn tvm_custom_asset_address() {
let cfg = load(
TVM_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "tvm:-239"
asset_address = "0:f418a04cf196ebc959366844a6cdf53a6fd6fff1eadafc892f05210bba31593e"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom tvm");
}
#[cfg(feature = "tvm")]
#[test]
fn tvm_upto_fails() {
let err = upto_err(TVM_PAY, "tvm:-239", "usdt");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "stellar")]
#[test]
fn stellar_named_usdc_tag() {
let cfg = load(STELLAR_PAY, &exact_accept("stellar:pubnet", "usdc"));
assert_eq!(tag_amount(&cfg), "10000", "0.001 USDC-7");
}
#[cfg(feature = "stellar")]
#[test]
fn stellar_custom_asset_address() {
let cfg = load(
STELLAR_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "stellar:pubnet"
asset_address = "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"
decimals = 7
"#,
);
assert_eq!(tag_amount(&cfg), "10000", "custom stellar");
}
#[cfg(feature = "stellar")]
#[test]
fn stellar_upto_fails() {
let err = upto_err(STELLAR_PAY, "stellar:pubnet", "usdc");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(all(feature = "stellar", feature = "evm"))]
#[test]
fn mixed_stellar_7_and_eip155_6_fails_decimals() {
let err = app_err(
&format!("{EVM_PAY}\n{STELLAR_PAY}"),
r#"
[[payment.accepts]]
scheme = "exact"
network = "eip155:8453"
asset = "usdc"
[[payment.accepts]]
scheme = "exact"
network = "stellar:pubnet"
asset = "usdc"
"#,
);
assert!(err.contains("decimals"), "got {err}");
assert!(err.contains('6') && err.contains('7'), "got {err}");
}
#[cfg(feature = "concordium")]
#[test]
fn ccd_named_usdr_tag() {
let cfg = load(
CCD_PAY,
&exact_accept("ccd:9dd9ca4d19e9393877d2c44b70f89acb", "usdr"),
);
assert_eq!(tag_amount(&cfg), "1000", "0.001 USDR-6");
}
#[cfg(feature = "concordium")]
#[test]
fn ccd_custom_asset_address_fails() {
let err = app_err(
CCD_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "ccd:9dd9ca4d19e9393877d2c44b70f89acb"
asset_address = "2xBpaHottqhwFZURMZW4uZduQvpxNDSy46iXMYs9kceNGaPpZX"
decimals = 6
"#,
);
assert!(err.contains("custom asset_address"), "got {err}");
}
#[cfg(feature = "concordium")]
#[test]
fn ccd_usdc_names_usdr_ccd() {
let err = app_err(
CCD_PAY,
&exact_accept("ccd:9dd9ca4d19e9393877d2c44b70f89acb", "usdc"),
);
assert!(err.contains("usdr"), "got {err}");
assert!(err.contains("ccd"), "got {err}");
}
#[cfg(feature = "concordium")]
#[test]
fn ccd_upto_fails() {
let err = upto_err(CCD_PAY, "ccd:9dd9ca4d19e9393877d2c44b70f89acb", "usdr");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "experimental-tron")]
#[test]
fn tron_named_usdt_defaults_to_permit2() {
let cfg = load(TRON_PAY, &exact_accept("tron:0x2b6653dc", "usdt"));
drop(crate::http::app(cfg.clone()).expect("router"));
let rates = validate_payment(&cfg.payment, &cfg.models, cfg.pricing.as_ref()).expect("rates");
let mini = rates.models.get("gpt-4o-mini").expect("mini");
let tags = price_tags(&cfg.payment, ceiling_or_price(mini, 0, false)).expect("tags");
let tag = tags.first().expect("tag");
assert_eq!(tag.requirements.amount.as_str(), "1000", "0.001 USDT-6");
assert_eq!(tag.requirements.scheme.as_str(), "exact", "scheme");
let extra = tag.requirements.extra.as_ref().expect("extra");
assert_eq!(
extra.get("assetTransferMethod"),
Some(&serde_json::json!("permit2")),
"Permit2 default"
);
}
#[cfg(feature = "experimental-tron")]
#[test]
fn tron_named_usdt_eip3009_fails() {
let err = app_err(
TRON_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "tron:0x2b6653dc"
asset = "usdt"
transfer_method = "eip3009"
"#,
);
assert!(err.contains("eip3009"), "got {err}");
}
#[cfg(feature = "experimental-tron")]
#[test]
fn tron_custom_eip3009_uses_tip712() {
let cfg = load(
TRON_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "tron:0x2b6653dc"
asset_address = "TXLAQ63Xg1NAzckPwKHvzw7CSEmLMEqcdj"
decimals = 6
eip712_name = "Tether USD"
eip712_version = "1"
transfer_method = "eip3009"
"#,
);
drop(crate::http::app(cfg.clone()).expect("router"));
let rates = validate_payment(&cfg.payment, &cfg.models, cfg.pricing.as_ref()).expect("rates");
let mini = rates.models.get("gpt-4o-mini").expect("mini");
let tags = price_tags(&cfg.payment, ceiling_or_price(mini, 0, false)).expect("tags");
let extra = tags
.first()
.and_then(|tag| tag.requirements.extra.as_ref())
.expect("extra");
assert_eq!(
extra.get("name"),
Some(&serde_json::json!("Tether USD")),
"tip712"
);
assert_eq!(
extra.get("version"),
Some(&serde_json::json!("1")),
"tip712"
);
assert_eq!(
extra.get("assetTransferMethod"),
Some(&serde_json::json!("eip3009")),
"eip3009"
);
}
#[cfg(feature = "experimental-tron")]
#[test]
fn tron_custom_asset_address() {
let cfg = load(
TRON_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "tron:0x2b6653dc"
asset_address = "TXLAQ63Xg1NAzckPwKHvzw7CSEmLMEqcdj"
decimals = 6
"#,
);
assert_eq!(tag_amount(&cfg), "1000", "custom tron");
}
#[cfg(feature = "experimental-tron")]
#[test]
fn tron_upto_fails() {
let err = upto_err(TRON_PAY, "tron:0x2b6653dc", "usdt");
assert!(err.contains("eip155"), "got {err}");
}
#[cfg(feature = "near")]
#[test]
fn near_rejects_transfer_method() {
let err = app_err(
NEAR_PAY,
r#"
[[payment.accepts]]
scheme = "exact"
network = "near:mainnet"
asset = "usdc"
transfer_method = "permit2"
"#,
);
assert!(err.contains("transfer_method"), "got {err}");
}