Skip to content

Commit b106c91

Browse files
feat: exchange projectid validation (#1075)
* chore: add validation method * chore: add projectId allowlist * chore: use &String to avoid deref * fix: tests
1 parent 5b4860e commit b106c91

12 files changed

+87
-19
lines changed

.env.example

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,6 @@ export RPC_PROXY_PROVIDER_MELD_API_URL=""
2222
export RPC_PROXY_POSTGRES_URI="postgres://postgres@localhost/postgres"
2323

2424

25-
# Payments Exchanges
26-
#export RPC_PROXY_EXCHANGES_COINBASE_PROJECT_ID=""
27-
#export RPC_PROXY_EXCHANGES_COINBASE_KEY_NAME=""
28-
#export RPC_PROXY_EXCHANGES_COINBASE_KEY_SECRET=""
29-
#export RPC_PROXY_EXCHANGES_BINANCE_CLIENT_ID=""
30-
#export RPC_PROXY_EXCHANGES_BINANCE_TOKEN=""
31-
#export RPC_PROXY_EXCHANGES_BINANCE_KEY=""
32-
#export RPC_PROXY_EXCHANGES_BINANCE_HOST=""
3325

3426
# Uncomment for Project ID that is allowed to make a test-specific requests
3527
# export RPC_PROXY_TESTING_PROJECT_ID=""
@@ -59,3 +51,14 @@ export RPC_PROXY_POSTGRES_URI="postgres://postgres@localhost/postgres"
5951

6052
# Uncomment for using the ENS names offchain gateway
6153
# export RPC_PROXY_NAMES_ALLOWED_ZONES="eth.id,xyz.id"
54+
55+
56+
# Payments
57+
#export RPC_PROXY_EXCHANGES_COINBASE_PROJECT_ID=""
58+
#export RPC_PROXY_EXCHANGES_COINBASE_KEY_NAME=""
59+
#export RPC_PROXY_EXCHANGES_COINBASE_KEY_SECRET=""
60+
#export RPC_PROXY_EXCHANGES_BINANCE_CLIENT_ID=""
61+
#export RPC_PROXY_EXCHANGES_BINANCE_TOKEN=""
62+
#export RPC_PROXY_EXCHANGES_BINANCE_KEY=""
63+
#export RPC_PROXY_EXCHANGES_BINANCE_HOST=""
64+
#export RPC_PROXY_EXCHANGES_ALLOWED_PROJECT_IDS=""

docker-compose.mock-bundler.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ services:
33
image: ghcr.io/foundry-rs/foundry:stable
44
restart: unless-stopped
55
ports: ["8545:8545"]
6-
entrypoint: [ "anvil", "--fork-url", "https://gateway.tenderly.co/public/sepolia", "--host", "0.0.0.0", "--gas-price", "1" ]
6+
entrypoint: [ "anvil", "--fork-url", "https://sepolia.gateway.tenderly.co", "--host", "0.0.0.0", "--gas-price", "1" ]
77
platform: linux/amd64
88

99
mock-paymaster:

src/env/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ mod test {
250250
("RPC_PROXY_EXCHANGES_BINANCE_TOKEN", "BINANCE_TOKEN"),
251251
("RPC_PROXY_EXCHANGES_BINANCE_KEY", "BINANCE_KEY"),
252252
("RPC_PROXY_EXCHANGES_BINANCE_HOST", "BINANCE_HOST"),
253+
(
254+
"RPC_PROXY_EXCHANGES_ALLOWED_PROJECT_IDS",
255+
"test_project_id,test_project_id_2",
256+
),
253257
];
254258

255259
values.iter().for_each(set_env_var);
@@ -356,6 +360,10 @@ mod test {
356360
binance_host: Some("BINANCE_HOST".to_owned()),
357361
coinbase_key_name: Some("COINBASE_KEY_NAME".to_owned()),
358362
coinbase_key_secret: Some("COINBASE_KEY_SECRET".to_owned()),
363+
allowed_project_ids: Some(vec![
364+
"test_project_id".to_owned(),
365+
"test_project_id_2".to_owned(),
366+
]),
359367
},
360368
}
361369
);

src/handlers/wallet/exchanges/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
strum::IntoEnumIterator,
77
strum_macros::{AsRefStr, EnumIter},
88
thiserror::Error,
9+
tracing::debug,
910
};
1011

1112
pub mod binance;
@@ -23,6 +24,7 @@ pub struct Config {
2324
pub binance_token: Option<String>,
2425
pub binance_key: Option<String>,
2526
pub binance_host: Option<String>,
27+
pub allowed_project_ids: Option<Vec<String>>,
2628
}
2729

2830
#[derive(Debug, Serialize, Clone)]
@@ -93,6 +95,9 @@ pub enum ExchangeError {
9395
#[error("Get pay url error: {0}")]
9496
GetPayUrlError(String),
9597

98+
#[error("Feature not enabled: {0}")]
99+
FeatureNotEnabled(String),
100+
96101
#[error("Internal error")]
97102
InternalError(String),
98103
}
@@ -157,3 +162,25 @@ pub fn get_supported_exchanges(asset: Option<String>) -> Result<Vec<Exchange>, E
157162
pub fn get_exchange_by_id(id: &str) -> Option<Exchange> {
158163
ExchangeType::from_id(id).map(|e| e.to_exchange())
159164
}
165+
166+
pub fn is_feature_enabled_for_project_id(
167+
state: State<Arc<AppState>>,
168+
project_id: &String,
169+
) -> Result<(), ExchangeError> {
170+
let allowed_project_ids = state
171+
.config
172+
.exchanges
173+
.allowed_project_ids
174+
.as_ref()
175+
.ok_or_else(|| ExchangeError::FeatureNotEnabled("Feature is not enabled".to_string()))?;
176+
177+
debug!("allowed_project_ids: {:?}", allowed_project_ids);
178+
179+
if !allowed_project_ids.contains(project_id) {
180+
return Err(ExchangeError::FeatureNotEnabled(
181+
"Project is not allowed to use this feature".to_string(),
182+
));
183+
}
184+
185+
Ok(())
186+
}

src/handlers/wallet/get_exchange_buy_status.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use {
22
crate::handlers::wallet::exchanges::{
3-
BuyTransactionStatus, ExchangeError, ExchangeType, GetBuyStatusParams,
3+
is_feature_enabled_for_project_id, BuyTransactionStatus, ExchangeError, ExchangeType,
4+
GetBuyStatusParams,
45
},
56
crate::{
67
handlers::{SdkInfoParams, HANDLER_TASK_METRICS},
@@ -64,11 +65,14 @@ impl GetExchangeBuyStatusError {
6465

6566
pub async fn handler(
6667
state: State<Arc<AppState>>,
68+
project_id: String,
6769
connect_info: ConnectInfo<SocketAddr>,
6870
headers: HeaderMap,
6971
query: Query<QueryParams>,
7072
Json(request): Json<GetExchangeBuyStatusRequest>,
7173
) -> Result<GetExchangeBuyStatusResponse, GetExchangeBuyStatusError> {
74+
is_feature_enabled_for_project_id(state.clone(), &project_id)
75+
.map_err(|e| GetExchangeBuyStatusError::ValidationError(e.to_string()))?;
7276
handler_internal(state, connect_info, headers, query, request)
7377
.with_metrics(HANDLER_TASK_METRICS.with_name("pay_get_exchange_buy_status"))
7478
.await

src/handlers/wallet/get_exchange_url.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use {
2-
crate::handlers::wallet::exchanges::{ExchangeError, ExchangeType, GetBuyUrlParams},
2+
crate::handlers::wallet::exchanges::{
3+
is_feature_enabled_for_project_id, ExchangeError, ExchangeType, GetBuyUrlParams,
4+
},
35
crate::{
46
handlers::{SdkInfoParams, HANDLER_TASK_METRICS},
57
state::AppState,
@@ -67,6 +69,8 @@ pub async fn handler(
6769
query: Query<QueryParams>,
6870
Json(request): Json<GeneratePayUrlRequest>,
6971
) -> Result<GeneratePayUrlResponse, GetExchangeUrlError> {
72+
is_feature_enabled_for_project_id(state.clone(), &project_id)
73+
.map_err(|e| GetExchangeUrlError::ValidationError(e.to_string()))?;
7074
handler_internal(state, project_id, connect_info, headers, query, request)
7175
.with_metrics(HANDLER_TASK_METRICS.with_name("pay_get_exchange_url"))
7276
.await

src/handlers/wallet/get_exchanges.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use {
2-
crate::handlers::wallet::exchanges::{get_supported_exchanges, Exchange},
2+
crate::handlers::wallet::exchanges::{
3+
get_supported_exchanges, is_feature_enabled_for_project_id, Exchange,
4+
},
35
crate::{
46
handlers::{SdkInfoParams, HANDLER_TASK_METRICS},
57
state::AppState,
@@ -67,11 +69,14 @@ impl GetExchangesError {
6769

6870
pub async fn handler(
6971
state: State<Arc<AppState>>,
72+
project_id: String,
7073
connect_info: ConnectInfo<SocketAddr>,
7174
headers: HeaderMap,
7275
query: Query<QueryParams>,
7376
Json(request): Json<GetExchangesRequest>,
7477
) -> Result<GetExchangesResponse, GetExchangesError> {
78+
is_feature_enabled_for_project_id(state.clone(), &project_id)
79+
.map_err(|e| GetExchangesError::ValidationError(e.to_string()))?;
7580
handler_internal(state, connect_info, headers, query, request)
7681
.with_metrics(HANDLER_TASK_METRICS.with_name("pay_get_exchanges"))
7782
.await

src/handlers/wallet/handler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ async fn handle_rpc(
244244
PAY_GET_EXCHANGES => serde_json::to_value(
245245
&get_exchanges::handler(
246246
state,
247+
project_id,
247248
connect_info,
248249
headers,
249250
Query(get_exchanges::QueryParams {
@@ -273,6 +274,7 @@ async fn handle_rpc(
273274
PAY_GET_EXCHANGE_BUY_STATUS => serde_json::to_value(
274275
&get_exchange_buy_status::handler(
275276
state,
277+
project_id,
276278
connect_info,
277279
headers,
278280
Query(get_exchange_buy_status::QueryParams {

terraform/ecs/cluster.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ resource "aws_ecs_task_definition" "app_task" {
145145
{ name = "RPC_PROXY_EXCHANGES_BINANCE_TOKEN", value = var.binance_token },
146146
{ name = "RPC_PROXY_EXCHANGES_BINANCE_KEY", value = var.binance_key },
147147
{ name = "RPC_PROXY_EXCHANGES_BINANCE_HOST", value = var.binance_host },
148+
{ name = "RPC_PROXY_EXCHANGES_ALLOWED_PROJECT_IDS", value = var.pay_allowed_project_ids },
148149

149150

150151
],

terraform/ecs/variables.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,10 @@ variable "binance_host" {
485485
sensitive = true
486486
default = ""
487487
}
488+
489+
variable "pay_allowed_project_ids" {
490+
description = "Allowed project ids for pay with exchange"
491+
type = string
492+
sensitive = false
493+
default = ""
494+
}

terraform/res_ecs.tf

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,14 @@ module "ecs" {
121121
testing_project_id = var.testing_project_id
122122

123123
# Exchanges
124-
coinbase_project_id = var.coinbase_project_id
125-
coinbase_key_name = var.coinbase_key_name
126-
coinbase_key_secret = var.coinbase_key_secret
127-
binance_client_id = var.binance_client_id
128-
binance_token = var.binance_token
129-
binance_key = var.binance_key
130-
binance_host = var.binance_host
124+
coinbase_project_id = var.coinbase_project_id
125+
coinbase_key_name = var.coinbase_key_name
126+
coinbase_key_secret = var.coinbase_key_secret
127+
binance_client_id = var.binance_client_id
128+
binance_token = var.binance_token
129+
binance_key = var.binance_key
130+
binance_host = var.binance_host
131+
pay_allowed_project_ids = var.pay_allowed_project_ids
131132

132133
depends_on = [aws_iam_role.application_role]
133134
}

terraform/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,9 @@ variable "binance_host" {
337337
default = ""
338338
}
339339

340+
variable "pay_allowed_project_ids" {
341+
description = "Allowed project ids for pay with exchange"
342+
type = string
343+
sensitive = false
344+
default = ""
345+
}

0 commit comments

Comments
 (0)