-
Notifications
You must be signed in to change notification settings - Fork 245
Expand file tree
/
Copy pathestimate_transaction_fee.rs
More file actions
164 lines (133 loc) · 5.22 KB
/
estimate_transaction_fee.rs
File metadata and controls
164 lines (133 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use solana_signers::SolanaSigner;
use std::sync::Arc;
use utoipa::ToSchema;
use crate::{
error::KoraError,
fee::fee::FeeConfigUtil,
rpc_server::middleware_utils::default_sig_verify,
state::get_request_signer_with_signer_key,
transaction::{TransactionUtil, VersionedTransactionResolved},
};
use serde::{Deserialize, Serialize};
use solana_client::nonblocking::rpc_client::RpcClient;
#[cfg(not(test))]
use crate::state::get_config;
#[cfg(test)]
use crate::tests::config_mock::mock_state::get_config;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct EstimateTransactionFeeRequest {
pub transaction: String, // Base64 encoded serialized transaction
#[serde(default)]
pub fee_token: Option<String>,
/// Optional signer signer_key to ensure consistency across related RPC calls
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signer_key: Option<String>,
/// Whether to verify signatures during simulation (defaults to true)
#[serde(default = "default_sig_verify")]
pub sig_verify: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct EstimateTransactionFeeResponse {
pub fee_in_lamports: u64,
pub fee_in_token: Option<f64>,
/// Public key of the signer used for fee estimation (for client consistency)
pub signer_pubkey: String,
/// Public key of the payment destination
pub payment_address: String,
}
pub async fn estimate_transaction_fee(
rpc_client: &Arc<RpcClient>,
request: EstimateTransactionFeeRequest,
) -> Result<EstimateTransactionFeeResponse, KoraError> {
let transaction = TransactionUtil::decode_b64_transaction(&request.transaction)?;
let signer = get_request_signer_with_signer_key(request.signer_key.as_deref())?;
let config = get_config()?;
let payment_destination = config.kora.get_payment_address(&signer.pubkey())?;
let validation_config = &config.validation;
let fee_payer = signer.pubkey();
let mut resolved_transaction = VersionedTransactionResolved::from_transaction(
&transaction,
rpc_client,
request.sig_verify,
)
.await?;
let fee_calculation = FeeConfigUtil::estimate_kora_fee(
rpc_client,
&mut resolved_transaction,
&fee_payer,
validation_config.is_payment_required(),
validation_config.price_source.clone(),
)
.await?;
let fee_in_lamports = fee_calculation.total_fee_lamports;
// Calculate fee in token if requested
let fee_in_token = FeeConfigUtil::calculate_fee_in_token(
rpc_client,
fee_in_lamports,
request.fee_token.as_deref(),
)
.await?;
Ok(EstimateTransactionFeeResponse {
fee_in_lamports,
fee_in_token,
signer_pubkey: fee_payer.to_string(),
payment_address: payment_destination.to_string(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{
common::{setup_or_get_test_config, setup_or_get_test_signer, RpcMockBuilder},
transaction_mock::create_mock_encoded_transaction,
};
#[tokio::test]
async fn test_estimate_transaction_fee_decode_error() {
let _ = setup_or_get_test_config();
let _ = setup_or_get_test_signer();
let rpc_client = Arc::new(RpcMockBuilder::new().build());
let request = EstimateTransactionFeeRequest {
transaction: "invalid_base64!@#$".to_string(),
fee_token: None,
signer_key: None,
sig_verify: true,
};
let result = estimate_transaction_fee(&rpc_client, request).await;
assert!(result.is_err(), "Should fail with decode error");
}
#[tokio::test]
async fn test_estimate_transaction_fee_invalid_signer_key() {
let _ = setup_or_get_test_config();
let _ = setup_or_get_test_signer();
let rpc_client = Arc::new(RpcMockBuilder::new().build());
let request = EstimateTransactionFeeRequest {
transaction: create_mock_encoded_transaction(),
fee_token: None,
signer_key: Some("invalid_pubkey".to_string()),
sig_verify: true,
};
let result = estimate_transaction_fee(&rpc_client, request).await;
assert!(result.is_err(), "Should fail with invalid signer key");
let error = result.unwrap_err();
assert!(matches!(error, KoraError::ValidationError(_)), "Should return ValidationError");
}
#[tokio::test]
async fn test_estimate_transaction_fee_invalid_token_mint() {
let _ = setup_or_get_test_config();
let _ = setup_or_get_test_signer();
let rpc_client = Arc::new(RpcMockBuilder::new().build());
let request = EstimateTransactionFeeRequest {
transaction: create_mock_encoded_transaction(),
fee_token: Some("invalid_mint_address".to_string()),
signer_key: None,
sig_verify: true,
};
let result = estimate_transaction_fee(&rpc_client, request).await;
assert!(result.is_err(), "Should fail with invalid token mint");
let error = result.unwrap_err();
assert!(
matches!(error, KoraError::InvalidTransaction(_)),
"Should return InvalidTransaction error due to invalid mint parsing"
);
}
}