Skip to content

Commit a7b33c2

Browse files
committed
Refactor Paybis quote handling to use quote_id
Updated Paybis request and provider logic to use quote_id instead of requested_amount/requested_amount_type. Adjusted related method signatures, struct fields, and tests to reflect this change. Added a mock_sell method for FiatQuoteRequest in testkit for improved test coverage.
1 parent 0446360 commit a7b33c2

File tree

10 files changed

+93
-172
lines changed

10 files changed

+93
-172
lines changed

apps/dynode/src/config/domain.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ mod tests {
137137
fn resolve_url_without_override() {
138138
let chain_config = make_chain_config(None, None);
139139
let base_url = make_url("https://example.com/rpc");
140-
assert_eq!(chain_config.resolve_url(&base_url, Some("eth_sendTransaction"), None).url, "https://example.com/rpc");
140+
assert_eq!(
141+
chain_config.resolve_url(&base_url, Some("eth_sendTransaction"), None).url,
142+
"https://example.com/rpc"
143+
);
141144
}
142145

143146
#[test]
@@ -176,7 +179,10 @@ mod tests {
176179
url: "https://tx-relay.example.com".to_string(),
177180
}]);
178181
let base_url = make_url("https://example.com/rpc");
179-
assert_eq!(chain_config.resolve_url(&base_url, Some("eth_blockNumber"), None).url, "https://example.com/rpc");
182+
assert_eq!(
183+
chain_config.resolve_url(&base_url, Some("eth_blockNumber"), None).url,
184+
"https://example.com/rpc"
185+
);
180186
}
181187

182188
#[test]

apps/dynode/src/config/mod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::{collections::HashMap, env, fs, path::{Path, PathBuf}};
1+
use std::{
2+
collections::HashMap,
3+
env, fs,
4+
path::{Path, PathBuf},
5+
};
26

37
use config::{Config, ConfigError, Environment, File};
48
use primitives::Chain;
@@ -120,12 +124,7 @@ pub fn load_config() -> Result<(NodeConfig, HashMap<Chain, ChainConfig>), Config
120124

121125
let chains = find_chain_files(&base_dir)
122126
.into_iter()
123-
.map(|path| {
124-
Config::builder()
125-
.add_source(File::from(path))
126-
.build()?
127-
.try_deserialize::<ChainsFile>()
128-
})
127+
.map(|path| Config::builder().add_source(File::from(path)).build()?.try_deserialize::<ChainsFile>())
129128
.collect::<Result<Vec<_>, _>>()?
130129
.into_iter()
131130
.flat_map(|cf| cf.chains)

apps/dynode/src/main.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use dynode::config::load_config;
66
use dynode::metrics::Metrics;
77
use dynode::monitoring::{NodeMonitor, NodeService};
88
use dynode::proxy::{ProxyRequestBuilder, ProxyResponse};
9-
use primitives::Chain;
109
use dynode::response::{ErrorResponse, ProxyRocketResponse};
1110
use gem_tracing::{error_with_fields, info_with_fields};
11+
use primitives::Chain;
1212
use reqwest::Method;
1313
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
1414
use rocket::config::Config;
@@ -83,7 +83,13 @@ async fn root_endpoint() -> &'static str {
8383
"ok"
8484
}
8585

86-
async fn process_proxy(chain: Chain, method: Method, request: &Request<'_>, data: Data<'_>, node_service: &NodeService) -> Result<ProxyResponse, ErrorResponse> {
86+
async fn process_proxy(
87+
chain: Chain,
88+
method: Method,
89+
request: &Request<'_>,
90+
data: Data<'_>,
91+
node_service: &NodeService,
92+
) -> Result<ProxyResponse, ErrorResponse> {
8793
let body = read_request_body(data).await?;
8894
let headers = build_header_map(request)?;
8995
let uri = request.uri().to_string();

apps/dynode/src/proxy/proxy_request_builder.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,7 @@ impl ProxyRequestBuilder {
1212
let user_agent = Self::extract_user_agent(&headers);
1313
let (path, path_with_query) = Self::prepare_paths(&uri);
1414

15-
Ok(ProxyRequest::new(
16-
method,
17-
headers,
18-
body,
19-
path,
20-
path_with_query,
21-
host,
22-
user_agent,
23-
chain,
24-
))
15+
Ok(ProxyRequest::new(method, headers, body, path, path_with_query, host, user_agent, chain))
2516
}
2617

2718
fn extract_host(headers: &HeaderMap) -> Result<String, Status> {
@@ -96,5 +87,4 @@ mod tests {
9687
assert_eq!(ProxyRequestBuilder::parse_hostname("example.com:8080"), "example.com");
9788
assert_eq!(ProxyRequestBuilder::parse_hostname("localhost:3000"), "localhost");
9889
}
99-
10090
}

crates/fiat/src/providers/paybis/client.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::models::{Assets, PaybisData, PaybisQuote, PaybisResponse, PaymentMethodWithLimits, QuoteRequest, Request, RequestResponse};
1+
use super::models::{Assets, PaybisQuote, PaybisResponse, QuoteRequest, Request, RequestResponse};
22
use crate::rsa_signature::generate_rsa_pss_signature;
33
use primitives::FiatProviderName;
44
use reqwest::Client;
@@ -89,18 +89,6 @@ impl PaybisClient {
8989
.into()
9090
}
9191

92-
pub async fn get_payment_method_limits(&self) -> Result<PaybisData<Vec<PaymentMethodWithLimits>>, Box<dyn std::error::Error + Send + Sync>> {
93-
let url = format!("{PAYBIS_API_BASE_URL}/v2/payment-method-list-with-limits");
94-
self.client
95-
.get(url)
96-
.header("Authorization", &self.api_key)
97-
.send()
98-
.await?
99-
.json::<PaybisResponse<PaybisData<Vec<PaymentMethodWithLimits>>>>()
100-
.await?
101-
.into()
102-
}
103-
10492
pub async fn create_request(&self, request_body: Request) -> Result<RequestResponse, Box<dyn std::error::Error + Send + Sync>> {
10593
let body = serde_json::to_string(&request_body)?;
10694
let url = format!("{PAYBIS_API_BASE_URL}/v3/request");
@@ -112,7 +100,7 @@ impl PaybisClient {
112100
wallet_address: &str,
113101
from_currency: &str,
114102
to_currency: &str,
115-
amount: f64,
103+
quote_id: &str,
116104
is_buy: bool,
117105
user_ip: &str,
118106
locale: &str,
@@ -123,7 +111,7 @@ impl PaybisClient {
123111
wallet_address.to_owned(),
124112
to_currency.to_string(),
125113
from_currency.to_string(),
126-
amount,
114+
quote_id.to_string(),
127115
user_ip.to_string(),
128116
locale.to_string(),
129117
)
@@ -133,7 +121,7 @@ impl PaybisClient {
133121
wallet_address.to_owned(),
134122
to_currency.to_string(),
135123
from_currency.to_string(),
136-
amount,
124+
quote_id.to_string(),
137125
user_ip.to_string(),
138126
locale.to_string(),
139127
)

crates/fiat/src/providers/paybis/mapper.rs

Lines changed: 22 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
use std::str::FromStr;
2-
31
use primitives::currency::Currency;
42
use primitives::{AssetId, Chain, FiatProviderName, FiatQuoteType, FiatTransaction, FiatTransactionStatus, PaymentType};
53
use streamer::FiatWebhook;
64

75
use crate::model::FiatProviderAsset;
8-
use crate::providers::paybis::models::{PaybisData, PaymentMethodWithLimits};
96
use primitives::fiat_assets::FiatAssetLimits;
107

118
use super::{
129
client::PaybisClient,
13-
models::{Currency as PaybisCurrency, PaybisWebhookData},
10+
models::{Currency as PaybisCurrency, PaybisData, PaybisWebhookData},
1411
};
1512

1613
pub fn map_asset_id(currency: PaybisCurrency) -> Option<AssetId> {
@@ -137,7 +134,16 @@ pub fn map_webhook_data(webhook_data: PaybisWebhookData) -> FiatWebhook {
137134
})
138135
}
139136

140-
fn map_asset(currency: PaybisCurrency, buy_limits: Vec<FiatAssetLimits>, sell_limits: Vec<FiatAssetLimits>) -> Option<FiatProviderAsset> {
137+
fn default_buy_limits() -> Vec<FiatAssetLimits> {
138+
vec![FiatAssetLimits {
139+
currency: Currency::USD,
140+
payment_type: PaymentType::Card,
141+
min_amount: None,
142+
max_amount: None,
143+
}]
144+
}
145+
146+
fn map_asset(currency: PaybisCurrency) -> Option<FiatProviderAsset> {
141147
if !currency.is_crypto() {
142148
return None;
143149
}
@@ -151,52 +157,13 @@ fn map_asset(currency: PaybisCurrency, buy_limits: Vec<FiatAssetLimits>, sell_li
151157
network: currency.blockchain_name.clone(),
152158
enabled: true,
153159
unsupported_countries: Some(currency.unsupported_countries()),
154-
buy_limits,
155-
sell_limits,
160+
buy_limits: default_buy_limits(),
161+
sell_limits: vec![],
156162
})
157163
}
158164

159165
pub fn map_assets(currencies: Vec<PaybisCurrency>) -> Vec<FiatProviderAsset> {
160-
currencies.into_iter().flat_map(|currency| map_asset(currency, vec![], vec![])).collect()
161-
}
162-
163-
fn map_payment_type(payment_method_name: &str) -> Option<PaymentType> {
164-
match payment_method_name {
165-
"gem-wallet-credit-card" => Some(PaymentType::Card),
166-
"gem-wallet-google-pay-credit-card" => Some(PaymentType::GooglePay),
167-
"gem-wallet-apple-pay-credit-card" => Some(PaymentType::ApplePay),
168-
_ => None,
169-
}
170-
}
171-
172-
pub fn map_assets_with_limits(currencies: Vec<PaybisCurrency>, limits: &PaybisData<Vec<PaymentMethodWithLimits>>) -> Vec<FiatProviderAsset> {
173-
currencies
174-
.into_iter()
175-
.filter_map(|currency| {
176-
let asset_buy_limits = limits
177-
.data
178-
.iter()
179-
.filter_map(|payment_method| map_payment_type(&payment_method.name).map(|payment_type| (payment_method, payment_type)))
180-
.flat_map(|(payment_method, payment_type)| {
181-
payment_method.pairs.iter().filter_map({
182-
let value = currency.code.clone();
183-
move |currency_pair| {
184-
currency_pair.to.iter().find(|c| c.currency_code == value).and_then(|currency_limit| {
185-
Currency::from_str(currency_pair.from.as_str()).ok().map(|fiat_currency| FiatAssetLimits {
186-
currency: fiat_currency,
187-
payment_type: payment_type.clone(),
188-
min_amount: Some(currency_limit.min_amount),
189-
max_amount: Some(currency_limit.max_amount),
190-
})
191-
})
192-
}
193-
})
194-
})
195-
.collect();
196-
197-
map_asset(currency, asset_buy_limits, vec![])
198-
})
199-
.collect()
166+
currencies.into_iter().flat_map(map_asset).collect()
200167
}
201168

202169
#[cfg(test)]
@@ -350,40 +317,13 @@ mod tests {
350317
}
351318

352319
#[test]
353-
fn test_paybis_limits_parsing() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
354-
let limits: PaybisData<Vec<PaymentMethodWithLimits>> = serde_json::from_str(include_str!("../../../testdata/paybis/assets_with_limits.json"))?;
355-
356-
let test_currencies = vec![
357-
PaybisCurrency {
358-
code: "USDT-TRC20".to_string(),
359-
blockchain_name: Some("tron".to_string()),
360-
},
361-
PaybisCurrency {
362-
code: "TRX".to_string(),
363-
blockchain_name: Some("tron".to_string()),
364-
},
365-
PaybisCurrency {
366-
code: "XRP".to_string(),
367-
blockchain_name: Some("xrp".to_string()),
368-
},
369-
];
370-
371-
let mapped_assets = map_assets_with_limits(test_currencies, &limits);
372-
373-
// Test that assets with limits have expected min/max amounts
374-
let usdt_trc20 = mapped_assets.iter().find(|a| a.symbol == "USDT-TRC20").expect("USDT-TRC20 should exist");
375-
assert!(!usdt_trc20.buy_limits.is_empty(), "USDT-TRC20 should have buy limits");
376-
377-
// Find USD limit
378-
let usd_limit = usdt_trc20.buy_limits.iter().find(|limit| limit.currency == Currency::USD);
379-
assert!(usd_limit.is_some(), "Should have USD limit");
380-
381-
if let Some(limit) = usd_limit {
382-
assert_eq!(limit.min_amount, Some(5.0));
383-
assert_eq!(limit.max_amount, Some(20000.0));
384-
assert_eq!(limit.payment_type, PaymentType::Card);
385-
}
386-
387-
Ok(())
320+
fn test_default_buy_limits() {
321+
let limits = default_buy_limits();
322+
323+
assert_eq!(limits.len(), 1);
324+
assert_eq!(limits[0].currency, Currency::USD);
325+
assert_eq!(limits[0].payment_type, PaymentType::Card);
326+
assert_eq!(limits[0].min_amount, None);
327+
assert_eq!(limits[0].max_amount, None);
388328
}
389329
}

crates/fiat/src/providers/paybis/models/limits.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use serde::{Deserialize, Serialize};
2-
use serde_serializers::deserialize_f64_from_str;
32

43
#[derive(Debug, Clone, Serialize, Deserialize)]
54
pub struct PaybisData<T> {
@@ -33,26 +32,3 @@ impl PaybisError {
3332
format!("Paybis API error [{}]: {}", self.code, self.message).into()
3433
}
3534
}
36-
37-
#[derive(Debug, Clone, Serialize, Deserialize)]
38-
pub struct PaymentMethodWithLimits {
39-
pub name: String,
40-
pub pairs: Vec<CurrencyPair>,
41-
}
42-
43-
#[derive(Debug, Clone, Serialize, Deserialize)]
44-
pub struct CurrencyPair {
45-
pub from: String,
46-
pub to: Vec<CurrencyLimit>,
47-
}
48-
49-
#[derive(Debug, Clone, Serialize, Deserialize)]
50-
pub struct CurrencyLimit {
51-
pub currency: String,
52-
#[serde(rename = "currencyCode")]
53-
pub currency_code: String,
54-
#[serde(rename = "minAmount", deserialize_with = "deserialize_f64_from_str")]
55-
pub min_amount: f64,
56-
#[serde(rename = "maxAmount", deserialize_with = "deserialize_f64_from_str")]
57-
pub max_amount: f64,
58-
}

crates/fiat/src/providers/paybis/models/request.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ pub struct Request {
1414
pub crypto_wallet_address: CryptoWalletAddress,
1515
pub currency_code_from: String,
1616
pub currency_code_to: String,
17-
pub requested_amount: f64,
18-
pub requested_amount_type: String,
17+
pub quote_id: String,
1918
pub user_ip: String,
2019
pub locale: String,
2120
}
@@ -32,7 +31,7 @@ impl Request {
3231
wallet_address: String,
3332
crypto_currency: String,
3433
fiat_currency: String,
35-
fiat_amount: f64,
34+
quote_id: String,
3635
user_ip: String,
3736
locale: String,
3837
) -> Self {
@@ -44,8 +43,7 @@ impl Request {
4443
},
4544
currency_code_from: fiat_currency,
4645
currency_code_to: crypto_currency,
47-
requested_amount: fiat_amount,
48-
requested_amount_type: "from".to_string(),
46+
quote_id,
4947
user_ip,
5048
locale,
5149
}
@@ -56,7 +54,7 @@ impl Request {
5654
wallet_address: String,
5755
crypto_currency: String,
5856
fiat_currency: String,
59-
crypto_amount: f64,
57+
quote_id: String,
6058
user_ip: String,
6159
locale: String,
6260
) -> Self {
@@ -68,8 +66,7 @@ impl Request {
6866
},
6967
currency_code_from: crypto_currency,
7068
currency_code_to: fiat_currency,
71-
requested_amount: crypto_amount,
72-
requested_amount_type: "from".to_string(),
69+
quote_id,
7370
user_ip,
7471
locale,
7572
}
@@ -90,8 +87,7 @@ mod tests {
9087
},
9188
currency_code_from: "USD".to_string(),
9289
currency_code_to: "SOL".to_string(),
93-
requested_amount: 50.0,
94-
requested_amount_type: "from".to_string(),
90+
quote_id: "test-quote-id".to_string(),
9591
user_ip: "1.2.3.4".to_string(),
9692
locale: "en".to_string(),
9793
};
@@ -104,8 +100,7 @@ mod tests {
104100
assert_eq!(parsed["cryptoWalletAddress"]["currencyCode"], "SOL");
105101
assert_eq!(parsed["currencyCodeFrom"], "USD");
106102
assert_eq!(parsed["currencyCodeTo"], "SOL");
107-
assert_eq!(parsed["requestedAmount"], 50.0);
108-
assert_eq!(parsed["requestedAmountType"], "from");
103+
assert_eq!(parsed["quoteId"], "test-quote-id");
109104
assert_eq!(parsed["userIp"], "1.2.3.4");
110105
assert_eq!(parsed["locale"], "en");
111106
}

0 commit comments

Comments
 (0)