cow_sdk_core/config/
protocol.rs1use serde::{Deserialize, Serialize};
2
3use crate::{
4 errors::{CoreError, ValidationError},
5 redaction::Redacted,
6 types::ChainId,
7};
8
9use super::{
10 chains::SupportedChainId,
11 env::{AddressPerChain, ApiBaseUrls, CowEnv, default_api_base_urls},
12 http::validate_header_value,
13};
14
15#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct ProtocolOptions {
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub env: Option<CowEnv>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub settlement_contract_override: Option<AddressPerChain>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub eth_flow_contract_override: Option<AddressPerChain>,
28}
29
30impl ProtocolOptions {
31 #[must_use]
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 #[must_use]
43 pub const fn with_env(mut self, env: CowEnv) -> Self {
44 self.env = Some(env);
45 self
46 }
47
48 #[must_use]
50 pub fn with_settlement_contract_override(mut self, overrides: AddressPerChain) -> Self {
51 self.settlement_contract_override = Some(overrides);
52 self
53 }
54
55 #[must_use]
57 pub fn with_eth_flow_contract_override(mut self, overrides: AddressPerChain) -> Self {
58 self.eth_flow_contract_override = Some(overrides);
59 self
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct ApiContext {
67 pub chain_id: SupportedChainId,
69 pub env: CowEnv,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub base_urls: Option<ApiBaseUrls>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub api_key: Option<Redacted<String>>,
77}
78
79impl Default for ApiContext {
80 fn default() -> Self {
81 Self {
82 chain_id: SupportedChainId::Mainnet,
83 env: CowEnv::Prod,
84 base_urls: None,
85 api_key: None,
86 }
87 }
88}
89
90impl ApiContext {
91 #[must_use]
97 pub const fn new(chain_id: SupportedChainId, env: CowEnv) -> Self {
98 Self {
99 chain_id,
100 env,
101 base_urls: None,
102 api_key: None,
103 }
104 }
105
106 #[must_use]
108 pub fn with_base_urls(mut self, base_urls: impl Into<ApiBaseUrls>) -> Self {
109 self.base_urls = Some(base_urls.into());
110 self
111 }
112
113 #[must_use]
115 pub fn with_api_key(mut self, api_key: Redacted<String>) -> Self {
116 self.api_key = Some(api_key);
117 self
118 }
119
120 pub fn validated_api_key(&self) -> Result<Option<&str>, ValidationError> {
127 self.api_key
128 .as_ref()
129 .map(|api_key| {
130 let value = api_key.as_inner().as_str();
131 validate_header_value(value, "api_key")?;
132 Ok(value)
133 })
134 .transpose()
135 }
136
137 pub fn resolved_base_url(&self) -> Result<String, CoreError> {
146 let chain_id: ChainId = self.chain_id.into();
147 let partner_api = self.validated_api_key()?.is_some();
148 let resolved = self.base_urls.as_ref().map_or_else(
151 || {
152 default_api_base_urls(self.env, partner_api)
153 .as_inner()
154 .get(&chain_id)
155 .cloned()
156 },
157 |base_urls| base_urls.as_inner().get(&chain_id).cloned(),
158 );
159
160 resolved.ok_or(CoreError::MissingBaseUrl {
161 chain_id,
162 env: self.env,
163 partner_api,
164 })
165 }
166}