0.7.25
This commit is contained in:
@@ -31,3 +31,5 @@ tracing-subscriber.workspace = true
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct KbConfig {
|
||||
impl KbConfig {
|
||||
/// Returns the default path of the JSON configuration file.
|
||||
pub fn default_path() -> std::path::PathBuf {
|
||||
kb_workspace_root_dir().join("config.json")
|
||||
return kb_workspace_root_dir().join("config.json");
|
||||
}
|
||||
|
||||
/// Loads a configuration from a JSON file and validates it.
|
||||
@@ -34,7 +34,7 @@ impl KbConfig {
|
||||
"cannot read configuration file '{}': {error}",
|
||||
path_ref.display()
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let config_result = serde_json::from_str::<Self>(&content);
|
||||
let config = match config_result {
|
||||
@@ -44,36 +44,28 @@ impl KbConfig {
|
||||
"cannot parse configuration file '{}': {error}",
|
||||
path_ref.display()
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let validation_result = config.validate();
|
||||
match validation_result {
|
||||
Ok(()) => Ok(config),
|
||||
Err(error) => Err(error),
|
||||
Ok(()) => return Ok(config),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the current configuration.
|
||||
pub fn validate(&self) -> Result<(), crate::KbError> {
|
||||
if self.app.name.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"app.name must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("app.name must not be empty".to_string()));
|
||||
}
|
||||
if self.app.environment.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"app.environment must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("app.environment must not be empty".to_string()));
|
||||
}
|
||||
if self.logging.level.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"logging.level must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("logging.level must not be empty".to_string()));
|
||||
}
|
||||
if self.logging.directory.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"logging.directory must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("logging.directory must not be empty".to_string()));
|
||||
}
|
||||
if self.logging.file_prefix.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
@@ -81,9 +73,7 @@ impl KbConfig {
|
||||
));
|
||||
}
|
||||
if self.data.sqlite_path.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"data.sqlite_path must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("data.sqlite_path must not be empty".to_string()));
|
||||
}
|
||||
if self.data.wallets_directory.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
@@ -131,7 +121,7 @@ impl KbConfig {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/// Creates the basic runtime directories required by the current configuration.
|
||||
@@ -165,7 +155,7 @@ impl KbConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/// Finds one HTTP endpoint by its logical name.
|
||||
@@ -173,10 +163,11 @@ impl KbConfig {
|
||||
&self,
|
||||
endpoint_name: &str,
|
||||
) -> std::option::Option<&KbHttpEndpointConfig> {
|
||||
self.solana
|
||||
return self
|
||||
.solana
|
||||
.http_endpoints
|
||||
.iter()
|
||||
.find(|endpoint| endpoint.name == endpoint_name)
|
||||
.find(|endpoint| return endpoint.name == endpoint_name);
|
||||
}
|
||||
|
||||
/// Returns a named WebSocket endpoint by reference.
|
||||
@@ -184,10 +175,11 @@ impl KbConfig {
|
||||
&self,
|
||||
endpoint_name: &str,
|
||||
) -> std::option::Option<&KbWsEndpointConfig> {
|
||||
self.solana
|
||||
return self
|
||||
.solana
|
||||
.ws_endpoints
|
||||
.iter()
|
||||
.find(|endpoint| endpoint.name == endpoint_name)
|
||||
.find(|endpoint| return endpoint.name == endpoint_name);
|
||||
}
|
||||
|
||||
fn validate_http_endpoint(
|
||||
@@ -196,11 +188,9 @@ impl KbConfig {
|
||||
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
||||
) -> Result<(), crate::KbError> {
|
||||
if endpoint.name.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"http endpoint name must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("http endpoint name must not be empty".to_string()));
|
||||
}
|
||||
if endpoint_names.iter().any(|name| name == &endpoint.name) {
|
||||
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
||||
return Err(crate::KbError::Config(format!(
|
||||
"duplicated endpoint name '{}'",
|
||||
endpoint.name
|
||||
@@ -237,7 +227,7 @@ impl KbConfig {
|
||||
)));
|
||||
}
|
||||
endpoint_names.push(endpoint.name.clone());
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn validate_ws_endpoint(
|
||||
@@ -246,11 +236,9 @@ impl KbConfig {
|
||||
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
||||
) -> Result<(), crate::KbError> {
|
||||
if endpoint.name.trim().is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"ws endpoint name must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("ws endpoint name must not be empty".to_string()));
|
||||
}
|
||||
if endpoint_names.iter().any(|name| name == &endpoint.name) {
|
||||
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
||||
return Err(crate::KbError::Config(format!(
|
||||
"duplicated endpoint name '{}'",
|
||||
endpoint.name
|
||||
@@ -299,7 +287,7 @@ impl KbConfig {
|
||||
)));
|
||||
}
|
||||
endpoint_names.push(endpoint.name.clone());
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +338,7 @@ pub struct KbLoggingConfig {
|
||||
impl KbLoggingConfig {
|
||||
/// Returns the resolved logging directory path.
|
||||
pub fn directory_path(&self) -> std::path::PathBuf {
|
||||
kb_resolve_workspace_relative_path(&self.directory)
|
||||
return kb_resolve_workspace_relative_path(&self.directory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,12 +354,12 @@ pub struct KbDataConfig {
|
||||
impl KbDataConfig {
|
||||
/// Returns the resolved SQLite database path.
|
||||
pub fn sqlite_path_buf(&self) -> std::path::PathBuf {
|
||||
kb_resolve_workspace_relative_path(&self.sqlite_path)
|
||||
return kb_resolve_workspace_relative_path(&self.sqlite_path);
|
||||
}
|
||||
|
||||
/// Returns the resolved wallets directory path.
|
||||
pub fn wallets_directory_path(&self) -> std::path::PathBuf {
|
||||
kb_resolve_workspace_relative_path(&self.wallets_directory)
|
||||
return kb_resolve_workspace_relative_path(&self.wallets_directory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +421,7 @@ impl KbHttpEndpointConfig {
|
||||
Some(env_var_name) => env_var_name,
|
||||
None => {
|
||||
return Ok(self.url.clone());
|
||||
}
|
||||
},
|
||||
};
|
||||
let api_key_result = std::env::var(env_var_name);
|
||||
let api_key = match api_key_result {
|
||||
@@ -443,13 +431,13 @@ impl KbHttpEndpointConfig {
|
||||
"cannot resolve api key env var '{}' for http endpoint '{}': {}",
|
||||
env_var_name, self.name, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let placeholder = format!("${{{}}}", env_var_name);
|
||||
if self.url.contains(&placeholder) {
|
||||
return Ok(self.url.replace(&placeholder, &api_key));
|
||||
}
|
||||
Ok(self.url.clone())
|
||||
return Ok(self.url.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +475,7 @@ pub struct KbWsEndpointConfig {
|
||||
impl KbWsEndpointConfig {
|
||||
/// Returns the resolved endpoint URL.
|
||||
pub fn resolved_url(&self) -> Result<std::string::String, crate::KbError> {
|
||||
kb_resolve_endpoint_url(&self.url, &self.api_key_env_var)
|
||||
return kb_resolve_endpoint_url(&self.url, &self.api_key_env_var);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,7 +500,7 @@ pub struct KbSqliteDatabaseConfig {
|
||||
impl KbSqliteDatabaseConfig {
|
||||
/// Returns the resolved SQLite database path.
|
||||
pub fn path_buf(&self) -> std::path::PathBuf {
|
||||
kb_resolve_workspace_relative_path(&self.path)
|
||||
return kb_resolve_workspace_relative_path(&self.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,8 +519,8 @@ pub struct KbDatabaseConfig {
|
||||
fn kb_workspace_root_dir() -> std::path::PathBuf {
|
||||
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
match manifest_dir.parent() {
|
||||
Some(parent) => parent.to_path_buf(),
|
||||
None => manifest_dir,
|
||||
Some(parent) => return parent.to_path_buf(),
|
||||
None => return manifest_dir,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +529,7 @@ fn kb_resolve_workspace_relative_path<P: AsRef<std::path::Path>>(path: P) -> std
|
||||
if input_path.is_absolute() {
|
||||
return input_path;
|
||||
}
|
||||
kb_workspace_root_dir().join(input_path)
|
||||
return kb_workspace_root_dir().join(input_path);
|
||||
}
|
||||
|
||||
fn kb_resolve_endpoint_url(
|
||||
@@ -553,7 +541,7 @@ fn kb_resolve_endpoint_url(
|
||||
Some(env_var_name) => env_var_name,
|
||||
None => {
|
||||
return Ok(url.to_string());
|
||||
}
|
||||
},
|
||||
};
|
||||
let placeholder = format!("${{{env_var_name}}}");
|
||||
if !url.contains(&placeholder) {
|
||||
@@ -567,7 +555,7 @@ fn kb_resolve_endpoint_url(
|
||||
"environment variable '{}' is required to resolve endpoint url '{}': {error}",
|
||||
env_var_name, url
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(url.replace(&placeholder, &env_value))
|
||||
return Ok(url.replace(&placeholder, &env_value));
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ pub use queries::get_dex_by_code;
|
||||
pub use queries::get_dex_decoded_event_by_key;
|
||||
pub use queries::get_known_http_endpoint;
|
||||
pub use queries::get_known_ws_endpoint;
|
||||
pub use queries::get_latest_pump_fun_create_payload_by_mint;
|
||||
pub use queries::get_launch_attribution_by_decoded_event_id;
|
||||
pub use queries::get_launch_surface_by_code;
|
||||
pub use queries::get_launch_surface_key_by_match;
|
||||
@@ -98,6 +99,7 @@ pub use queries::get_pair_metric_by_pair_id;
|
||||
pub use queries::get_pool_by_address;
|
||||
pub use queries::get_pool_listing_by_pool_id;
|
||||
pub use queries::get_pool_origin_by_pool_id;
|
||||
pub use queries::get_token_by_id;
|
||||
pub use queries::get_token_by_mint;
|
||||
pub use queries::get_trade_event_by_decoded_event_id;
|
||||
pub use queries::get_wallet_by_address;
|
||||
@@ -108,6 +110,7 @@ pub use queries::insert_chain_instruction;
|
||||
pub use queries::insert_db_runtime_event;
|
||||
pub use queries::insert_onchain_observation;
|
||||
pub use queries::list_chain_instructions_by_transaction_id;
|
||||
pub use queries::list_chain_transaction_signatures_for_replay;
|
||||
pub use queries::list_db_metadata;
|
||||
pub use queries::list_dex_decoded_events_by_transaction_id;
|
||||
pub use queries::list_dexes;
|
||||
@@ -135,12 +138,14 @@ pub use queries::list_recent_swaps;
|
||||
pub use queries::list_recent_token_burn_events;
|
||||
pub use queries::list_recent_token_mint_events;
|
||||
pub use queries::list_tokens;
|
||||
pub use queries::list_tokens_missing_metadata;
|
||||
pub use queries::list_trade_events_by_pair_id;
|
||||
pub use queries::list_trade_events_by_transaction_id;
|
||||
pub use queries::list_wallet_holdings_by_wallet_id;
|
||||
pub use queries::list_wallet_participations_by_pool_id;
|
||||
pub use queries::list_wallet_participations_by_wallet_id;
|
||||
pub use queries::list_wallets;
|
||||
pub use queries::update_pair_symbol;
|
||||
pub use queries::upsert_chain_slot;
|
||||
pub use queries::upsert_chain_transaction;
|
||||
pub use queries::upsert_db_metadata;
|
||||
|
||||
@@ -19,9 +19,7 @@ pub struct KbDatabase {
|
||||
|
||||
impl KbDatabase {
|
||||
/// Opens a database connection without initializing the schema.
|
||||
pub async fn connect(
|
||||
config: &crate::KbDatabaseConfig,
|
||||
) -> Result<Self, crate::KbError> {
|
||||
pub async fn connect(config: &crate::KbDatabaseConfig) -> Result<Self, crate::KbError> {
|
||||
if !config.enabled {
|
||||
return Err(crate::KbError::Config(
|
||||
"database is disabled in configuration".to_string(),
|
||||
@@ -40,11 +38,11 @@ impl KbDatabase {
|
||||
Ok(pool) => pool,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
backend: crate::KbDatabaseBackend::Sqlite,
|
||||
database_url,
|
||||
connection: KbDatabaseConnection::Sqlite(pool),
|
||||
})
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -64,47 +62,40 @@ impl KbDatabase {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
Ok(database)
|
||||
return Ok(database);
|
||||
}
|
||||
|
||||
/// Returns the configured backend.
|
||||
pub fn backend(
|
||||
&self,
|
||||
) -> crate::KbDatabaseBackend {
|
||||
self.backend
|
||||
pub fn backend(&self) -> crate::KbDatabaseBackend {
|
||||
return self.backend;
|
||||
}
|
||||
|
||||
/// Returns a displayable database URL-like string.
|
||||
pub fn database_url(
|
||||
&self,
|
||||
) -> &str {
|
||||
&self.database_url
|
||||
pub fn database_url(&self) -> &str {
|
||||
return &self.database_url;
|
||||
}
|
||||
|
||||
/// Pings the database.
|
||||
pub async fn ping(
|
||||
&self,
|
||||
) -> Result<(), crate::KbError> {
|
||||
pub async fn ping(&self) -> Result<(), crate::KbError> {
|
||||
match &self.connection {
|
||||
KbDatabaseConnection::Sqlite(pool) => {
|
||||
let ping_result = sqlx::query("SELECT 1").execute(pool).await;
|
||||
match ping_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot ping sqlite database '{}': {}",
|
||||
self.database_url,
|
||||
error
|
||||
))),
|
||||
Ok(_) => return Ok(()),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot ping sqlite database '{}': {}",
|
||||
self.database_url, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying connection enum.
|
||||
pub(crate) fn connection(
|
||||
&self,
|
||||
) -> &KbDatabaseConnection {
|
||||
&self.connection
|
||||
pub(crate) fn connection(&self) -> &KbDatabaseConnection {
|
||||
return &self.connection;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ impl KbAnalysisSignalDto {
|
||||
score: std::option::Option<f64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
signal_kind,
|
||||
severity,
|
||||
@@ -42,7 +42,7 @@ impl KbAnalysisSignalDto {
|
||||
score,
|
||||
payload,
|
||||
created_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl TryFrom<crate::KbAnalysisSignalEntity> for KbAnalysisSignalDto {
|
||||
"cannot parse analysis signal created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let payload_result = serde_json::from_str::<serde_json::Value>(&entity.payload_json);
|
||||
let payload = match payload_result {
|
||||
@@ -73,9 +73,9 @@ impl TryFrom<crate::KbAnalysisSignalEntity> for KbAnalysisSignalDto {
|
||||
"cannot parse analysis signal payload_json '{}': {}",
|
||||
entity.payload_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
signal_kind: entity.signal_kind,
|
||||
severity,
|
||||
@@ -84,6 +84,6 @@ impl TryFrom<crate::KbAnalysisSignalEntity> for KbAnalysisSignalDto {
|
||||
score: entity.score,
|
||||
payload,
|
||||
created_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl KbChainInstructionDto {
|
||||
parsed_type: std::option::Option<std::string::String>,
|
||||
parsed_json: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
transaction_id,
|
||||
parent_instruction_id,
|
||||
@@ -63,7 +63,7 @@ impl KbChainInstructionDto {
|
||||
parsed_type,
|
||||
parsed_json,
|
||||
created_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ impl TryFrom<crate::KbChainInstructionEntity> for KbChainInstructionDto {
|
||||
"cannot convert chain instruction instruction_index '{}' to u32: {}",
|
||||
entity.instruction_index, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let inner_instruction_index = match entity.inner_instruction_index {
|
||||
Some(inner_instruction_index) => {
|
||||
@@ -91,9 +91,9 @@ impl TryFrom<crate::KbChainInstructionEntity> for KbChainInstructionDto {
|
||||
"cannot convert chain instruction inner_instruction_index '{}' to u32: {}",
|
||||
inner_instruction_index, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let stack_height = match entity.stack_height {
|
||||
@@ -106,9 +106,9 @@ impl TryFrom<crate::KbChainInstructionEntity> for KbChainInstructionDto {
|
||||
"cannot convert chain instruction stack_height '{}' to u32: {}",
|
||||
stack_height, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
|
||||
@@ -119,9 +119,9 @@ impl TryFrom<crate::KbChainInstructionEntity> for KbChainInstructionDto {
|
||||
"cannot parse chain instruction created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
transaction_id: entity.transaction_id,
|
||||
parent_instruction_id: entity.parent_instruction_id,
|
||||
@@ -135,6 +135,6 @@ impl TryFrom<crate::KbChainInstructionEntity> for KbChainInstructionDto {
|
||||
parsed_type: entity.parsed_type,
|
||||
parsed_json: entity.parsed_json,
|
||||
created_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,13 @@ impl KbChainSlotDto {
|
||||
block_time_unix: std::option::Option<i64>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
slot,
|
||||
parent_slot,
|
||||
block_time_unix,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ impl TryFrom<crate::KbChainSlotEntity> for KbChainSlotDto {
|
||||
"cannot convert chain slot '{}' to u64: {}",
|
||||
entity.slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let parent_slot = match entity.parent_slot {
|
||||
Some(parent_slot) => {
|
||||
@@ -59,9 +59,9 @@ impl TryFrom<crate::KbChainSlotEntity> for KbChainSlotDto {
|
||||
"cannot convert chain parent_slot '{}' to u64: {}",
|
||||
parent_slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
|
||||
@@ -72,7 +72,7 @@ impl TryFrom<crate::KbChainSlotEntity> for KbChainSlotDto {
|
||||
"cannot parse chain slot created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -82,14 +82,14 @@ impl TryFrom<crate::KbChainSlotEntity> for KbChainSlotDto {
|
||||
"cannot parse chain slot updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
slot,
|
||||
parent_slot,
|
||||
block_time_unix: entity.block_time_unix,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl KbChainTransactionDto {
|
||||
transaction_json: std::string::String,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
signature,
|
||||
slot,
|
||||
@@ -55,7 +55,7 @@ impl KbChainTransactionDto {
|
||||
transaction_json,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ impl TryFrom<crate::KbChainTransactionEntity> for KbChainTransactionDto {
|
||||
"cannot convert chain transaction slot '{}' to u64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
|
||||
@@ -86,7 +86,7 @@ impl TryFrom<crate::KbChainTransactionEntity> for KbChainTransactionDto {
|
||||
"cannot parse chain transaction created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -96,9 +96,9 @@ impl TryFrom<crate::KbChainTransactionEntity> for KbChainTransactionDto {
|
||||
"cannot parse chain transaction updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
signature: entity.signature,
|
||||
slot,
|
||||
@@ -110,6 +110,6 @@ impl TryFrom<crate::KbChainTransactionEntity> for KbChainTransactionDto {
|
||||
transaction_json: entity.transaction_json,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ pub struct KbDbMetadataDto {
|
||||
impl KbDbMetadataDto {
|
||||
/// Creates a new metadata DTO with the current UTC timestamp.
|
||||
pub fn new(key: std::string::String, value: std::string::String) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
key,
|
||||
value,
|
||||
updated_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,22 +36,22 @@ impl TryFrom<crate::KbDbMetadataEntity> for KbDbMetadataDto {
|
||||
"cannot parse db metadata timestamp '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
key: entity.key,
|
||||
value: entity.value,
|
||||
updated_at: parsed.with_timezone(&chrono::Utc),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KbDbMetadataDto> for crate::KbDbMetadataEntity {
|
||||
fn from(dto: KbDbMetadataDto) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
key: dto.key,
|
||||
value: dto.value,
|
||||
updated_at: dto.updated_at.to_rfc3339(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ impl KbDbRuntimeEventDto {
|
||||
source: std::string::String,
|
||||
message: std::string::String,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
event_kind,
|
||||
level,
|
||||
source,
|
||||
message,
|
||||
created_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,20 +50,20 @@ impl TryFrom<crate::KbDbRuntimeEventEntity> for KbDbRuntimeEventDto {
|
||||
"cannot parse runtime event created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let level_result = crate::KbDbRuntimeEventLevel::from_i16(entity.level);
|
||||
let level = match level_result {
|
||||
Ok(level) => level,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
event_kind: entity.event_kind,
|
||||
level,
|
||||
source: entity.source,
|
||||
message: entity.message,
|
||||
created_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl KbDexDto {
|
||||
is_enabled: bool,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
code,
|
||||
name,
|
||||
@@ -42,7 +42,7 @@ impl KbDexDto {
|
||||
is_enabled,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ impl TryFrom<crate::KbDexEntity> for KbDexDto {
|
||||
"cannot parse dex created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -68,9 +68,9 @@ impl TryFrom<crate::KbDexEntity> for KbDexDto {
|
||||
"cannot parse dex updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
code: entity.code,
|
||||
name: entity.name,
|
||||
@@ -79,6 +79,6 @@ impl TryFrom<crate::KbDexEntity> for KbDexDto {
|
||||
is_enabled: entity.is_enabled != 0,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl KbDexDecodedEventDto {
|
||||
market_account: std::option::Option<std::string::String>,
|
||||
payload_json: std::string::String,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
@@ -63,7 +63,7 @@ impl KbDexDecodedEventDto {
|
||||
market_account,
|
||||
payload_json,
|
||||
created_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ impl TryFrom<crate::KbDexDecodedEventEntity> for KbDexDecodedEventDto {
|
||||
"cannot parse dex decoded event created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
transaction_id: entity.transaction_id,
|
||||
instruction_id: entity.instruction_id,
|
||||
@@ -95,6 +95,6 @@ impl TryFrom<crate::KbDexDecodedEventEntity> for KbDexDecodedEventDto {
|
||||
market_account: entity.market_account,
|
||||
payload_json: entity.payload_json,
|
||||
created_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl KbKnownHttpEndpointDto {
|
||||
enabled: bool,
|
||||
roles: std::vec::Vec<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
name,
|
||||
provider,
|
||||
url,
|
||||
@@ -38,7 +38,7 @@ impl KbKnownHttpEndpointDto {
|
||||
roles,
|
||||
last_seen_at: None,
|
||||
updated_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ impl TryFrom<crate::KbKnownHttpEndpointEntity> for KbKnownHttpEndpointDto {
|
||||
"cannot parse known http endpoint roles_json '{}': {}",
|
||||
entity.roles_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -65,7 +65,7 @@ impl TryFrom<crate::KbKnownHttpEndpointEntity> for KbKnownHttpEndpointDto {
|
||||
"cannot parse known http endpoint updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let last_seen_at = match entity.last_seen_at {
|
||||
Some(last_seen_at_text) => {
|
||||
@@ -77,12 +77,12 @@ impl TryFrom<crate::KbKnownHttpEndpointEntity> for KbKnownHttpEndpointDto {
|
||||
"cannot parse known http endpoint last_seen_at '{}': {}",
|
||||
last_seen_at_text, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
name: entity.name,
|
||||
provider: entity.provider,
|
||||
url: entity.url,
|
||||
@@ -90,7 +90,7 @@ impl TryFrom<crate::KbKnownHttpEndpointEntity> for KbKnownHttpEndpointDto {
|
||||
roles,
|
||||
last_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,16 +106,16 @@ impl TryFrom<KbKnownHttpEndpointDto> for crate::KbKnownHttpEndpointEntity {
|
||||
"cannot serialize known http endpoint roles: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
name: dto.name,
|
||||
provider: dto.provider,
|
||||
url: dto.url,
|
||||
enabled: if dto.enabled { 1 } else { 0 },
|
||||
roles_json,
|
||||
last_seen_at: dto.last_seen_at.map(|value| value.to_rfc3339()),
|
||||
last_seen_at: dto.last_seen_at.map(|value| return value.to_rfc3339()),
|
||||
updated_at: dto.updated_at.to_rfc3339(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl KbKnownWsEndpointDto {
|
||||
enabled: bool,
|
||||
roles: std::vec::Vec<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
name,
|
||||
provider,
|
||||
url,
|
||||
@@ -38,7 +38,7 @@ impl KbKnownWsEndpointDto {
|
||||
roles,
|
||||
last_seen_at: None,
|
||||
updated_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ impl TryFrom<crate::KbKnownWsEndpointEntity> for KbKnownWsEndpointDto {
|
||||
"cannot parse known ws endpoint roles_json '{}': {}",
|
||||
entity.roles_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -65,7 +65,7 @@ impl TryFrom<crate::KbKnownWsEndpointEntity> for KbKnownWsEndpointDto {
|
||||
"cannot parse known ws endpoint updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let last_seen_at = match entity.last_seen_at {
|
||||
Some(last_seen_at_text) => {
|
||||
@@ -77,12 +77,12 @@ impl TryFrom<crate::KbKnownWsEndpointEntity> for KbKnownWsEndpointDto {
|
||||
"cannot parse known ws endpoint last_seen_at '{}': {}",
|
||||
last_seen_at_text, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
name: entity.name,
|
||||
provider: entity.provider,
|
||||
url: entity.url,
|
||||
@@ -90,7 +90,7 @@ impl TryFrom<crate::KbKnownWsEndpointEntity> for KbKnownWsEndpointDto {
|
||||
roles,
|
||||
last_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,16 +106,16 @@ impl TryFrom<KbKnownWsEndpointDto> for crate::KbKnownWsEndpointEntity {
|
||||
"cannot serialize known ws endpoint roles: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
name: dto.name,
|
||||
provider: dto.provider,
|
||||
url: dto.url,
|
||||
enabled: if dto.enabled { 1 } else { 0 },
|
||||
roles_json,
|
||||
last_seen_at: dto.last_seen_at.map(|value| value.to_rfc3339()),
|
||||
last_seen_at: dto.last_seen_at.map(|value| return value.to_rfc3339()),
|
||||
updated_at: dto.updated_at.to_rfc3339(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl KbLaunchAttributionDto {
|
||||
matched_value: std::string::String,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
launch_surface_id,
|
||||
transaction_id,
|
||||
@@ -58,7 +58,7 @@ impl KbLaunchAttributionDto {
|
||||
matched_value,
|
||||
attributed_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl TryFrom<crate::KbLaunchAttributionEntity> for KbLaunchAttributionDto {
|
||||
"cannot parse launch_attribution attributed_at '{}': {}",
|
||||
entity.attributed_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -84,9 +84,9 @@ impl TryFrom<crate::KbLaunchAttributionEntity> for KbLaunchAttributionDto {
|
||||
"cannot parse launch_attribution updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
launch_surface_id: entity.launch_surface_id,
|
||||
transaction_id: entity.transaction_id,
|
||||
@@ -99,6 +99,6 @@ impl TryFrom<crate::KbLaunchAttributionEntity> for KbLaunchAttributionDto {
|
||||
matched_value: entity.matched_value,
|
||||
attributed_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl KbLaunchSurfaceDto {
|
||||
is_enabled: bool,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
code,
|
||||
name,
|
||||
@@ -38,7 +38,7 @@ impl KbLaunchSurfaceDto {
|
||||
is_enabled,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl TryFrom<crate::KbLaunchSurfaceEntity> for KbLaunchSurfaceDto {
|
||||
"cannot parse launch_surface created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -64,9 +64,9 @@ impl TryFrom<crate::KbLaunchSurfaceEntity> for KbLaunchSurfaceDto {
|
||||
"cannot parse launch_surface updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
code: entity.code,
|
||||
name: entity.name,
|
||||
@@ -74,6 +74,6 @@ impl TryFrom<crate::KbLaunchSurfaceEntity> for KbLaunchSurfaceDto {
|
||||
is_enabled: entity.is_enabled != 0,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ impl KbLaunchSurfaceKeyDto {
|
||||
match_value: std::string::String,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
launch_surface_id,
|
||||
match_kind,
|
||||
match_value,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ impl TryFrom<crate::KbLaunchSurfaceKeyEntity> for KbLaunchSurfaceKeyDto {
|
||||
"cannot parse launch_surface_key created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -60,15 +60,15 @@ impl TryFrom<crate::KbLaunchSurfaceKeyEntity> for KbLaunchSurfaceKeyDto {
|
||||
"cannot parse launch_surface_key updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
launch_surface_id: entity.launch_surface_id,
|
||||
match_kind: entity.match_kind,
|
||||
match_value: entity.match_value,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ impl KbLiquidityEventDto {
|
||||
quote_amount: std::string::String,
|
||||
lp_amount: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -74,7 +74,7 @@ impl KbLiquidityEventDto {
|
||||
quote_amount,
|
||||
lp_amount,
|
||||
executed_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ impl TryFrom<crate::KbLiquidityEventEntity> for KbLiquidityEventDto {
|
||||
"cannot parse liquidity event executed_at '{}': {}",
|
||||
entity.executed_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let slot = match entity.slot {
|
||||
Some(slot) => {
|
||||
@@ -107,12 +107,12 @@ impl TryFrom<crate::KbLiquidityEventEntity> for KbLiquidityEventDto {
|
||||
"cannot convert liquidity event slot '{}' to u64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -129,6 +129,6 @@ impl TryFrom<crate::KbLiquidityEventEntity> for KbLiquidityEventDto {
|
||||
quote_amount: entity.quote_amount,
|
||||
lp_amount: entity.lp_amount,
|
||||
executed_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ impl KbObservedTokenDto {
|
||||
status: crate::KbObservedTokenStatus,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
mint,
|
||||
symbol,
|
||||
@@ -49,16 +49,14 @@ impl KbObservedTokenDto {
|
||||
first_seen_at: now,
|
||||
last_seen_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
type Error = crate::KbError;
|
||||
|
||||
fn try_from(
|
||||
entity: crate::KbObservedTokenEntity,
|
||||
) -> Result<Self, Self::Error> {
|
||||
fn try_from(entity: crate::KbObservedTokenEntity) -> Result<Self, Self::Error> {
|
||||
let status_result = crate::KbObservedTokenStatus::from_i16(entity.status);
|
||||
let status = match status_result {
|
||||
Ok(status) => status,
|
||||
@@ -70,8 +68,7 @@ impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot parse observed token first_seen_at '{}': {}",
|
||||
entity.first_seen_at,
|
||||
error
|
||||
entity.first_seen_at, error
|
||||
)));
|
||||
},
|
||||
};
|
||||
@@ -81,8 +78,7 @@ impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot parse observed token last_seen_at '{}': {}",
|
||||
entity.last_seen_at,
|
||||
error
|
||||
entity.last_seen_at, error
|
||||
)));
|
||||
},
|
||||
};
|
||||
@@ -92,8 +88,7 @@ impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot parse observed token updated_at '{}': {}",
|
||||
entity.updated_at,
|
||||
error
|
||||
entity.updated_at, error
|
||||
)));
|
||||
},
|
||||
};
|
||||
@@ -105,15 +100,14 @@ impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot convert observed token decimals '{}' to u8: {}",
|
||||
decimals,
|
||||
error
|
||||
decimals, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
mint: entity.mint,
|
||||
symbol: entity.symbol,
|
||||
@@ -124,6 +118,6 @@ impl TryFrom<crate::KbObservedTokenEntity> for KbObservedTokenDto {
|
||||
first_seen_at,
|
||||
last_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl KbOnchainObservationDto {
|
||||
slot: std::option::Option<u64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
observation_kind,
|
||||
source_kind,
|
||||
@@ -42,7 +42,7 @@ impl KbOnchainObservationDto {
|
||||
slot,
|
||||
payload,
|
||||
observed_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl TryFrom<crate::KbOnchainObservationEntity> for KbOnchainObservationDto {
|
||||
"cannot parse on-chain observation observed_at '{}': {}",
|
||||
entity.observed_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let payload_result = serde_json::from_str::<serde_json::Value>(&entity.payload_json);
|
||||
let payload = match payload_result {
|
||||
@@ -73,7 +73,7 @@ impl TryFrom<crate::KbOnchainObservationEntity> for KbOnchainObservationDto {
|
||||
"cannot parse on-chain observation payload_json '{}': {}",
|
||||
entity.payload_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let slot = match entity.slot {
|
||||
Some(slot) => {
|
||||
@@ -85,12 +85,12 @@ impl TryFrom<crate::KbOnchainObservationEntity> for KbOnchainObservationDto {
|
||||
"cannot convert on-chain observation slot '{}' to u64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
observation_kind: entity.observation_kind,
|
||||
source_kind,
|
||||
@@ -99,6 +99,6 @@ impl TryFrom<crate::KbOnchainObservationEntity> for KbOnchainObservationDto {
|
||||
slot,
|
||||
payload,
|
||||
observed_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl KbPairDto {
|
||||
symbol: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -42,7 +42,7 @@ impl KbPairDto {
|
||||
symbol,
|
||||
first_seen_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ impl TryFrom<crate::KbPairEntity> for KbPairDto {
|
||||
"cannot parse pair first_seen_at '{}': {}",
|
||||
entity.first_seen_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -68,9 +68,9 @@ impl TryFrom<crate::KbPairEntity> for KbPairDto {
|
||||
"cannot parse pair updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -79,6 +79,6 @@ impl TryFrom<crate::KbPairEntity> for KbPairDto {
|
||||
symbol: entity.symbol,
|
||||
first_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl KbPairAnalyticSignalDto {
|
||||
last_transaction_id: std::option::Option<i64>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
pair_id,
|
||||
signal_kind,
|
||||
@@ -58,7 +58,7 @@ impl KbPairAnalyticSignalDto {
|
||||
last_transaction_id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl TryFrom<crate::KbPairAnalyticSignalEntity> for KbPairAnalyticSignalDto {
|
||||
"cannot parse pair_analytic_signal signal_value_json '{}': {}",
|
||||
entity.signal_value_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let created_at_result = chrono::DateTime::parse_from_rfc3339(entity.created_at.as_str());
|
||||
let created_at = match created_at_result {
|
||||
@@ -90,7 +90,7 @@ impl TryFrom<crate::KbPairAnalyticSignalEntity> for KbPairAnalyticSignalDto {
|
||||
"cannot parse pair_analytic_signal created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(entity.updated_at.as_str());
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -100,9 +100,9 @@ impl TryFrom<crate::KbPairAnalyticSignalEntity> for KbPairAnalyticSignalDto {
|
||||
"cannot parse pair_analytic_signal updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
pair_id: entity.pair_id,
|
||||
signal_kind: entity.signal_kind,
|
||||
@@ -115,6 +115,6 @@ impl TryFrom<crate::KbPairAnalyticSignalEntity> for KbPairAnalyticSignalDto {
|
||||
last_transaction_id: entity.last_transaction_id,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl KbPairCandleDto {
|
||||
last_trade_signature: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
pair_id,
|
||||
timeframe_seconds,
|
||||
@@ -83,7 +83,7 @@ impl KbPairCandleDto {
|
||||
last_trade_signature,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ impl TryFrom<crate::KbPairCandleEntity> for KbPairCandleDto {
|
||||
"cannot parse pair_candle created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -109,9 +109,9 @@ impl TryFrom<crate::KbPairCandleEntity> for KbPairCandleDto {
|
||||
"cannot parse pair_candle updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
pair_id: entity.pair_id,
|
||||
timeframe_seconds: entity.timeframe_seconds,
|
||||
@@ -130,6 +130,6 @@ impl TryFrom<crate::KbPairCandleEntity> for KbPairCandleDto {
|
||||
last_trade_signature: entity.last_trade_signature,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl KbPairMetricDto {
|
||||
/// Creates a new pair-metric DTO.
|
||||
pub fn new(pair_id: i64) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
pair_id,
|
||||
first_slot: None,
|
||||
@@ -54,7 +54,7 @@ impl KbPairMetricDto {
|
||||
last_price_quote_per_base: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ impl TryFrom<crate::KbPairMetricEntity> for KbPairMetricDto {
|
||||
"cannot parse pair_metric created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -80,9 +80,9 @@ impl TryFrom<crate::KbPairMetricEntity> for KbPairMetricDto {
|
||||
"cannot parse pair_metric updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
pair_id: entity.pair_id,
|
||||
first_slot: entity.first_slot,
|
||||
@@ -97,6 +97,6 @@ impl TryFrom<crate::KbPairMetricEntity> for KbPairMetricDto {
|
||||
last_price_quote_per_base: entity.last_price_quote_per_base,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl KbPoolDto {
|
||||
status: crate::KbPoolStatus,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
address,
|
||||
@@ -38,7 +38,7 @@ impl KbPoolDto {
|
||||
status,
|
||||
first_seen_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl TryFrom<crate::KbPoolEntity> for KbPoolDto {
|
||||
"cannot parse pool first_seen_at '{}': {}",
|
||||
entity.first_seen_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -74,9 +74,9 @@ impl TryFrom<crate::KbPoolEntity> for KbPoolDto {
|
||||
"cannot parse pool updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
address: entity.address,
|
||||
@@ -84,6 +84,6 @@ impl TryFrom<crate::KbPoolEntity> for KbPoolDto {
|
||||
status,
|
||||
first_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl KbPoolListingDto {
|
||||
initial_price_quote: std::option::Option<f64>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -54,7 +54,7 @@ impl KbPoolListingDto {
|
||||
initial_quote_reserve,
|
||||
initial_price_quote,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ impl TryFrom<crate::KbPoolListingEntity> for KbPoolListingDto {
|
||||
"cannot parse pool_listing detected_at '{}': {}",
|
||||
entity.detected_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -85,9 +85,9 @@ impl TryFrom<crate::KbPoolListingEntity> for KbPoolListingDto {
|
||||
"cannot parse pool_listing updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -99,6 +99,6 @@ impl TryFrom<crate::KbPoolListingEntity> for KbPoolListingDto {
|
||||
initial_quote_reserve: entity.initial_quote_reserve,
|
||||
initial_price_quote: entity.initial_price_quote,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl KbPoolOriginDto {
|
||||
launch_attribution_id: std::option::Option<i64>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -70,7 +70,7 @@ impl KbPoolOriginDto {
|
||||
launch_attribution_id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl TryFrom<crate::KbPoolOriginEntity> for KbPoolOriginDto {
|
||||
"cannot parse pool_origin created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -101,9 +101,9 @@ impl TryFrom<crate::KbPoolOriginEntity> for KbPoolOriginDto {
|
||||
"cannot parse pool_origin updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -119,6 +119,6 @@ impl TryFrom<crate::KbPoolOriginEntity> for KbPoolOriginDto {
|
||||
launch_attribution_id: entity.launch_attribution_id,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl KbPoolTokenDto {
|
||||
token_order: std::option::Option<i64>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
pool_id,
|
||||
token_id,
|
||||
@@ -42,7 +42,7 @@ impl KbPoolTokenDto {
|
||||
token_order,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl TryFrom<crate::KbPoolTokenEntity> for KbPoolTokenDto {
|
||||
"cannot parse pool_token created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -73,9 +73,9 @@ impl TryFrom<crate::KbPoolTokenEntity> for KbPoolTokenDto {
|
||||
"cannot parse pool_token updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
pool_id: entity.pool_id,
|
||||
token_id: entity.token_id,
|
||||
@@ -84,6 +84,6 @@ impl TryFrom<crate::KbPoolTokenEntity> for KbPoolTokenDto {
|
||||
token_order: entity.token_order,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl KbSwapDto {
|
||||
price_quote: std::option::Option<std::string::String>,
|
||||
trade_side: crate::KbSwapTradeSide,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -70,7 +70,7 @@ impl KbSwapDto {
|
||||
price_quote,
|
||||
trade_side,
|
||||
executed_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl TryFrom<crate::KbSwapEntity> for KbSwapDto {
|
||||
"cannot parse swap executed_at '{}': {}",
|
||||
entity.executed_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let slot = match entity.slot {
|
||||
Some(slot) => {
|
||||
@@ -103,12 +103,12 @@ impl TryFrom<crate::KbSwapEntity> for KbSwapDto {
|
||||
"cannot convert swap slot '{}' to u64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -124,6 +124,6 @@ impl TryFrom<crate::KbSwapEntity> for KbSwapDto {
|
||||
price_quote: entity.price_quote,
|
||||
trade_side,
|
||||
executed_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ impl KbTokenDto {
|
||||
is_quote_token: bool,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
mint,
|
||||
symbol,
|
||||
@@ -46,7 +46,7 @@ impl KbTokenDto {
|
||||
is_quote_token,
|
||||
first_seen_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ impl TryFrom<crate::KbTokenEntity> for KbTokenDto {
|
||||
"cannot parse token first_seen_at '{}': {}",
|
||||
entity.first_seen_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -72,7 +72,7 @@ impl TryFrom<crate::KbTokenEntity> for KbTokenDto {
|
||||
"cannot parse token updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let decimals = match entity.decimals {
|
||||
Some(decimals) => {
|
||||
@@ -84,12 +84,12 @@ impl TryFrom<crate::KbTokenEntity> for KbTokenDto {
|
||||
"cannot convert token decimals '{}' to u8: {}",
|
||||
decimals, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
mint: entity.mint,
|
||||
symbol: entity.symbol,
|
||||
@@ -99,6 +99,6 @@ impl TryFrom<crate::KbTokenEntity> for KbTokenDto {
|
||||
is_quote_token: entity.is_quote_token != 0,
|
||||
first_seen_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl KbTokenBurnEventDto {
|
||||
amount: std::string::String,
|
||||
supply_after: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
token_id,
|
||||
signature,
|
||||
@@ -50,24 +50,21 @@ impl KbTokenBurnEventDto {
|
||||
amount,
|
||||
supply_after,
|
||||
executed_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<crate::KbTokenBurnEventEntity> for KbTokenBurnEventDto {
|
||||
type Error = crate::KbError;
|
||||
|
||||
fn try_from(
|
||||
entity: crate::KbTokenBurnEventEntity,
|
||||
) -> Result<Self, Self::Error> {
|
||||
fn try_from(entity: crate::KbTokenBurnEventEntity) -> Result<Self, Self::Error> {
|
||||
let executed_at_result = chrono::DateTime::parse_from_rfc3339(&entity.executed_at);
|
||||
let executed_at = match executed_at_result {
|
||||
Ok(executed_at) => executed_at.with_timezone(&chrono::Utc),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot parse token burn event executed_at '{}': {}",
|
||||
entity.executed_at,
|
||||
error
|
||||
entity.executed_at, error
|
||||
)));
|
||||
},
|
||||
};
|
||||
@@ -79,15 +76,14 @@ impl TryFrom<crate::KbTokenBurnEventEntity> for KbTokenBurnEventDto {
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot convert token burn event slot '{}' to u64: {}",
|
||||
slot,
|
||||
error
|
||||
slot, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
token_id: entity.token_id,
|
||||
signature: entity.signature,
|
||||
@@ -98,6 +94,6 @@ impl TryFrom<crate::KbTokenBurnEventEntity> for KbTokenBurnEventDto {
|
||||
amount: entity.amount,
|
||||
supply_after: entity.supply_after,
|
||||
executed_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl KbTokenMintEventDto {
|
||||
amount: std::string::String,
|
||||
supply_after: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
token_id,
|
||||
signature,
|
||||
@@ -50,7 +50,7 @@ impl KbTokenMintEventDto {
|
||||
amount,
|
||||
supply_after,
|
||||
executed_at: chrono::Utc::now(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ impl TryFrom<crate::KbTokenMintEventEntity> for KbTokenMintEventDto {
|
||||
"cannot parse token mint event executed_at '{}': {}",
|
||||
entity.executed_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let slot = match entity.slot {
|
||||
Some(slot) => {
|
||||
@@ -78,12 +78,12 @@ impl TryFrom<crate::KbTokenMintEventEntity> for KbTokenMintEventDto {
|
||||
"cannot convert token mint event slot '{}' to u64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
token_id: entity.token_id,
|
||||
signature: entity.signature,
|
||||
@@ -94,6 +94,6 @@ impl TryFrom<crate::KbTokenMintEventEntity> for KbTokenMintEventDto {
|
||||
amount: entity.amount,
|
||||
supply_after: entity.supply_after,
|
||||
executed_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ impl KbTradeEventDto {
|
||||
payload_json: std::string::String,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
dex_id,
|
||||
pool_id,
|
||||
@@ -86,7 +86,7 @@ impl KbTradeEventDto {
|
||||
payload_json,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ impl TryFrom<crate::KbTradeEventEntity> for KbTradeEventDto {
|
||||
"cannot parse trade_event created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -118,9 +118,9 @@ impl TryFrom<crate::KbTradeEventEntity> for KbTradeEventDto {
|
||||
"cannot parse trade_event updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
dex_id: entity.dex_id,
|
||||
pool_id: entity.pool_id,
|
||||
@@ -140,14 +140,14 @@ impl TryFrom<crate::KbTradeEventEntity> for KbTradeEventDto {
|
||||
payload_json: entity.payload_json,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn kb_trade_side_from_string(value: &str) -> crate::KbSwapTradeSide {
|
||||
match value {
|
||||
"BuyBase" => crate::KbSwapTradeSide::BuyBase,
|
||||
"SellBase" => crate::KbSwapTradeSide::SellBase,
|
||||
_ => crate::KbSwapTradeSide::Unknown,
|
||||
"BuyBase" => return crate::KbSwapTradeSide::BuyBase,
|
||||
"SellBase" => return crate::KbSwapTradeSide::SellBase,
|
||||
_ => return crate::KbSwapTradeSide::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ impl KbWalletDto {
|
||||
label: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
address,
|
||||
label,
|
||||
first_seen_at: now,
|
||||
last_seen_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl TryFrom<crate::KbWalletEntity> for KbWalletDto {
|
||||
"cannot parse wallet first_seen_at '{}': {}",
|
||||
entity.first_seen_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let last_seen_at_result = chrono::DateTime::parse_from_rfc3339(&entity.last_seen_at);
|
||||
let last_seen_at = match last_seen_at_result {
|
||||
@@ -56,14 +56,14 @@ impl TryFrom<crate::KbWalletEntity> for KbWalletDto {
|
||||
"cannot parse wallet last_seen_at '{}': {}",
|
||||
entity.last_seen_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
address: entity.address,
|
||||
label: entity.label,
|
||||
first_seen_at,
|
||||
last_seen_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl KbWalletHoldingDto {
|
||||
source_endpoint_name: std::option::Option<std::string::String>,
|
||||
) -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
wallet_id,
|
||||
token_id,
|
||||
@@ -70,7 +70,7 @@ impl KbWalletHoldingDto {
|
||||
source_endpoint_name,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl TryFrom<crate::KbWalletHoldingEntity> for KbWalletHoldingDto {
|
||||
"cannot parse wallet_holding created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -101,9 +101,9 @@ impl TryFrom<crate::KbWalletHoldingEntity> for KbWalletHoldingDto {
|
||||
"cannot parse wallet_holding updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
wallet_id: entity.wallet_id,
|
||||
token_id: entity.token_id,
|
||||
|
||||
@@ -52,8 +52,7 @@ impl KbWalletParticipationDto {
|
||||
pair_id,
|
||||
role.as_str(),
|
||||
);
|
||||
|
||||
Self {
|
||||
return Self {
|
||||
id: None,
|
||||
wallet_id,
|
||||
transaction_id,
|
||||
@@ -66,7 +65,7 @@ impl KbWalletParticipationDto {
|
||||
source_endpoint_name,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +86,7 @@ impl TryFrom<crate::KbWalletParticipationEntity> for KbWalletParticipationDto {
|
||||
"cannot parse wallet_participation created_at '{}': {}",
|
||||
entity.created_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
|
||||
let updated_at = match updated_at_result {
|
||||
@@ -97,9 +96,9 @@ impl TryFrom<crate::KbWalletParticipationEntity> for KbWalletParticipationDto {
|
||||
"cannot parse wallet_participation updated_at '{}': {}",
|
||||
entity.updated_at, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
id: Some(entity.id),
|
||||
wallet_id: entity.wallet_id,
|
||||
transaction_id: entity.transaction_id,
|
||||
@@ -112,7 +111,7 @@ impl TryFrom<crate::KbWalletParticipationEntity> for KbWalletParticipationDto {
|
||||
source_endpoint_name: entity.source_endpoint_name,
|
||||
created_at,
|
||||
updated_at,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +126,8 @@ fn kb_build_wallet_participation_unique_key(
|
||||
let decoded_event_id_value = decoded_event_id.unwrap_or_default();
|
||||
let pool_id_value = pool_id.unwrap_or_default();
|
||||
let pair_id_value = pair_id.unwrap_or_default();
|
||||
format!(
|
||||
return format!(
|
||||
"{}:{}:{}:{}:{}:{}",
|
||||
wallet_id, transaction_id, decoded_event_id_value, pool_id_value, pair_id_value, role
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub use chain_slot::get_chain_slot;
|
||||
pub use chain_slot::list_recent_chain_slots;
|
||||
pub use chain_slot::upsert_chain_slot;
|
||||
pub use chain_transaction::get_chain_transaction_by_signature;
|
||||
pub use chain_transaction::list_chain_transaction_signatures_for_replay;
|
||||
pub use chain_transaction::list_recent_chain_transactions;
|
||||
pub use chain_transaction::upsert_chain_transaction;
|
||||
pub use db_metadata::get_db_metadata;
|
||||
@@ -59,6 +60,7 @@ pub use dex::get_dex_by_code;
|
||||
pub use dex::list_dexes;
|
||||
pub use dex::upsert_dex;
|
||||
pub use dex_decoded_event::get_dex_decoded_event_by_key;
|
||||
pub use dex_decoded_event::get_latest_pump_fun_create_payload_by_mint;
|
||||
pub use dex_decoded_event::list_dex_decoded_events_by_transaction_id;
|
||||
pub use dex_decoded_event::upsert_dex_decoded_event;
|
||||
pub use known_http_endpoint::get_known_http_endpoint;
|
||||
@@ -85,6 +87,7 @@ pub use onchain_observation::insert_onchain_observation;
|
||||
pub use onchain_observation::list_recent_onchain_observations;
|
||||
pub use pair::get_pair_by_pool_id;
|
||||
pub use pair::list_pairs;
|
||||
pub use pair::update_pair_symbol;
|
||||
pub use pair::upsert_pair;
|
||||
pub use pair_analytic_signal::get_pair_analytic_signal_by_key;
|
||||
pub use pair_analytic_signal::list_pair_analytic_signals_by_pair_id;
|
||||
@@ -108,8 +111,10 @@ pub use pool_token::list_pool_tokens_by_pool_id;
|
||||
pub use pool_token::upsert_pool_token;
|
||||
pub use swap::list_recent_swaps;
|
||||
pub use swap::upsert_swap;
|
||||
pub use token::get_token_by_id;
|
||||
pub use token::get_token_by_mint;
|
||||
pub use token::list_tokens;
|
||||
pub use token::list_tokens_missing_metadata;
|
||||
pub use token::upsert_token;
|
||||
pub use token_burn_event::list_recent_token_burn_events;
|
||||
pub use token_burn_event::upsert_token_burn_event;
|
||||
|
||||
@@ -15,7 +15,7 @@ pub async fn insert_analysis_signal(
|
||||
"cannot serialize analysis signal payload: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
@@ -49,10 +49,10 @@ VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
"cannot insert kb_analysis_signals on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(query_result.last_insert_rowid())
|
||||
}
|
||||
return Ok(query_result.last_insert_rowid());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ LIMIT ?
|
||||
"cannot list analysis signals on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -103,8 +103,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,10 +58,10 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"cannot insert kb_chain_instructions on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(insert_result.last_insert_rowid())
|
||||
}
|
||||
return Ok(insert_result.last_insert_rowid());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ ORDER BY instruction_index ASC, inner_instruction_index ASC, id ASC
|
||||
"cannot list kb_chain_instructions for transaction_id '{}' on sqlite: {}",
|
||||
transaction_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -114,8 +114,8 @@ ORDER BY instruction_index ASC, inner_instruction_index ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +141,8 @@ WHERE transaction_id = ?
|
||||
transaction_id, error
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +163,9 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
async fn make_transaction(database: &crate::KbDatabase) -> i64 {
|
||||
@@ -179,9 +179,9 @@ mod tests {
|
||||
None,
|
||||
r#"{"transaction":{"message":{"instructions":[]}}}"#.to_string(),
|
||||
);
|
||||
crate::upsert_chain_transaction(database, &dto)
|
||||
return crate::upsert_chain_transaction(database, &dto)
|
||||
.await
|
||||
.expect("chain transaction upsert must succeed")
|
||||
.expect("chain transaction upsert must succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -15,7 +15,7 @@ pub async fn upsert_chain_slot(
|
||||
"cannot convert chain slot '{}' to i64: {}",
|
||||
dto.slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let parent_slot = match dto.parent_slot {
|
||||
Some(parent_slot) => {
|
||||
@@ -27,9 +27,9 @@ pub async fn upsert_chain_slot(
|
||||
"cannot convert chain parent_slot '{}' to i64: {}",
|
||||
parent_slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -63,8 +63,8 @@ ON CONFLICT(slot) DO UPDATE SET
|
||||
error
|
||||
)));
|
||||
}
|
||||
Ok(dto.slot)
|
||||
}
|
||||
return Ok(dto.slot);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ pub async fn get_chain_slot(
|
||||
"cannot convert requested chain slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
@@ -108,19 +108,19 @@ LIMIT 1
|
||||
"cannot fetch kb_chain_slots for slot '{}' on sqlite: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbChainSlotDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ LIMIT ?
|
||||
"cannot list kb_chain_slots on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -168,8 +168,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,9 +190,9 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -17,9 +17,9 @@ pub async fn upsert_chain_transaction(
|
||||
"cannot convert chain transaction slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -80,13 +80,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_chain_transactions id for signature '{}' on sqlite: {}",
|
||||
dto.signature, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_chain_transactions id for signature '{}' on sqlite: {}",
|
||||
dto.signature, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,19 +128,19 @@ LIMIT 1
|
||||
"cannot fetch kb_chain_transactions for signature '{}' on sqlite: {}",
|
||||
signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbChainTransactionDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +183,7 @@ LIMIT ?
|
||||
"cannot list kb_chain_transactions on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -192,8 +194,49 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists persisted chain transaction signatures for local pipeline replay.
|
||||
pub async fn list_chain_transaction_signatures_for_replay(
|
||||
database: &crate::KbDatabase,
|
||||
limit: std::option::Option<i64>,
|
||||
) -> Result<std::vec::Vec<std::string::String>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let effective_limit = match limit {
|
||||
Some(limit) => {
|
||||
if limit <= 0 {
|
||||
10_000
|
||||
} else {
|
||||
limit
|
||||
}
|
||||
},
|
||||
None => 10_000,
|
||||
};
|
||||
let query_result = sqlx::query_scalar::<sqlx::Sqlite, std::string::String>(
|
||||
r#"
|
||||
SELECT signature
|
||||
FROM kb_chain_transactions
|
||||
ORDER BY id ASC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(effective_limit)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
match query_result {
|
||||
Ok(signatures) => return Ok(signatures),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot list kb_chain_transactions signatures for local replay on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +257,9 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -249,10 +292,7 @@ mod tests {
|
||||
assert_eq!(fetched.signature, "sig-chain-transaction-1");
|
||||
assert_eq!(fetched.slot, Some(515151));
|
||||
assert_eq!(fetched.block_time_unix, Some(1_700_000_001));
|
||||
assert_eq!(
|
||||
fetched.source_endpoint_name,
|
||||
Some("helius_primary_http".to_string())
|
||||
);
|
||||
assert_eq!(fetched.source_endpoint_name, Some("helius_primary_http".to_string()));
|
||||
assert_eq!(fetched.version_text, Some("0".to_string()));
|
||||
assert_eq!(fetched.meta_json, Some(r#"{"fee":5000}"#.to_string()));
|
||||
let listed = crate::list_recent_chain_transactions(&database, 10)
|
||||
|
||||
@@ -29,13 +29,15 @@ ON CONFLICT(key) DO UPDATE SET
|
||||
.execute(pool)
|
||||
.await;
|
||||
match query_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_db_metadata on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(_) => return Ok(()),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_db_metadata on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,19 +69,19 @@ LIMIT 1
|
||||
"cannot read kb_db_metadata '{}' on sqlite: {}",
|
||||
key, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbDbMetadataDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ ORDER BY key ASC
|
||||
"cannot list kb_db_metadata on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -119,8 +121,8 @@ ORDER BY key ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +148,7 @@ mod tests {
|
||||
.await
|
||||
.expect("database init must succeed");
|
||||
let dto = crate::KbDbMetadataDto::new("schema_version".to_string(), "0.5.0".to_string());
|
||||
crate::upsert_db_metadata(&database, &dto)
|
||||
.await
|
||||
.expect("upsert must succeed");
|
||||
crate::upsert_db_metadata(&database, &dto).await.expect("upsert must succeed");
|
||||
let fetched = crate::get_db_metadata(&database, "schema_version")
|
||||
.await
|
||||
.expect("fetch must succeed");
|
||||
@@ -156,9 +156,7 @@ mod tests {
|
||||
let fetched = fetched.expect("metadata must exist");
|
||||
assert_eq!(fetched.key, "schema_version");
|
||||
assert_eq!(fetched.value, "0.5.0");
|
||||
let listed = crate::list_db_metadata(&database)
|
||||
.await
|
||||
.expect("list must succeed");
|
||||
let listed = crate::list_db_metadata(&database).await.expect("list must succeed");
|
||||
assert!(!listed.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ VALUES (?, ?, ?, ?, ?)
|
||||
"cannot insert kb_db_runtime_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(query_result.last_insert_rowid())
|
||||
}
|
||||
return Ok(query_result.last_insert_rowid());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ LIMIT ?
|
||||
"cannot list runtime events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -87,8 +87,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,13 +56,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_dexes id for code '{}' on sqlite: {}",
|
||||
dto.code, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_dexes id for code '{}' on sqlite: {}",
|
||||
dto.code, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,19 +101,19 @@ LIMIT 1
|
||||
"cannot read kb_dexes '{}' on sqlite: {}",
|
||||
code, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbDexDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +147,7 @@ ORDER BY code ASC
|
||||
"cannot list kb_dexes on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -156,8 +158,8 @@ ORDER BY code ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +186,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
@@ -200,9 +196,7 @@ mod tests {
|
||||
.expect("get dex must succeed");
|
||||
assert!(dex.is_some());
|
||||
assert_eq!(dex.expect("dex must exist").name, "Raydium");
|
||||
let dexes = crate::list_dexes(&database)
|
||||
.await
|
||||
.expect("list dexes must succeed");
|
||||
let dexes = crate::list_dexes(&database).await.expect("list dexes must succeed");
|
||||
assert_eq!(dexes.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +77,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_dex_decoded_events id on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_dex_decoded_events id on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,19 +137,19 @@ LIMIT 1
|
||||
"cannot fetch kb_dex_decoded_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbDexDecodedEventDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +191,7 @@ ORDER BY id ASC
|
||||
"cannot list kb_dex_decoded_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -200,8 +202,48 @@ ORDER BY id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the latest Pump.fun create payload associated with a token mint.
|
||||
pub async fn get_latest_pump_fun_create_payload_by_mint(
|
||||
database: &crate::KbDatabase,
|
||||
mint: &str,
|
||||
) -> Result<std::option::Option<std::string::String>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let payload_result = sqlx::query_scalar::<sqlx::Sqlite, std::string::String>(
|
||||
r#"
|
||||
SELECT payload_json
|
||||
FROM kb_dex_decoded_events
|
||||
WHERE protocol_name = 'pump_fun'
|
||||
AND event_kind IN ('pump_fun.create', 'pump_fun.create_v2_token')
|
||||
AND (
|
||||
token_a_mint = ?
|
||||
OR json_extract(payload_json, '$.mint') = ?
|
||||
OR json_extract(payload_json, '$.tokenMint') = ?
|
||||
)
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(mint)
|
||||
.bind(mint)
|
||||
.bind(mint)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
match payload_result {
|
||||
Ok(payload_option) => return Ok(payload_option),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot read latest pump.fun create payload for mint '{}' on sqlite: {}",
|
||||
mint, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +270,7 @@ mod tests {
|
||||
};
|
||||
let database_result = crate::KbDatabase::connect_and_initialize(&config).await;
|
||||
match database_result {
|
||||
Ok(database) => database,
|
||||
Ok(database) => return database,
|
||||
Err(error) => panic!("database init must succeed: {}", error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,15 @@ ON CONFLICT(name) DO UPDATE SET
|
||||
.execute(pool)
|
||||
.await;
|
||||
match query_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_known_http_endpoints on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(_) => return Ok(()),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_known_http_endpoints on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,19 +90,19 @@ LIMIT 1
|
||||
"cannot read known http endpoint '{}' on sqlite: {}",
|
||||
name, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbKnownHttpEndpointDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ ORDER BY name ASC
|
||||
"cannot list known http endpoints on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -144,8 +146,8 @@ ORDER BY name ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +189,7 @@ mod tests {
|
||||
let fetched = fetched.expect("endpoint must exist");
|
||||
assert_eq!(fetched.provider, "helius");
|
||||
assert_eq!(fetched.roles.len(), 2);
|
||||
let listed = crate::list_known_http_endpoints(&database)
|
||||
.await
|
||||
.expect("list must succeed");
|
||||
let listed = crate::list_known_http_endpoints(&database).await.expect("list must succeed");
|
||||
assert_eq!(listed.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ pub async fn upsert_known_ws_endpoint(
|
||||
Ok(entity) => entity,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query(
|
||||
@@ -46,13 +45,15 @@ ON CONFLICT(name) DO UPDATE SET
|
||||
.execute(pool)
|
||||
.await;
|
||||
match query_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_known_ws_endpoints on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(_) => return Ok(()),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot upsert kb_known_ws_endpoints on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,19 +89,19 @@ LIMIT 1
|
||||
"cannot read known ws endpoint '{}' on sqlite: {}",
|
||||
name, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbKnownWsEndpointDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ ORDER BY name ASC
|
||||
"cannot list known ws endpoints on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -144,8 +145,8 @@ ORDER BY name ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +188,7 @@ mod tests {
|
||||
let fetched = fetched.expect("endpoint must exist");
|
||||
assert_eq!(fetched.provider, "solana");
|
||||
assert_eq!(fetched.roles.len(), 2);
|
||||
let listed = crate::list_known_ws_endpoints(&database)
|
||||
.await
|
||||
.expect("list must succeed");
|
||||
let listed = crate::list_known_ws_endpoints(&database).await.expect("list must succeed");
|
||||
assert_eq!(listed.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,13 +68,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_attributions id for decoded_event_id '{}' on sqlite: {}",
|
||||
dto.decoded_event_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_attributions id for decoded_event_id '{}' on sqlite: {}",
|
||||
dto.decoded_event_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +87,8 @@ pub async fn get_launch_attribution_by_decoded_event_id(
|
||||
) -> Result<std::option::Option<crate::KbLaunchAttributionDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchAttributionEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchAttributionEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
launch_surface_id,
|
||||
@@ -105,10 +106,10 @@ FROM kb_launch_attributions
|
||||
WHERE decoded_event_id = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(decoded_event_id)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(decoded_event_id)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
let entity_option = match query_result {
|
||||
Ok(entity_option) => entity_option,
|
||||
Err(error) => {
|
||||
@@ -116,13 +117,13 @@ LIMIT 1
|
||||
"cannot read kb_launch_attributions by decoded_event_id '{}' on sqlite: {}",
|
||||
decoded_event_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbLaunchAttributionDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbLaunchAttributionDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +134,8 @@ pub async fn list_launch_attributions_by_pool_id(
|
||||
) -> Result<std::vec::Vec<crate::KbLaunchAttributionDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchAttributionEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchAttributionEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
launch_surface_id,
|
||||
@@ -153,10 +153,10 @@ FROM kb_launch_attributions
|
||||
WHERE pool_id = ?
|
||||
ORDER BY attributed_at ASC, id ASC
|
||||
"#,
|
||||
)
|
||||
.bind(pool_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(pool_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let entities = match query_result {
|
||||
Ok(entities) => entities,
|
||||
Err(error) => {
|
||||
@@ -164,7 +164,7 @@ ORDER BY attributed_at ASC, id ASC
|
||||
"cannot list kb_launch_attributions by pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -175,7 +175,7 @@ ORDER BY attributed_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_surfaces id for code '{}' on sqlite: {}",
|
||||
dto.code, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_surfaces id for code '{}' on sqlite: {}",
|
||||
dto.code, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +72,8 @@ pub async fn get_launch_surface_by_code(
|
||||
) -> Result<std::option::Option<crate::KbLaunchSurfaceDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
@@ -85,10 +86,10 @@ FROM kb_launch_surfaces
|
||||
WHERE code = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(code)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(code)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
let entity_option = match query_result {
|
||||
Ok(entity_option) => entity_option,
|
||||
Err(error) => {
|
||||
@@ -96,13 +97,13 @@ LIMIT 1
|
||||
"cannot read kb_launch_surfaces '{}' on sqlite: {}",
|
||||
code, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbLaunchSurfaceDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbLaunchSurfaceDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +113,8 @@ pub async fn list_launch_surfaces(
|
||||
) -> Result<std::vec::Vec<crate::KbLaunchSurfaceDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
@@ -126,9 +126,9 @@ SELECT
|
||||
FROM kb_launch_surfaces
|
||||
ORDER BY code ASC
|
||||
"#,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let entities = match query_result {
|
||||
Ok(entities) => entities,
|
||||
Err(error) => {
|
||||
@@ -136,7 +136,7 @@ ORDER BY code ASC
|
||||
"cannot list kb_launch_surfaces on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -147,7 +147,7 @@ ORDER BY code ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_surface_keys id for '{}:{}' on sqlite: {}",
|
||||
dto.match_kind, dto.match_value, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_launch_surface_keys id for '{}:{}' on sqlite: {}",
|
||||
dto.match_kind, dto.match_value, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +70,8 @@ pub async fn get_launch_surface_key_by_match(
|
||||
) -> Result<std::option::Option<crate::KbLaunchSurfaceKeyDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceKeyEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceKeyEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
launch_surface_id,
|
||||
@@ -82,11 +83,11 @@ FROM kb_launch_surface_keys
|
||||
WHERE match_kind = ? AND match_value = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(match_kind)
|
||||
.bind(match_value)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(match_kind)
|
||||
.bind(match_value)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
let entity_option = match query_result {
|
||||
Ok(entity_option) => entity_option,
|
||||
Err(error) => {
|
||||
@@ -94,13 +95,13 @@ LIMIT 1
|
||||
"cannot read kb_launch_surface_keys '{}:{}' on sqlite: {}",
|
||||
match_kind, match_value, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbLaunchSurfaceKeyDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbLaunchSurfaceKeyDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,9 +112,8 @@ pub async fn list_launch_surface_keys_by_surface_id(
|
||||
) -> Result<std::vec::Vec<crate::KbLaunchSurfaceKeyDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceKeyEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbLaunchSurfaceKeyEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
launch_surface_id,
|
||||
@@ -125,10 +125,10 @@ FROM kb_launch_surface_keys
|
||||
WHERE launch_surface_id = ?
|
||||
ORDER BY match_kind ASC, match_value ASC
|
||||
"#,
|
||||
)
|
||||
.bind(launch_surface_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(launch_surface_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let entities = match query_result {
|
||||
Ok(entities) => entities,
|
||||
Err(error) => {
|
||||
@@ -136,7 +136,7 @@ ORDER BY match_kind ASC, match_value ASC
|
||||
"cannot list kb_launch_surface_keys by surface_id '{}' on sqlite: {}",
|
||||
launch_surface_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -147,7 +147,7 @@ ORDER BY match_kind ASC, match_value ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ pub async fn upsert_liquidity_event(
|
||||
"cannot convert liquidity event slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -96,13 +96,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_liquidity_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_liquidity_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ LIMIT ?
|
||||
"cannot list kb_liquidity_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -161,7 +163,7 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,13 +63,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match select_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_observed_tokens id for mint '{}' on sqlite: {}",
|
||||
dto.mint, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_observed_tokens id for mint '{}' on sqlite: {}",
|
||||
dto.mint, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,19 +110,19 @@ LIMIT 1
|
||||
"cannot read observed token '{}' on sqlite: {}",
|
||||
mint, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbObservedTokenDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +164,7 @@ LIMIT ?
|
||||
"cannot list observed tokens on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -173,8 +175,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,9 +224,7 @@ mod tests {
|
||||
assert_eq!(fetched.symbol.as_deref(), Some("WSOL"));
|
||||
assert_eq!(fetched.decimals, Some(9));
|
||||
assert_eq!(fetched.status, crate::KbObservedTokenStatus::Active);
|
||||
let listed = crate::list_observed_tokens(&database, 10)
|
||||
.await
|
||||
.expect("list must succeed");
|
||||
let listed = crate::list_observed_tokens(&database, 10).await.expect("list must succeed");
|
||||
assert_eq!(listed.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub async fn insert_onchain_observation(
|
||||
"cannot serialize on-chain observation payload: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let slot_i64 = match dto.slot {
|
||||
Some(slot) => {
|
||||
@@ -27,9 +27,9 @@ pub async fn insert_onchain_observation(
|
||||
"cannot convert on-chain observation slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -64,10 +64,10 @@ VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
"cannot insert kb_onchain_observations on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(query_result.last_insert_rowid())
|
||||
}
|
||||
return Ok(query_result.last_insert_rowid());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ LIMIT ?
|
||||
"cannot list on-chain observations on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -118,8 +118,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ ON CONFLICT(pool_id) DO UPDATE SET
|
||||
dex_id = excluded.dex_id,
|
||||
base_token_id = excluded.base_token_id,
|
||||
quote_token_id = excluded.quote_token_id,
|
||||
symbol = excluded.symbol,
|
||||
symbol = CASE
|
||||
WHEN excluded.symbol IS NOT NULL AND trim(excluded.symbol) <> ''
|
||||
THEN excluded.symbol
|
||||
ELSE kb_pairs.symbol
|
||||
END,
|
||||
updated_at = excluded.updated_at
|
||||
"#,
|
||||
)
|
||||
@@ -56,13 +60,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pairs id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pairs id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,19 +105,54 @@ LIMIT 1
|
||||
"cannot read kb_pairs by pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbPairDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the display symbol of one normalized pair row.
|
||||
pub async fn update_pair_symbol(
|
||||
database: &crate::KbDatabase,
|
||||
pair_id: i64,
|
||||
symbol: std::option::Option<&str>,
|
||||
) -> Result<bool, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query(
|
||||
r#"
|
||||
UPDATE kb_pairs
|
||||
SET
|
||||
symbol = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
"#,
|
||||
)
|
||||
.bind(symbol)
|
||||
.bind(chrono::Utc::now().to_rfc3339())
|
||||
.bind(pair_id)
|
||||
.execute(pool)
|
||||
.await;
|
||||
match query_result {
|
||||
Ok(done) => return Ok(done.rows_affected() > 0),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot update kb_pairs symbol for id '{}' on sqlite: {}",
|
||||
pair_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +186,7 @@ ORDER BY id ASC
|
||||
"cannot list kb_pairs on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -156,8 +197,8 @@ ORDER BY id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +225,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
@@ -248,13 +283,8 @@ mod tests {
|
||||
.await
|
||||
.expect("get pair must succeed");
|
||||
assert!(pair.is_some());
|
||||
assert_eq!(
|
||||
pair.expect("pair must exist").symbol.as_deref(),
|
||||
Some("BASE/WSOL")
|
||||
);
|
||||
let pairs = crate::list_pairs(&database)
|
||||
.await
|
||||
.expect("list pairs must succeed");
|
||||
assert_eq!(pair.expect("pair must exist").symbol.as_deref(), Some("BASE/WSOL"));
|
||||
let pairs = crate::list_pairs(&database).await.expect("list pairs must succeed");
|
||||
assert_eq!(pairs.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub async fn upsert_pair_analytic_signal(
|
||||
"cannot serialize pair analytic signal payload: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
@@ -77,13 +77,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_analytic_signals id on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_analytic_signals id on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +99,8 @@ pub async fn get_pair_analytic_signal_by_key(
|
||||
) -> Result<std::option::Option<crate::KbPairAnalyticSignalDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbPairAnalyticSignalEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbPairAnalyticSignalEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
pair_id,
|
||||
@@ -117,13 +118,13 @@ FROM kb_pair_analytic_signals
|
||||
WHERE pair_id = ? AND signal_kind = ? AND timeframe_seconds = ? AND bucket_start_unix = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(pair_id)
|
||||
.bind(signal_kind)
|
||||
.bind(timeframe_seconds)
|
||||
.bind(bucket_start_unix)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(pair_id)
|
||||
.bind(signal_kind)
|
||||
.bind(timeframe_seconds)
|
||||
.bind(bucket_start_unix)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
let entity_option = match query_result {
|
||||
Ok(entity_option) => entity_option,
|
||||
Err(error) => {
|
||||
@@ -131,13 +132,13 @@ LIMIT 1
|
||||
"cannot read kb_pair_analytic_signals by key on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbPairAnalyticSignalDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbPairAnalyticSignalDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +149,8 @@ pub async fn list_pair_analytic_signals_by_pair_id(
|
||||
) -> Result<std::vec::Vec<crate::KbPairAnalyticSignalDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result =
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbPairAnalyticSignalEntity>(
|
||||
r#"
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbPairAnalyticSignalEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
pair_id,
|
||||
@@ -168,10 +168,10 @@ FROM kb_pair_analytic_signals
|
||||
WHERE pair_id = ?
|
||||
ORDER BY timeframe_seconds ASC, bucket_start_unix ASC, signal_kind ASC, id ASC
|
||||
"#,
|
||||
)
|
||||
.bind(pair_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
)
|
||||
.bind(pair_id)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let entities = match query_result {
|
||||
Ok(entities) => entities,
|
||||
Err(error) => {
|
||||
@@ -179,7 +179,7 @@ ORDER BY timeframe_seconds ASC, bucket_start_unix ASC, signal_kind ASC, id ASC
|
||||
"cannot list kb_pair_analytic_signals by pair_id '{}' on sqlite: {}",
|
||||
pair_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -190,7 +190,7 @@ ORDER BY timeframe_seconds ASC, bucket_start_unix ASC, signal_kind ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,13 +87,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_candles id for pair_id '{}' timeframe '{}' bucket '{}' on sqlite: {}",
|
||||
dto.pair_id, dto.timeframe_seconds, dto.bucket_start_unix, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_candles id for pair_id '{}' timeframe '{}' bucket '{}' on sqlite: {}",
|
||||
dto.pair_id, dto.timeframe_seconds, dto.bucket_start_unix, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,13 +146,13 @@ LIMIT 1
|
||||
"cannot read kb_pair_candles by key on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbPairCandleDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbPairCandleDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +201,7 @@ ORDER BY bucket_start_unix ASC, id ASC
|
||||
"cannot list kb_pair_candles by pair_id '{}' timeframe '{}' on sqlite: {}",
|
||||
pair_id, timeframe_seconds, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -210,7 +212,7 @@ ORDER BY bucket_start_unix ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,13 +74,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_metrics id for pair_id '{}' on sqlite: {}",
|
||||
dto.pair_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pair_metrics id for pair_id '{}' on sqlite: {}",
|
||||
dto.pair_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +125,13 @@ LIMIT 1
|
||||
"cannot read kb_pair_metrics by pair_id '{}' on sqlite: {}",
|
||||
pair_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbPairMetricDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbPairMetricDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ ORDER BY pair_id ASC
|
||||
"cannot list kb_pair_metrics on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -180,7 +182,7 @@ ORDER BY pair_id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pools id for address '{}' on sqlite: {}",
|
||||
dto.address, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pools id for address '{}' on sqlite: {}",
|
||||
dto.address, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,19 +97,19 @@ LIMIT 1
|
||||
"cannot read kb_pools '{}' on sqlite: {}",
|
||||
address, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbPoolDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +142,7 @@ ORDER BY id ASC
|
||||
"cannot list kb_pools on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -151,8 +153,8 @@ ORDER BY id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,13 +181,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
@@ -206,13 +202,8 @@ mod tests {
|
||||
.await
|
||||
.expect("get pool must succeed");
|
||||
assert!(pool.is_some());
|
||||
assert_eq!(
|
||||
pool.expect("pool must exist").pool_kind,
|
||||
crate::KbPoolKind::Amm
|
||||
);
|
||||
let pools = crate::list_pools(&database)
|
||||
.await
|
||||
.expect("list pools must succeed");
|
||||
assert_eq!(pool.expect("pool must exist").pool_kind, crate::KbPoolKind::Amm);
|
||||
let pools = crate::list_pools(&database).await.expect("list pools must succeed");
|
||||
assert_eq!(pools.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +66,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_listings id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_listings id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,19 +114,19 @@ LIMIT 1
|
||||
"cannot read kb_pool_listings by pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbPoolListingDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +163,7 @@ ORDER BY detected_at ASC, id ASC
|
||||
"cannot list kb_pool_listings on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -172,8 +174,8 @@ ORDER BY detected_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,13 +202,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
|
||||
@@ -69,13 +69,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_origins id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_origins id for pool_id '{}' on sqlite: {}",
|
||||
dto.pool_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +121,13 @@ LIMIT 1
|
||||
"cannot read kb_pool_origins by pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbPoolOriginDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbPoolOriginDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +168,7 @@ ORDER BY created_at ASC, id ASC
|
||||
"cannot list kb_pool_origins on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -177,7 +179,7 @@ ORDER BY created_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +56,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_tokens id on sqlite: {}",
|
||||
error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_pool_tokens id on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ ORDER BY token_order ASC, id ASC
|
||||
"cannot list kb_pool_tokens for pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -110,8 +112,8 @@ ORDER BY token_order ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,13 +140,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
|
||||
@@ -17,9 +17,9 @@ pub async fn upsert_swap(
|
||||
"cannot convert swap slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -93,13 +93,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_swaps id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_swaps id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +148,7 @@ LIMIT ?
|
||||
"cannot list kb_swaps on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -157,7 +159,7 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_tokens id for mint '{}' on sqlite: {}",
|
||||
dto.mint, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_tokens id for mint '{}' on sqlite: {}",
|
||||
dto.mint, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,22 +106,71 @@ LIMIT 1
|
||||
"cannot read kb_tokens '{}' on sqlite: {}",
|
||||
mint, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbTokenDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => Ok(Some(dto)),
|
||||
Err(error) => Err(error),
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads one normalized token row by internal id.
|
||||
pub async fn get_token_by_id(
|
||||
database: &crate::KbDatabase,
|
||||
token_id: i64,
|
||||
) -> Result<std::option::Option<crate::KbTokenDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::KbTokenEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
mint,
|
||||
symbol,
|
||||
name,
|
||||
decimals,
|
||||
token_program,
|
||||
is_quote_token,
|
||||
first_seen_at,
|
||||
updated_at
|
||||
FROM kb_tokens
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(token_id)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
let entity_option = match query_result {
|
||||
Ok(entity_option) => entity_option,
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot read kb_tokens id '{}' on sqlite: {}",
|
||||
token_id, error
|
||||
)));
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => {
|
||||
let dto_result = crate::KbTokenDto::try_from(entity);
|
||||
match dto_result {
|
||||
Ok(dto) => return Ok(Some(dto)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists all normalized token rows ordered by mint.
|
||||
pub async fn list_tokens(
|
||||
@@ -152,7 +203,7 @@ ORDER BY mint ASC, id ASC
|
||||
"cannot list kb_tokens on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -163,7 +214,161 @@ ORDER BY mint ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists token rows whose display or mint metadata is incomplete.
|
||||
pub async fn list_tokens_missing_metadata(
|
||||
database: &crate::KbDatabase,
|
||||
limit: std::option::Option<i64>,
|
||||
) -> Result<std::vec::Vec<crate::KbTokenDto>, crate::KbError> {
|
||||
match database.connection() {
|
||||
crate::KbDatabaseConnection::Sqlite(pool) => {
|
||||
let entities_result = match limit {
|
||||
Some(limit) => {
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbTokenEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
mint,
|
||||
symbol,
|
||||
name,
|
||||
decimals,
|
||||
token_program,
|
||||
is_quote_token,
|
||||
first_seen_at,
|
||||
updated_at
|
||||
FROM kb_tokens
|
||||
WHERE symbol IS NULL
|
||||
OR trim(symbol) = ''
|
||||
OR name IS NULL
|
||||
OR trim(name) = ''
|
||||
OR decimals IS NULL
|
||||
OR token_program IS NULL
|
||||
OR trim(token_program) = ''
|
||||
ORDER BY updated_at ASC, id ASC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(limit)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
},
|
||||
None => {
|
||||
sqlx::query_as::<sqlx::Sqlite, crate::KbTokenEntity>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
mint,
|
||||
symbol,
|
||||
name,
|
||||
decimals,
|
||||
token_program,
|
||||
is_quote_token,
|
||||
first_seen_at,
|
||||
updated_at
|
||||
FROM kb_tokens
|
||||
WHERE symbol IS NULL
|
||||
OR trim(symbol) = ''
|
||||
OR name IS NULL
|
||||
OR trim(name) = ''
|
||||
OR decimals IS NULL
|
||||
OR token_program IS NULL
|
||||
OR trim(token_program) = ''
|
||||
ORDER BY updated_at ASC, id ASC
|
||||
"#,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
},
|
||||
};
|
||||
let entities = match entities_result {
|
||||
Ok(entities) => entities,
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot list kb_tokens missing metadata on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
let dto_result = crate::KbTokenDto::try_from(entity);
|
||||
let dto = match dto_result {
|
||||
Ok(dto) => dto,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
async fn make_database() -> std::sync::Arc<crate::KbDatabase> {
|
||||
let tempdir_result = tempfile::tempdir();
|
||||
let tempdir = match tempdir_result {
|
||||
Ok(tempdir) => tempdir,
|
||||
Err(error) => panic!("tempdir must succeed: {}", error),
|
||||
};
|
||||
let database_path = tempdir.path().join("token_query.sqlite3");
|
||||
let config = crate::KbDatabaseConfig {
|
||||
enabled: true,
|
||||
backend: crate::KbDatabaseBackend::Sqlite,
|
||||
sqlite: crate::KbSqliteDatabaseConfig {
|
||||
path: database_path.to_string_lossy().to_string(),
|
||||
create_if_missing: true,
|
||||
busy_timeout_ms: 5000,
|
||||
max_connections: 1,
|
||||
auto_initialize_schema: true,
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
let database_result = crate::KbDatabase::connect_and_initialize(&config).await;
|
||||
let database = match database_result {
|
||||
Ok(database) => database,
|
||||
Err(error) => panic!("database init must succeed: {}", error),
|
||||
};
|
||||
return std::sync::Arc::new(database);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_tokens_missing_metadata_only_returns_incomplete_rows() {
|
||||
let database = make_database().await;
|
||||
let incomplete = crate::KbTokenDto::new(
|
||||
"IncompleteMint111".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||
false,
|
||||
);
|
||||
let complete = crate::KbTokenDto::new(
|
||||
"CompleteMint111".to_string(),
|
||||
Some("CMP".to_string()),
|
||||
Some("Complete".to_string()),
|
||||
Some(6),
|
||||
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||
false,
|
||||
);
|
||||
let incomplete_result = crate::upsert_token(database.as_ref(), &incomplete).await;
|
||||
if let Err(error) = incomplete_result {
|
||||
panic!("incomplete token upsert must succeed: {}", error);
|
||||
}
|
||||
let complete_result = crate::upsert_token(database.as_ref(), &complete).await;
|
||||
if let Err(error) = complete_result {
|
||||
panic!("complete token upsert must succeed: {}", error);
|
||||
}
|
||||
let missing_result = crate::list_tokens_missing_metadata(database.as_ref(), None).await;
|
||||
let missing = match missing_result {
|
||||
Ok(missing) => missing,
|
||||
Err(error) => panic!("missing metadata list must succeed: {}", error),
|
||||
};
|
||||
assert_eq!(missing.len(), 1);
|
||||
assert_eq!(missing[0].mint, "IncompleteMint111");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ pub async fn upsert_token_burn_event(
|
||||
"cannot convert token burn event slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -78,13 +78,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_token_burn_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_token_burn_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ LIMIT ?
|
||||
"cannot list kb_token_burn_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -137,8 +139,8 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,13 +167,7 @@ mod tests {
|
||||
.expect("database init must succeed");
|
||||
let dex_id = crate::upsert_dex(
|
||||
&database,
|
||||
&crate::KbDexDto::new(
|
||||
"raydium".to_string(),
|
||||
"Raydium".to_string(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
&crate::KbDexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
|
||||
)
|
||||
.await
|
||||
.expect("dex upsert must succeed");
|
||||
@@ -299,9 +295,7 @@ mod tests {
|
||||
assert!(liquidity_id > 0);
|
||||
assert!(mint_id > 0);
|
||||
assert!(burn_id > 0);
|
||||
let swaps = crate::list_recent_swaps(&database, 10)
|
||||
.await
|
||||
.expect("swaps list must succeed");
|
||||
let swaps = crate::list_recent_swaps(&database, 10).await.expect("swaps list must succeed");
|
||||
let liquidity_events = crate::list_recent_liquidity_events(&database, 10)
|
||||
.await
|
||||
.expect("liquidity list must succeed");
|
||||
|
||||
@@ -17,9 +17,9 @@ pub async fn upsert_token_mint_event(
|
||||
"cannot convert token mint event slot '{}' to i64: {}",
|
||||
slot, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match database.connection() {
|
||||
@@ -78,13 +78,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_token_mint_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_token_mint_events id for signature '{}' and instruction_index '{}' on sqlite: {}",
|
||||
dto.signature, dto.instruction_index, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ LIMIT ?
|
||||
"cannot list kb_token_mint_events on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -137,7 +139,7 @@ LIMIT ?
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +90,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_trade_events id for decoded_event_id '{}' on sqlite: {}",
|
||||
dto.decoded_event_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_trade_events id for decoded_event_id '{}' on sqlite: {}",
|
||||
dto.decoded_event_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,13 +146,13 @@ LIMIT 1
|
||||
"cannot read kb_trade_events by decoded_event_id '{}' on sqlite: {}",
|
||||
decoded_event_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbTradeEventDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbTradeEventDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ ORDER BY created_at ASC, id ASC
|
||||
"cannot list kb_trade_events by pair_id '{}' on sqlite: {}",
|
||||
pair_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -209,8 +211,8 @@ ORDER BY created_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ ORDER BY id ASC
|
||||
"cannot list kb_trade_events by transaction_id '{}' on sqlite: {}",
|
||||
transaction_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -269,15 +271,15 @@ ORDER BY id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn kb_trade_side_to_string(value: crate::KbSwapTradeSide) -> &'static str {
|
||||
match value {
|
||||
crate::KbSwapTradeSide::BuyBase => "BuyBase",
|
||||
crate::KbSwapTradeSide::SellBase => "SellBase",
|
||||
crate::KbSwapTradeSide::Unknown => "Unknown",
|
||||
crate::KbSwapTradeSide::BuyBase => return "BuyBase",
|
||||
crate::KbSwapTradeSide::SellBase => return "SellBase",
|
||||
crate::KbSwapTradeSide::Unknown => return "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallets id for address '{}' on sqlite: {}",
|
||||
dto.address, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallets id for address '{}' on sqlite: {}",
|
||||
dto.address, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,13 +89,13 @@ LIMIT 1
|
||||
"cannot read kb_wallets '{}' on sqlite: {}",
|
||||
address, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbWalletDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbWalletDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +126,7 @@ ORDER BY address ASC
|
||||
"cannot list kb_wallets on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -135,7 +137,7 @@ ORDER BY address ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,13 +76,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallet_holdings id for wallet_id '{}' token_id '{}' on sqlite: {}",
|
||||
dto.wallet_id, dto.token_id, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallet_holdings id for wallet_id '{}' token_id '{}' on sqlite: {}",
|
||||
dto.wallet_id, dto.token_id, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +130,13 @@ LIMIT 1
|
||||
"cannot read kb_wallet_holdings by wallet_id '{}' token_id '{}' on sqlite: {}",
|
||||
wallet_id, token_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbWalletHoldingDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbWalletHoldingDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +180,7 @@ ORDER BY token_id ASC, id ASC
|
||||
"cannot list kb_wallet_holdings by wallet_id '{}' on sqlite: {}",
|
||||
wallet_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -189,7 +191,7 @@ ORDER BY token_id ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,15 @@ LIMIT 1
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
match id_result {
|
||||
Ok(id) => Ok(id),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallet_participations id for unique_key '{}' on sqlite: {}",
|
||||
dto.unique_key, error
|
||||
))),
|
||||
Ok(id) => return Ok(id),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot fetch kb_wallet_participations id for unique_key '{}' on sqlite: {}",
|
||||
dto.unique_key, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +109,13 @@ LIMIT 1
|
||||
"cannot read kb_wallet_participations by unique_key '{}' on sqlite: {}",
|
||||
unique_key, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
match entity_option {
|
||||
Some(entity) => crate::KbWalletParticipationDto::try_from(entity).map(Some),
|
||||
None => Ok(None),
|
||||
Some(entity) => return crate::KbWalletParticipationDto::try_from(entity).map(Some),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +156,7 @@ ORDER BY created_at ASC, id ASC
|
||||
"cannot list kb_wallet_participations by wallet_id '{}' on sqlite: {}",
|
||||
wallet_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -165,8 +167,8 @@ ORDER BY created_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +209,7 @@ ORDER BY created_at ASC, id ASC
|
||||
"cannot list kb_wallet_participations by pool_id '{}' on sqlite: {}",
|
||||
pool_id, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for entity in entities {
|
||||
@@ -218,7 +220,7 @@ ORDER BY created_at ASC, id ASC
|
||||
};
|
||||
dtos.push(dto);
|
||||
}
|
||||
Ok(dtos)
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,10 @@ pub(crate) fn sqlite_database_url_from_config(
|
||||
) -> Result<std::string::String, crate::KbError> {
|
||||
let path = config.sqlite.path.trim();
|
||||
if path.is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"database.sqlite.path must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("database.sqlite.path must not be empty".to_string()));
|
||||
}
|
||||
let database_path = config.sqlite.path_buf();
|
||||
Ok(format!("sqlite://{}", database_path.display()))
|
||||
return Ok(format!("sqlite://{}", database_path.display()));
|
||||
}
|
||||
|
||||
/// Opens a SQLite pool according to configuration.
|
||||
@@ -22,9 +20,7 @@ pub(crate) async fn connect_sqlite(
|
||||
) -> Result<sqlx::SqlitePool, crate::KbError> {
|
||||
let path = config.sqlite.path.trim();
|
||||
if path.is_empty() {
|
||||
return Err(crate::KbError::Config(
|
||||
"database.sqlite.path must not be empty".to_string(),
|
||||
));
|
||||
return Err(crate::KbError::Config("database.sqlite.path must not be empty".to_string()));
|
||||
}
|
||||
if config.sqlite.max_connections == 0 {
|
||||
return Err(crate::KbError::Config(
|
||||
@@ -49,9 +45,7 @@ pub(crate) async fn connect_sqlite(
|
||||
.filename(&database_path)
|
||||
.create_if_missing(config.sqlite.create_if_missing)
|
||||
.foreign_keys(true)
|
||||
.busy_timeout(std::time::Duration::from_millis(
|
||||
config.sqlite.busy_timeout_ms,
|
||||
));
|
||||
.busy_timeout(std::time::Duration::from_millis(config.sqlite.busy_timeout_ms));
|
||||
if config.sqlite.use_wal {
|
||||
connect_options = connect_options.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal);
|
||||
}
|
||||
@@ -59,10 +53,13 @@ pub(crate) async fn connect_sqlite(
|
||||
sqlx::sqlite::SqlitePoolOptions::new().max_connections(config.sqlite.max_connections);
|
||||
let connect_result = pool_options.connect_with(connect_options).await;
|
||||
match connect_result {
|
||||
Ok(pool) => Ok(pool),
|
||||
Err(error) => Err(crate::KbError::Db(format!(
|
||||
"cannot open sqlite database '{}': {}",
|
||||
database_path.display(), error
|
||||
))),
|
||||
Ok(pool) => return Ok(pool),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"cannot open sqlite database '{}': {}",
|
||||
database_path.display(),
|
||||
error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,28 +21,28 @@ impl KbAnalysisSignalSeverity {
|
||||
/// Converts the severity to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Info => 0,
|
||||
Self::Low => 1,
|
||||
Self::Medium => 2,
|
||||
Self::High => 3,
|
||||
Self::Critical => 4,
|
||||
Self::Info => return 0,
|
||||
Self::Low => return 1,
|
||||
Self::Medium => return 2,
|
||||
Self::High => return 3,
|
||||
Self::Critical => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a severity from its stable integer representation.
|
||||
pub fn from_i16(
|
||||
value: i16,
|
||||
) -> Result<Self, crate::KbError> {
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Info),
|
||||
1 => Ok(Self::Low),
|
||||
2 => Ok(Self::Medium),
|
||||
3 => Ok(Self::High),
|
||||
4 => Ok(Self::Critical),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbAnalysisSignalSeverity value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Info),
|
||||
1 => return Ok(Self::Low),
|
||||
2 => return Ok(Self::Medium),
|
||||
3 => return Ok(Self::High),
|
||||
4 => return Ok(Self::Critical),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbAnalysisSignalSeverity value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,22 @@ impl KbLiquidityEventKind {
|
||||
/// Converts the event kind to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Add => 0,
|
||||
Self::Remove => 1,
|
||||
Self::Add => return 0,
|
||||
Self::Remove => return 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores an event kind from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Add),
|
||||
1 => Ok(Self::Remove),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbLiquidityEventKind value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Add),
|
||||
1 => return Ok(Self::Remove),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbLiquidityEventKind value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,28 @@ impl KbObservationSourceKind {
|
||||
/// Converts the source kind to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::HttpRpc => 0,
|
||||
Self::WsRpc => 1,
|
||||
Self::Grpc => 2,
|
||||
Self::Dex => 3,
|
||||
Self::Other => 4,
|
||||
Self::HttpRpc => return 0,
|
||||
Self::WsRpc => return 1,
|
||||
Self::Grpc => return 2,
|
||||
Self::Dex => return 3,
|
||||
Self::Other => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a source kind from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::HttpRpc),
|
||||
1 => Ok(Self::WsRpc),
|
||||
2 => Ok(Self::Grpc),
|
||||
3 => Ok(Self::Dex),
|
||||
4 => Ok(Self::Other),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbObservationSourceKind value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::HttpRpc),
|
||||
1 => return Ok(Self::WsRpc),
|
||||
2 => return Ok(Self::Grpc),
|
||||
3 => return Ok(Self::Dex),
|
||||
4 => return Ok(Self::Other),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbObservationSourceKind value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,24 +19,26 @@ impl KbObservedTokenStatus {
|
||||
/// Converts the status to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::New => 0,
|
||||
Self::Active => 1,
|
||||
Self::Ignored => 2,
|
||||
Self::Blocked => 3,
|
||||
Self::New => return 0,
|
||||
Self::Active => return 1,
|
||||
Self::Ignored => return 2,
|
||||
Self::Blocked => return 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a status from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::New),
|
||||
1 => Ok(Self::Active),
|
||||
2 => Ok(Self::Ignored),
|
||||
3 => Ok(Self::Blocked),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbObservedTokenStatus value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::New),
|
||||
1 => return Ok(Self::Active),
|
||||
2 => return Ok(Self::Ignored),
|
||||
3 => return Ok(Self::Blocked),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbObservedTokenStatus value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// file: kb_lib/src/db/types/pool_kind.rs
|
||||
|
||||
//! Pool kind.
|
||||
@@ -22,28 +21,23 @@ impl KbPoolKind {
|
||||
/// Converts the kind to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Unknown => 0,
|
||||
Self::Amm => 1,
|
||||
Self::Clmm => 2,
|
||||
Self::BondingCurve => 3,
|
||||
Self::OrderBook => 4,
|
||||
Self::Unknown => return 0,
|
||||
Self::Amm => return 1,
|
||||
Self::Clmm => return 2,
|
||||
Self::BondingCurve => return 3,
|
||||
Self::OrderBook => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a kind from its stable integer representation.
|
||||
pub fn from_i16(
|
||||
value: i16,
|
||||
) -> Result<Self, crate::KbError> {
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Unknown),
|
||||
1 => Ok(Self::Amm),
|
||||
2 => Ok(Self::Clmm),
|
||||
3 => Ok(Self::BondingCurve),
|
||||
4 => Ok(Self::OrderBook),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbPoolKind value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Unknown),
|
||||
1 => return Ok(Self::Amm),
|
||||
2 => return Ok(Self::Clmm),
|
||||
3 => return Ok(Self::BondingCurve),
|
||||
4 => return Ok(Self::OrderBook),
|
||||
_ => return Err(crate::KbError::Db(format!("invalid KbPoolKind value: {}", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,23 @@ impl KbPoolStatus {
|
||||
/// Converts the status to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Unknown => 0,
|
||||
Self::Pending => 1,
|
||||
Self::Active => 2,
|
||||
Self::Inactive => 3,
|
||||
Self::Closed => 4,
|
||||
Self::Unknown => return 0,
|
||||
Self::Pending => return 1,
|
||||
Self::Active => return 2,
|
||||
Self::Inactive => return 3,
|
||||
Self::Closed => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a status from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Unknown),
|
||||
1 => Ok(Self::Pending),
|
||||
2 => Ok(Self::Active),
|
||||
3 => Ok(Self::Inactive),
|
||||
4 => Ok(Self::Closed),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbPoolStatus value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Unknown),
|
||||
1 => return Ok(Self::Pending),
|
||||
2 => return Ok(Self::Active),
|
||||
3 => return Ok(Self::Inactive),
|
||||
4 => return Ok(Self::Closed),
|
||||
_ => return Err(crate::KbError::Db(format!("invalid KbPoolStatus value: {}", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,25 @@ impl KbPoolTokenRole {
|
||||
/// Converts the role to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Base => 0,
|
||||
Self::Quote => 1,
|
||||
Self::LpMint => 2,
|
||||
Self::Reserve => 3,
|
||||
Self::Other => 4,
|
||||
Self::Base => return 0,
|
||||
Self::Quote => return 1,
|
||||
Self::LpMint => return 2,
|
||||
Self::Reserve => return 3,
|
||||
Self::Other => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a role from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Base),
|
||||
1 => Ok(Self::Quote),
|
||||
2 => Ok(Self::LpMint),
|
||||
3 => Ok(Self::Reserve),
|
||||
4 => Ok(Self::Other),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbPoolTokenRole value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Base),
|
||||
1 => return Ok(Self::Quote),
|
||||
2 => return Ok(Self::LpMint),
|
||||
3 => return Ok(Self::Reserve),
|
||||
4 => return Ok(Self::Other),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!("invalid KbPoolTokenRole value: {}", value)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,28 @@ impl KbDbRuntimeEventLevel {
|
||||
/// Converts the level to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Trace => 0,
|
||||
Self::Debug => 1,
|
||||
Self::Info => 2,
|
||||
Self::Warn => 3,
|
||||
Self::Error => 4,
|
||||
Self::Trace => return 0,
|
||||
Self::Debug => return 1,
|
||||
Self::Info => return 2,
|
||||
Self::Warn => return 3,
|
||||
Self::Error => return 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a level from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Trace),
|
||||
1 => Ok(Self::Debug),
|
||||
2 => Ok(Self::Info),
|
||||
3 => Ok(Self::Warn),
|
||||
4 => Ok(Self::Error),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbDbRuntimeEventLevel value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Trace),
|
||||
1 => return Ok(Self::Debug),
|
||||
2 => return Ok(Self::Info),
|
||||
3 => return Ok(Self::Warn),
|
||||
4 => return Ok(Self::Error),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbDbRuntimeEventLevel value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,22 +17,24 @@ impl KbSwapTradeSide {
|
||||
/// Converts the trade side to its stable integer representation.
|
||||
pub fn to_i16(self) -> i16 {
|
||||
match self {
|
||||
Self::Unknown => 0,
|
||||
Self::BuyBase => 1,
|
||||
Self::SellBase => 2,
|
||||
Self::Unknown => return 0,
|
||||
Self::BuyBase => return 1,
|
||||
Self::SellBase => return 2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a trade side from its stable integer representation.
|
||||
pub fn from_i16(value: i16) -> Result<Self, crate::KbError> {
|
||||
match value {
|
||||
0 => Ok(Self::Unknown),
|
||||
1 => Ok(Self::BuyBase),
|
||||
2 => Ok(Self::SellBase),
|
||||
_ => Err(crate::KbError::Db(format!(
|
||||
"invalid KbSwapTradeSide value: {}",
|
||||
value
|
||||
))),
|
||||
0 => return Ok(Self::Unknown),
|
||||
1 => return Ok(Self::BuyBase),
|
||||
2 => return Ok(Self::SellBase),
|
||||
_ => {
|
||||
return Err(crate::KbError::Db(format!(
|
||||
"invalid KbSwapTradeSide value: {}",
|
||||
value
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ pub struct KbDetectionPersistenceService {
|
||||
impl KbDetectionPersistenceService {
|
||||
/// Creates a new detection persistence service.
|
||||
pub fn new(database: std::sync::Arc<crate::KbDatabase>) -> Self {
|
||||
Self { database }
|
||||
return Self { database };
|
||||
}
|
||||
/// Returns the shared database handle.
|
||||
pub fn database(&self) -> &std::sync::Arc<crate::KbDatabase> {
|
||||
&self.database
|
||||
return &self.database;
|
||||
}
|
||||
|
||||
/// Persists one on-chain observation.
|
||||
@@ -32,7 +32,7 @@ impl KbDetectionPersistenceService {
|
||||
input.slot,
|
||||
input.payload.clone(),
|
||||
);
|
||||
crate::insert_onchain_observation(self.database.as_ref(), &dto).await
|
||||
return crate::insert_onchain_observation(self.database.as_ref(), &dto).await;
|
||||
}
|
||||
|
||||
/// Persists one analysis signal.
|
||||
@@ -48,7 +48,7 @@ impl KbDetectionPersistenceService {
|
||||
input.score,
|
||||
input.payload.clone(),
|
||||
);
|
||||
crate::insert_analysis_signal(self.database.as_ref(), &dto).await
|
||||
return crate::insert_analysis_signal(self.database.as_ref(), &dto).await;
|
||||
}
|
||||
|
||||
/// Registers one token candidate from a technical source.
|
||||
@@ -104,11 +104,7 @@ impl KbDetectionPersistenceService {
|
||||
Ok(signal_id) => signal_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(crate::KbDetectionTokenCandidateResult {
|
||||
token_id,
|
||||
observation_id,
|
||||
signal_id,
|
||||
})
|
||||
return Ok(crate::KbDetectionTokenCandidateResult { token_id, observation_id, signal_id });
|
||||
}
|
||||
|
||||
/// Registers one pool candidate from a technical source.
|
||||
@@ -154,7 +150,7 @@ impl KbDetectionPersistenceService {
|
||||
"cannot register pool candidate: no known dex matches program id '{}'",
|
||||
input.dex_program_id
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let dex_id = match matched_dex.id {
|
||||
Some(dex_id) => dex_id,
|
||||
@@ -162,7 +158,7 @@ impl KbDetectionPersistenceService {
|
||||
return Err(crate::KbError::Db(
|
||||
"cannot register pool candidate: matched dex has no id".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
let pool_dto = crate::KbPoolDto::new(
|
||||
dex_id,
|
||||
@@ -221,13 +217,13 @@ impl KbDetectionPersistenceService {
|
||||
Ok(signal_id) => signal_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(crate::KbDetectionPoolCandidateResult {
|
||||
return Ok(crate::KbDetectionPoolCandidateResult {
|
||||
dex_id,
|
||||
pool_id,
|
||||
pool_listing_id,
|
||||
observation_id,
|
||||
signal_id,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,9 +244,9 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -293,14 +289,8 @@ mod tests {
|
||||
.expect("list signals must succeed");
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"So11111111111111111111111111111111111111112"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"So11111111111111111111111111111111111111112"
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "So11111111111111111111111111111111111111112");
|
||||
assert_eq!(signals[0].object_key, "So11111111111111111111111111111111111111112");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -340,10 +330,7 @@ mod tests {
|
||||
.await
|
||||
.expect("get token must succeed");
|
||||
assert!(token.is_some());
|
||||
assert_eq!(
|
||||
token.expect("token must exist").symbol.as_deref(),
|
||||
Some("TEST")
|
||||
);
|
||||
assert_eq!(token.expect("token must exist").symbol.as_deref(), Some("TEST"));
|
||||
let observations = crate::list_recent_onchain_observations(service.database().as_ref(), 10)
|
||||
.await
|
||||
.expect("list observations must succeed");
|
||||
@@ -352,18 +339,9 @@ mod tests {
|
||||
.expect("list signals must succeed");
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].related_observation_id,
|
||||
Some(result.observation_id)
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].related_observation_id, Some(result.observation_id));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -37,12 +37,12 @@ pub struct KbSolanaWsDetectionService {
|
||||
impl KbSolanaWsDetectionService {
|
||||
/// Creates a new Solana WebSocket detection service.
|
||||
pub fn new(persistence: crate::KbDetectionPersistenceService) -> Self {
|
||||
Self { persistence }
|
||||
return Self { persistence };
|
||||
}
|
||||
|
||||
/// Returns the shared persistence façade.
|
||||
pub fn persistence(&self) -> &crate::KbDetectionPersistenceService {
|
||||
&self.persistence
|
||||
return &self.persistence;
|
||||
}
|
||||
|
||||
/// Processes one Solana WebSocket JSON-RPC notification.
|
||||
@@ -57,24 +57,22 @@ impl KbSolanaWsDetectionService {
|
||||
Some(observation_kind) => observation_kind,
|
||||
None => return Ok(crate::KbSolanaWsDetectionOutcome::Ignored),
|
||||
};
|
||||
let token_candidate_result = self
|
||||
.try_register_token_candidate(endpoint_name.clone(), notification)
|
||||
.await;
|
||||
let token_candidate_result =
|
||||
self.try_register_token_candidate(endpoint_name.clone(), notification).await;
|
||||
match token_candidate_result {
|
||||
Ok(Some(result)) => {
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::TokenCandidateRegistered { result });
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
let pool_candidate_result = self
|
||||
.try_register_pool_candidate(endpoint_name.clone(), notification)
|
||||
.await;
|
||||
let pool_candidate_result =
|
||||
self.try_register_pool_candidate(endpoint_name.clone(), notification).await;
|
||||
match pool_candidate_result {
|
||||
Ok(Some(result)) => {
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::PoolCandidateRegistered { result });
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
let payload = build_notification_payload(notification);
|
||||
@@ -93,10 +91,7 @@ impl KbSolanaWsDetectionService {
|
||||
slot,
|
||||
payload.clone(),
|
||||
);
|
||||
let observation_id_result = self
|
||||
.persistence
|
||||
.record_observation(&observation_input)
|
||||
.await;
|
||||
let observation_id_result = self.persistence.record_observation(&observation_input).await;
|
||||
let observation_id = match observation_id_result {
|
||||
Ok(observation_id) => observation_id,
|
||||
Err(error) => return Err(error),
|
||||
@@ -127,7 +122,7 @@ impl KbSolanaWsDetectionService {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
Ok(crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id })
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id });
|
||||
}
|
||||
|
||||
/// Tries to register a token candidate from one notification.
|
||||
@@ -210,8 +205,8 @@ impl KbSolanaWsDetectionService {
|
||||
);
|
||||
let result = self.persistence.register_token_candidate(&input).await;
|
||||
match result {
|
||||
Ok(result) => Ok(Some(result)),
|
||||
Err(error) => Err(error),
|
||||
Ok(result) => return Ok(Some(result)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,8 +283,8 @@ impl KbSolanaWsDetectionService {
|
||||
);
|
||||
let result = self.persistence.register_pool_candidate(&input).await;
|
||||
match result {
|
||||
Ok(result) => Ok(Some(result)),
|
||||
Err(error) => Err(error),
|
||||
Ok(result) => return Ok(Some(result)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,27 +294,27 @@ fn map_notification_method_to_observation_kind(
|
||||
method: &str,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
match method {
|
||||
"accountNotification" => Some("ws.account_notification".to_string()),
|
||||
"blockNotification" => Some("ws.block_notification".to_string()),
|
||||
"logsNotification" => Some("ws.logs_notification".to_string()),
|
||||
"programNotification" => Some("ws.program_notification".to_string()),
|
||||
"rootNotification" => Some("ws.root_notification".to_string()),
|
||||
"signatureNotification" => Some("ws.signature_notification".to_string()),
|
||||
"slotNotification" => Some("ws.slot_notification".to_string()),
|
||||
"slotsUpdatesNotification" => Some("ws.slots_updates_notification".to_string()),
|
||||
"voteNotification" => Some("ws.vote_notification".to_string()),
|
||||
_ => None,
|
||||
"accountNotification" => return Some("ws.account_notification".to_string()),
|
||||
"blockNotification" => return Some("ws.block_notification".to_string()),
|
||||
"logsNotification" => return Some("ws.logs_notification".to_string()),
|
||||
"programNotification" => return Some("ws.program_notification".to_string()),
|
||||
"rootNotification" => return Some("ws.root_notification".to_string()),
|
||||
"signatureNotification" => return Some("ws.signature_notification".to_string()),
|
||||
"slotNotification" => return Some("ws.slot_notification".to_string()),
|
||||
"slotsUpdatesNotification" => return Some("ws.slots_updates_notification".to_string()),
|
||||
"voteNotification" => return Some("ws.vote_notification".to_string()),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps one raw notification into a normalized JSON payload.
|
||||
fn build_notification_payload(notification: &crate::KbJsonRpcWsNotification) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
return serde_json::json!({
|
||||
"jsonrpc": notification.jsonrpc,
|
||||
"method": notification.method,
|
||||
"subscription": notification.params.subscription,
|
||||
"result": notification.params.result,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Builds one logical object key from the notification result.
|
||||
@@ -340,8 +335,7 @@ fn build_object_key(
|
||||
if let Some(slot) = slot_option {
|
||||
return format!("slot:{slot}");
|
||||
}
|
||||
|
||||
format!("subscription:{subscription}")
|
||||
return format!("subscription:{subscription}");
|
||||
}
|
||||
|
||||
/// Extracts a slot number from one notification result.
|
||||
@@ -364,7 +358,7 @@ fn extract_slot_from_result(method: &str, result: &serde_json::Value) -> std::op
|
||||
return Some(slot);
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts a pubkey from one notification result.
|
||||
@@ -379,7 +373,7 @@ fn extract_pubkey_from_result(
|
||||
return Some(pubkey.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts a signature from one notification result.
|
||||
@@ -394,13 +388,13 @@ fn extract_signature_from_result(
|
||||
return Some(signature.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts one account-like JSON object from one notification result.
|
||||
fn extract_account_value_from_result<'a>(
|
||||
result: &'a serde_json::Value,
|
||||
) -> std::option::Option<&'a serde_json::Value> {
|
||||
fn extract_account_value_from_result(
|
||||
result: &serde_json::Value,
|
||||
) -> std::option::Option<&serde_json::Value> {
|
||||
if let Some(account) = result.get("account") {
|
||||
return Some(account);
|
||||
}
|
||||
@@ -412,7 +406,7 @@ fn extract_account_value_from_result<'a>(
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts the parsed account type from one account-like JSON object.
|
||||
@@ -431,8 +425,8 @@ fn extract_parsed_account_type(
|
||||
};
|
||||
let type_option = parsed.get("type").and_then(serde_json::Value::as_str);
|
||||
match type_option {
|
||||
Some(parsed_type) => Some(parsed_type.to_string()),
|
||||
None => None,
|
||||
Some(parsed_type) => return Some(parsed_type.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,12 +434,10 @@ fn extract_parsed_account_type(
|
||||
fn extract_account_owner(
|
||||
account_value: &serde_json::Value,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let owner_option = account_value
|
||||
.get("owner")
|
||||
.and_then(serde_json::Value::as_str);
|
||||
let owner_option = account_value.get("owner").and_then(serde_json::Value::as_str);
|
||||
match owner_option {
|
||||
Some(owner) => Some(owner.to_string()),
|
||||
None => None,
|
||||
Some(owner) => return Some(owner.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,8 +467,8 @@ fn extract_decimals_from_account_value(
|
||||
};
|
||||
let converted = u8::try_from(decimals);
|
||||
match converted {
|
||||
Ok(decimals) => Some(decimals),
|
||||
Err(_) => None,
|
||||
Ok(decimals) => return Some(decimals),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +481,7 @@ fn extract_program_notification_owner(
|
||||
Some(account_value) => account_value,
|
||||
None => return None,
|
||||
};
|
||||
extract_account_owner(account_value)
|
||||
return extract_account_owner(account_value);
|
||||
}
|
||||
|
||||
/// Extracts the parsed token amount decimals from one parsed token account notification.
|
||||
@@ -516,17 +508,15 @@ fn extract_token_account_decimals_from_account_value(
|
||||
Some(token_amount) => token_amount,
|
||||
None => return None,
|
||||
};
|
||||
let decimals_option = token_amount
|
||||
.get("decimals")
|
||||
.and_then(serde_json::Value::as_u64);
|
||||
let decimals_option = token_amount.get("decimals").and_then(serde_json::Value::as_u64);
|
||||
let decimals = match decimals_option {
|
||||
Some(decimals) => decimals,
|
||||
None => return None,
|
||||
};
|
||||
let convert_result = u8::try_from(decimals);
|
||||
match convert_result {
|
||||
Ok(decimals) => Some(decimals),
|
||||
Err(_) => None,
|
||||
Ok(decimals) => return Some(decimals),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,8 +541,8 @@ fn extract_parsed_account_mint(
|
||||
};
|
||||
let mint_option = info.get("mint").and_then(serde_json::Value::as_str);
|
||||
match mint_option {
|
||||
Some(mint) => Some(mint.to_string()),
|
||||
None => None,
|
||||
Some(mint) => return Some(mint.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +570,7 @@ fn extract_logs_lines(result: &serde_json::Value) -> std::vec::Vec<std::string::
|
||||
lines.push(line.to_string());
|
||||
}
|
||||
}
|
||||
lines
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// Extracts the error field from a signature notification result.
|
||||
@@ -593,8 +583,8 @@ fn extract_signature_notification_err(
|
||||
None => return None,
|
||||
};
|
||||
match value.get("err") {
|
||||
Some(err) => Some(err.clone()),
|
||||
None => None,
|
||||
Some(err) => return Some(err.clone()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,8 +611,8 @@ fn build_signal_kind_for_notification(
|
||||
}
|
||||
}
|
||||
}
|
||||
"signal.account_notification.generic".to_string()
|
||||
}
|
||||
return "signal.account_notification.generic".to_string();
|
||||
},
|
||||
"logsNotification" => {
|
||||
let lines = extract_logs_lines(result);
|
||||
for line in &lines {
|
||||
@@ -639,21 +629,21 @@ fn build_signal_kind_for_notification(
|
||||
return "signal.logs_notification.initialize_account".to_string();
|
||||
}
|
||||
}
|
||||
"signal.logs_notification.generic".to_string()
|
||||
}
|
||||
return "signal.logs_notification.generic".to_string();
|
||||
},
|
||||
"signatureNotification" => {
|
||||
let err_option = extract_signature_notification_err(result);
|
||||
match err_option {
|
||||
Some(err) => {
|
||||
if err.is_null() {
|
||||
"signal.signature_notification.confirmed".to_string()
|
||||
return "signal.signature_notification.confirmed".to_string();
|
||||
} else {
|
||||
"signal.signature_notification.failed".to_string()
|
||||
return "signal.signature_notification.failed".to_string();
|
||||
}
|
||||
}
|
||||
None => "signal.signature_notification.generic".to_string(),
|
||||
},
|
||||
None => return "signal.signature_notification.generic".to_string(),
|
||||
}
|
||||
}
|
||||
},
|
||||
"programNotification" => {
|
||||
let owner_option = extract_program_notification_owner(result);
|
||||
let owner = match owner_option {
|
||||
@@ -666,12 +656,9 @@ fn build_signal_kind_for_notification(
|
||||
if owner == crate::SPL_TOKEN_2022_PROGRAM_ID.to_string() {
|
||||
return "signal.program_notification.spl_token_2022".to_string();
|
||||
}
|
||||
"signal.program_notification.generic".to_string()
|
||||
}
|
||||
_ => format!(
|
||||
"signal.{}",
|
||||
method.replace("Notification", "").to_lowercase()
|
||||
),
|
||||
return "signal.program_notification.generic".to_string();
|
||||
},
|
||||
_ => return format!("signal.{}", method.replace("Notification", "").to_lowercase()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,8 +668,8 @@ fn build_signal_severity_for_notification(
|
||||
result: &serde_json::Value,
|
||||
) -> crate::KbAnalysisSignalSeverity {
|
||||
match method {
|
||||
"programNotification" => crate::KbAnalysisSignalSeverity::Medium,
|
||||
"accountNotification" => crate::KbAnalysisSignalSeverity::Low,
|
||||
"programNotification" => return crate::KbAnalysisSignalSeverity::Medium,
|
||||
"accountNotification" => return crate::KbAnalysisSignalSeverity::Low,
|
||||
"logsNotification" => {
|
||||
let lines = extract_logs_lines(result);
|
||||
for line in &lines {
|
||||
@@ -690,22 +677,22 @@ fn build_signal_severity_for_notification(
|
||||
return crate::KbAnalysisSignalSeverity::Medium;
|
||||
}
|
||||
}
|
||||
crate::KbAnalysisSignalSeverity::Low
|
||||
}
|
||||
return crate::KbAnalysisSignalSeverity::Low;
|
||||
},
|
||||
"signatureNotification" => {
|
||||
let err_option = extract_signature_notification_err(result);
|
||||
match err_option {
|
||||
Some(err) => {
|
||||
if err.is_null() {
|
||||
crate::KbAnalysisSignalSeverity::Low
|
||||
return crate::KbAnalysisSignalSeverity::Low;
|
||||
} else {
|
||||
crate::KbAnalysisSignalSeverity::Medium
|
||||
return crate::KbAnalysisSignalSeverity::Medium;
|
||||
}
|
||||
}
|
||||
None => crate::KbAnalysisSignalSeverity::Low,
|
||||
},
|
||||
None => return crate::KbAnalysisSignalSeverity::Low,
|
||||
}
|
||||
}
|
||||
_ => crate::KbAnalysisSignalSeverity::Low,
|
||||
},
|
||||
_ => return crate::KbAnalysisSignalSeverity::Low,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,13 +713,13 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
fn build_slot_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "slotNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -743,11 +730,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 1008_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_program_mint_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "programNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -773,11 +760,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 2048_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_program_pool_candidate_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "programNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -798,11 +785,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 5555_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_logs_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "logsNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -820,11 +807,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 3001_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_signature_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "signatureNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -838,7 +825,7 @@ mod tests {
|
||||
}),
|
||||
subscription: 4001_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -859,7 +846,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -894,7 +881,7 @@ mod tests {
|
||||
assert!(result.token_id > 0);
|
||||
assert!(result.observation_id > 0);
|
||||
assert!(result.signal_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let token_result = crate::get_token_by_mint(
|
||||
@@ -923,14 +910,8 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -951,7 +932,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -970,10 +951,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
signals[0].signal_kind,
|
||||
"signal.logs_notification.initialize_mint"
|
||||
);
|
||||
assert_eq!(signals[0].signal_kind, "signal.logs_notification.initialize_mint");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -994,7 +972,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -1013,10 +991,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
signals[0].signal_kind,
|
||||
"signal.signature_notification.confirmed"
|
||||
);
|
||||
assert_eq!(signals[0].signal_kind, "signal.signature_notification.confirmed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1053,7 +1028,7 @@ mod tests {
|
||||
assert!(result.pool_id > 0);
|
||||
assert!(result.pool_listing_id > 0);
|
||||
result.pool_id
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
};
|
||||
let pool_result = crate::get_pool_by_address(
|
||||
|
||||
@@ -29,14 +29,14 @@ impl KbDetectionObservationInput {
|
||||
slot: std::option::Option<u64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
observation_kind,
|
||||
source_kind,
|
||||
endpoint_name,
|
||||
object_key,
|
||||
slot,
|
||||
payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ impl KbDetectionSignalInput {
|
||||
score: std::option::Option<f64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
signal_kind,
|
||||
severity,
|
||||
object_key,
|
||||
related_observation_id,
|
||||
score,
|
||||
payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ impl KbDetectionTokenCandidateInput {
|
||||
signal_score: std::option::Option<f64>,
|
||||
signal_payload: std::option::Option<serde_json::Value>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
mint,
|
||||
symbol,
|
||||
name,
|
||||
@@ -149,7 +149,7 @@ impl KbDetectionTokenCandidateInput {
|
||||
signal_severity,
|
||||
signal_score,
|
||||
signal_payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ impl KbDetectionPoolCandidateInput {
|
||||
signal_score: std::option::Option<f64>,
|
||||
signal_payload: std::option::Option<serde_json::Value>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
pool_address,
|
||||
dex_program_id,
|
||||
source_kind,
|
||||
@@ -219,7 +219,7 @@ impl KbDetectionPoolCandidateInput {
|
||||
signal_severity,
|
||||
signal_score,
|
||||
signal_payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ impl KbWsDetectionNotificationEnvelope {
|
||||
endpoint_name: std::option::Option<std::string::String>,
|
||||
notification: crate::KbJsonRpcWsNotification,
|
||||
) -> Self {
|
||||
Self {
|
||||
endpoint_name,
|
||||
notification,
|
||||
}
|
||||
return Self { endpoint_name, notification };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +51,7 @@ pub struct KbWsDetectionRelay {
|
||||
impl KbWsDetectionRelay {
|
||||
/// Creates a new relay.
|
||||
pub fn new(detector: crate::KbSolanaWsDetectionService) -> Self {
|
||||
Self { detector }
|
||||
return Self { detector };
|
||||
}
|
||||
|
||||
/// Creates a bounded relay channel.
|
||||
@@ -64,7 +61,7 @@ impl KbWsDetectionRelay {
|
||||
tokio::sync::mpsc::Sender<crate::KbWsDetectionNotificationEnvelope>,
|
||||
tokio::sync::mpsc::Receiver<crate::KbWsDetectionNotificationEnvelope>,
|
||||
) {
|
||||
tokio::sync::mpsc::channel(capacity)
|
||||
return tokio::sync::mpsc::channel(capacity);
|
||||
}
|
||||
|
||||
/// Processes one forwarded notification.
|
||||
@@ -72,9 +69,10 @@ impl KbWsDetectionRelay {
|
||||
&self,
|
||||
envelope: &crate::KbWsDetectionNotificationEnvelope,
|
||||
) -> Result<crate::KbSolanaWsDetectionOutcome, crate::KbError> {
|
||||
self.detector
|
||||
return self
|
||||
.detector
|
||||
.process_notification(envelope.endpoint_name.clone(), &envelope.notification)
|
||||
.await
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Spawns one background relay worker.
|
||||
@@ -82,7 +80,7 @@ impl KbWsDetectionRelay {
|
||||
self,
|
||||
mut receiver: tokio::sync::mpsc::Receiver<crate::KbWsDetectionNotificationEnvelope>,
|
||||
) -> tokio::task::JoinHandle<crate::KbWsDetectionRelayStats> {
|
||||
tokio::spawn(async move {
|
||||
return tokio::spawn(async move {
|
||||
let mut stats = crate::KbWsDetectionRelayStats::default();
|
||||
loop {
|
||||
let recv_result = receiver.recv().await;
|
||||
@@ -103,25 +101,25 @@ impl KbWsDetectionRelay {
|
||||
error
|
||||
);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
};
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::Ignored => {
|
||||
stats.ignored_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { .. } => {
|
||||
stats.observation_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::TokenCandidateRegistered { .. } => {
|
||||
stats.token_candidate_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::PoolCandidateRegistered { .. } => {
|
||||
stats.pool_candidate_count += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
stats
|
||||
})
|
||||
return stats;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,13 +140,13 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
fn build_slot_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "slotNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -159,7 +157,7 @@ mod tests {
|
||||
}),
|
||||
subscription: 1008_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -180,7 +178,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected relay outcome"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ enum KbDexlabInstructionKind {
|
||||
impl KbDexlabDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more DexLab events.
|
||||
@@ -93,7 +93,7 @@ impl KbDexlabDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -104,7 +104,7 @@ impl KbDexlabDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -140,45 +140,24 @@ impl KbDexlabDecoder {
|
||||
kb_classify_instruction_kind(parsed_json.as_ref(), &log_messages);
|
||||
let pool_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"pool",
|
||||
"poolAddress",
|
||||
"poolAccount",
|
||||
"amm",
|
||||
"ammPool",
|
||||
"poolState",
|
||||
],
|
||||
&["pool", "poolAddress", "poolAccount", "amm", "ammPool", "poolState"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenA",
|
||||
"tokenAMint",
|
||||
"mintA",
|
||||
"baseMint",
|
||||
"token0Mint",
|
||||
"mint0",
|
||||
],
|
||||
&["tokenA", "tokenAMint", "mintA", "baseMint", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenB",
|
||||
"tokenBMint",
|
||||
"mintB",
|
||||
"quoteMint",
|
||||
"token1Mint",
|
||||
"mint1",
|
||||
],
|
||||
&["tokenB", "tokenBMint", "mintB", "quoteMint", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["payer", "creator", "user", "owner"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let fee_tier = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["feeTier", "fee_tier", "tradeFeeTier", "feeRate"],
|
||||
@@ -246,7 +225,7 @@ impl KbDexlabDecoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +257,7 @@ fn kb_classify_instruction_kind(
|
||||
if kb_log_messages_contain_keyword(log_messages, "swap") {
|
||||
return KbDexlabInstructionKind::Swap;
|
||||
}
|
||||
KbDexlabInstructionKind::Unknown
|
||||
return KbDexlabInstructionKind::Unknown;
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
@@ -305,7 +284,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -316,7 +295,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
@@ -326,7 +305,7 @@ fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -340,7 +319,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -349,7 +328,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -361,11 +340,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +358,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -412,7 +393,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -422,7 +403,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
@@ -432,7 +413,7 @@ fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTra
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -462,7 +443,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(801);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -498,7 +479,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(802);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -526,7 +507,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(803);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -559,7 +540,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(804);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -584,10 +565,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert_eq!(event.fee_tier, Some("0.3%".to_string()));
|
||||
}
|
||||
},
|
||||
crate::KbDexlabDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,10 +593,10 @@ mod tests {
|
||||
event.token_b_mint,
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
crate::KbDexlabDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ enum KbFluxbeamInstructionKind {
|
||||
impl KbFluxbeamDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more FluxBeam events.
|
||||
@@ -93,7 +93,7 @@ impl KbFluxbeamDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -104,7 +104,7 @@ impl KbFluxbeamDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -140,50 +140,29 @@ impl KbFluxbeamDecoder {
|
||||
kb_classify_instruction_kind(parsed_json.as_ref(), &log_messages);
|
||||
let pool_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"pool",
|
||||
"poolAddress",
|
||||
"poolAccount",
|
||||
"amm",
|
||||
"ammPool",
|
||||
"poolState",
|
||||
],
|
||||
&["pool", "poolAddress", "poolAccount", "amm", "ammPool", "poolState"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let lp_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["lpMint", "lp_mint", "lpTokenMint"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenA",
|
||||
"tokenAMint",
|
||||
"mintA",
|
||||
"baseMint",
|
||||
"token0Mint",
|
||||
"mint0",
|
||||
],
|
||||
&["tokenA", "tokenAMint", "mintA", "baseMint", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenB",
|
||||
"tokenBMint",
|
||||
"mintB",
|
||||
"quoteMint",
|
||||
"token1Mint",
|
||||
"mint1",
|
||||
],
|
||||
&["tokenB", "tokenBMint", "mintB", "quoteMint", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["payer", "creator", "user", "owner"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
if instruction_kind == KbFluxbeamInstructionKind::CreatePool {
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "fluxbeam",
|
||||
@@ -247,7 +226,7 @@ impl KbFluxbeamDecoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +257,7 @@ fn kb_classify_instruction_kind(
|
||||
if kb_log_messages_contain_keyword(log_messages, "swap") {
|
||||
return KbFluxbeamInstructionKind::Swap;
|
||||
}
|
||||
KbFluxbeamInstructionKind::Unknown
|
||||
return KbFluxbeamInstructionKind::Unknown;
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
@@ -305,7 +284,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -316,7 +295,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
@@ -326,7 +305,7 @@ fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -340,7 +319,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -349,7 +328,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -361,11 +340,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +358,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -412,7 +393,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -422,7 +403,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
@@ -432,7 +413,7 @@ fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTra
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -462,7 +443,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(701);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -499,7 +480,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(702);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -527,7 +508,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(703);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -561,7 +542,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(704);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -586,10 +567,10 @@ mod tests {
|
||||
event.token_b_mint,
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
crate::KbFluxbeamDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,10 +595,10 @@ mod tests {
|
||||
event.token_b_mint,
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
crate::KbFluxbeamDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ enum KbMeteoraDammV1InstructionKind {
|
||||
impl KbMeteoraDammV1Decoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Meteora DAMM v1 events.
|
||||
@@ -96,7 +96,7 @@ impl KbMeteoraDammV1Decoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -107,7 +107,7 @@ impl KbMeteoraDammV1Decoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -143,50 +143,29 @@ impl KbMeteoraDammV1Decoder {
|
||||
kb_classify_instruction_kind(parsed_json.as_ref(), &log_messages);
|
||||
let pool_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"pool",
|
||||
"poolAddress",
|
||||
"poolAccount",
|
||||
"amm",
|
||||
"ammPool",
|
||||
"poolState",
|
||||
],
|
||||
&["pool", "poolAddress", "poolAccount", "amm", "ammPool", "poolState"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenAMint",
|
||||
"mintA",
|
||||
"baseMint",
|
||||
"token0Mint",
|
||||
"mint0",
|
||||
"coinMint",
|
||||
],
|
||||
&["tokenAMint", "mintA", "baseMint", "token0Mint", "mint0", "coinMint"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenBMint",
|
||||
"mintB",
|
||||
"quoteMint",
|
||||
"token1Mint",
|
||||
"mint1",
|
||||
"pcMint",
|
||||
],
|
||||
&["tokenBMint", "mintB", "quoteMint", "token1Mint", "mint1", "pcMint"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let config_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["config", "poolConfig", "ammConfig", "tradeFeeConfig"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["creator", "payer", "user", "owner"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
if instruction_kind == KbMeteoraDammV1InstructionKind::CreatePool
|
||||
|| instruction_kind == KbMeteoraDammV1InstructionKind::CreatePoolWithConfig
|
||||
{
|
||||
@@ -257,7 +236,7 @@ impl KbMeteoraDammV1Decoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +276,7 @@ fn kb_classify_instruction_kind(
|
||||
if kb_log_messages_contain_keyword(log_messages, "swap") {
|
||||
return KbMeteoraDammV1InstructionKind::Swap;
|
||||
}
|
||||
KbMeteoraDammV1InstructionKind::Unknown
|
||||
return KbMeteoraDammV1InstructionKind::Unknown;
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
@@ -325,7 +304,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -336,7 +315,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
@@ -346,7 +325,7 @@ fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -360,7 +339,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -369,7 +348,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -381,11 +360,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,7 +378,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -432,7 +413,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key(
|
||||
@@ -443,7 +424,7 @@ fn kb_value_contains_any_key(
|
||||
Some(value) => value,
|
||||
None => return false,
|
||||
};
|
||||
kb_value_contains_any_key_inner(value, candidate_keys)
|
||||
return kb_value_contains_any_key_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[&str]) -> bool {
|
||||
@@ -467,7 +448,7 @@ fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -477,7 +458,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
@@ -487,7 +468,7 @@ fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTra
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -517,7 +498,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(501);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -554,7 +535,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(502);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -582,7 +563,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(503);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -615,7 +596,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(504);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -640,10 +621,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert!(event.used_config);
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDammV1DecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,10 +649,10 @@ mod tests {
|
||||
event.token_b_mint,
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDammV1DecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ enum KbMeteoraDammV2InstructionKind {
|
||||
impl KbMeteoraDammV2Decoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Meteora DAMM v2 events.
|
||||
@@ -99,7 +99,7 @@ impl KbMeteoraDammV2Decoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -110,7 +110,7 @@ impl KbMeteoraDammV2Decoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -148,27 +148,27 @@ impl KbMeteoraDammV2Decoder {
|
||||
parsed_json.as_ref(),
|
||||
&["pool", "poolAddress", "poolAccount", "poolState", "cpAmm"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["tokenAMint", "mintA", "baseMint", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["tokenBMint", "mintB", "quoteMint", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let config_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["staticConfig", "dynamicConfig", "config", "poolConfig"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["creator", "payer", "user", "owner"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
if instruction_kind == KbMeteoraDammV2InstructionKind::CreatePoolStatic
|
||||
|| instruction_kind == KbMeteoraDammV2InstructionKind::CreatePoolDynamic
|
||||
|| instruction_kind == KbMeteoraDammV2InstructionKind::CreatePoolCustomizable
|
||||
@@ -178,7 +178,7 @@ impl KbMeteoraDammV2Decoder {
|
||||
KbMeteoraDammV2InstructionKind::CreatePoolDynamic => "dynamic".to_string(),
|
||||
KbMeteoraDammV2InstructionKind::CreatePoolCustomizable => {
|
||||
"customizable".to_string()
|
||||
}
|
||||
},
|
||||
_ => "unknown".to_string(),
|
||||
};
|
||||
let payload_json = serde_json::json!({
|
||||
@@ -249,7 +249,7 @@ impl KbMeteoraDammV2Decoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_classify_instruction_kind(
|
||||
@@ -333,7 +333,7 @@ fn kb_classify_instruction_kind(
|
||||
{
|
||||
return KbMeteoraDammV2InstructionKind::Swap;
|
||||
}
|
||||
KbMeteoraDammV2InstructionKind::Unknown
|
||||
return KbMeteoraDammV2InstructionKind::Unknown;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -344,7 +344,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
@@ -354,7 +354,7 @@ fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -368,7 +368,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -377,7 +377,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -389,11 +389,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,7 +407,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -440,7 +442,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key(
|
||||
@@ -451,7 +453,7 @@ fn kb_value_contains_any_key(
|
||||
Some(value) => value,
|
||||
None => return false,
|
||||
};
|
||||
kb_value_contains_any_key_inner(value, candidate_keys)
|
||||
return kb_value_contains_any_key_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[&str]) -> bool {
|
||||
@@ -475,7 +477,7 @@ fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -485,7 +487,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
@@ -495,7 +497,7 @@ fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTra
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -525,7 +527,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(401);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -562,7 +564,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(402);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -590,7 +592,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(403);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -623,7 +625,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(404);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -648,10 +650,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert_eq!(event.create_kind, "customizable".to_string());
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDammV2DecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,10 +679,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert!(event.used_swap2);
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDammV2DecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ pub struct KbMeteoraDbcDecoder;
|
||||
impl KbMeteoraDbcDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Meteora DBC events.
|
||||
@@ -93,7 +93,7 @@ impl KbMeteoraDbcDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -104,7 +104,7 @@ impl KbMeteoraDbcDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -142,27 +142,27 @@ impl KbMeteoraDbcDecoder {
|
||||
parsed_json.as_ref(),
|
||||
&["pool", "poolAccount", "poolState", "virtualPool", "poolKey"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["baseMint", "tokenAMint", "mintA", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["quoteMint", "tokenBMint", "mintB", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let config_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["poolConfig", "config", "dbcConfig", "curveConfig"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["creator", "poolCreator", "owner", "user"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
if instruction_kind == KbMeteoraDbcInstructionKind::CreatePool {
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "meteora_dbc",
|
||||
@@ -228,7 +228,7 @@ impl KbMeteoraDbcDecoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_any_keyword(
|
||||
@@ -269,7 +269,7 @@ fn kb_log_messages_contain_any_keyword(
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -280,7 +280,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_log_text(value: &str) -> std::string::String {
|
||||
@@ -290,7 +290,7 @@ fn kb_normalize_log_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -304,7 +304,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -313,7 +313,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -325,11 +325,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +343,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -376,7 +378,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key(
|
||||
@@ -387,7 +389,7 @@ fn kb_value_contains_any_key(
|
||||
Some(value) => value,
|
||||
None => return false,
|
||||
};
|
||||
kb_value_contains_any_key_inner(value, candidate_keys)
|
||||
return kb_value_contains_any_key_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[&str]) -> bool {
|
||||
@@ -411,7 +413,7 @@ fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -421,7 +423,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
@@ -431,7 +433,7 @@ fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTra
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
fn kb_classify_instruction_kind(
|
||||
@@ -456,32 +458,21 @@ fn kb_classify_instruction_kind(
|
||||
}
|
||||
let has_create_config = kb_value_contains_any_key(
|
||||
parsed_json,
|
||||
&[
|
||||
"poolConfig",
|
||||
"migrationQuoteThreshold",
|
||||
"curveConfig",
|
||||
"dbcConfig",
|
||||
],
|
||||
&["poolConfig", "migrationQuoteThreshold", "curveConfig", "dbcConfig"],
|
||||
);
|
||||
if has_create_config {
|
||||
return KbMeteoraDbcInstructionKind::CreatePool;
|
||||
}
|
||||
if kb_log_messages_contain_any_keyword(
|
||||
log_messages,
|
||||
&[
|
||||
"create_pool",
|
||||
"createpool",
|
||||
"initialize_pool",
|
||||
"initializepool",
|
||||
"launch_pool",
|
||||
],
|
||||
&["create_pool", "createpool", "initialize_pool", "initializepool", "launch_pool"],
|
||||
) {
|
||||
return KbMeteoraDbcInstructionKind::CreatePool;
|
||||
}
|
||||
if kb_log_messages_contain_any_keyword(log_messages, &["swap2", "swap"]) {
|
||||
return KbMeteoraDbcInstructionKind::Swap;
|
||||
}
|
||||
KbMeteoraDbcInstructionKind::Unknown
|
||||
return KbMeteoraDbcInstructionKind::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -511,7 +502,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(301);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -547,7 +538,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(302);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -575,7 +566,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(303);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -607,7 +598,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(304);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -632,10 +623,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert_eq!(event.config_account, Some("DbcConfig111".to_string()));
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDbcDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,10 +651,10 @@ mod tests {
|
||||
event.token_b_mint,
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
crate::KbMeteoraDbcDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,10 +678,10 @@ mod tests {
|
||||
};
|
||||
assert_eq!(decoded.len(), 1);
|
||||
match &decoded[0] {
|
||||
crate::KbMeteoraDbcDecodedEvent::Swap(_) => {}
|
||||
crate::KbMeteoraDbcDecodedEvent::Swap(_) => {},
|
||||
crate::KbMeteoraDbcDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,10 +697,10 @@ mod tests {
|
||||
};
|
||||
assert_eq!(decoded.len(), 1);
|
||||
match &decoded[0] {
|
||||
crate::KbMeteoraDbcDecodedEvent::CreatePool(_) => {}
|
||||
crate::KbMeteoraDbcDecodedEvent::CreatePool(_) => {},
|
||||
crate::KbMeteoraDbcDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! Orca Whirlpools transaction decoder.
|
||||
|
||||
/// Orca Whirlpools program id.
|
||||
pub const KB_ORCA_WHIRLPOOLS_PROGRAM_ID: &str =
|
||||
"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc";
|
||||
pub const KB_ORCA_WHIRLPOOLS_PROGRAM_ID: &str = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc";
|
||||
|
||||
/// Decoded Orca Whirlpools create-pool event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -83,7 +82,7 @@ enum KbOrcaWhirlpoolsInstructionKind {
|
||||
impl KbOrcaWhirlpoolsDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Orca Whirlpools events.
|
||||
@@ -100,7 +99,7 @@ impl KbOrcaWhirlpoolsDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -111,7 +110,7 @@ impl KbOrcaWhirlpoolsDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -137,7 +136,8 @@ impl KbOrcaWhirlpoolsDecoder {
|
||||
Ok(accounts) => accounts,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let parsed_json_result = kb_parse_optional_parsed_json(instruction.parsed_json.as_ref());
|
||||
let parsed_json_result =
|
||||
kb_parse_optional_parsed_json(instruction.parsed_json.as_ref());
|
||||
let parsed_json = match parsed_json_result {
|
||||
Ok(parsed_json) => parsed_json,
|
||||
Err(error) => return Err(error),
|
||||
@@ -146,59 +146,33 @@ impl KbOrcaWhirlpoolsDecoder {
|
||||
kb_classify_instruction_kind(parsed_json.as_ref(), &log_messages);
|
||||
let pool_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"whirlpool",
|
||||
"pool",
|
||||
"poolAddress",
|
||||
"poolAccount",
|
||||
"whirlpoolAddress",
|
||||
],
|
||||
&["whirlpool", "pool", "poolAddress", "poolAccount", "whirlpoolAddress"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenMintA",
|
||||
"tokenAMint",
|
||||
"mintA",
|
||||
"baseMint",
|
||||
"token0Mint",
|
||||
"mint0",
|
||||
],
|
||||
&["tokenMintA", "tokenAMint", "mintA", "baseMint", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 1));
|
||||
.or_else(|| return kb_extract_account(&accounts, 1));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenMintB",
|
||||
"tokenBMint",
|
||||
"mintB",
|
||||
"quoteMint",
|
||||
"token1Mint",
|
||||
"mint1",
|
||||
],
|
||||
&["tokenMintB", "tokenBMint", "mintB", "quoteMint", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 2));
|
||||
.or_else(|| return kb_extract_account(&accounts, 2));
|
||||
let config_account = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"whirlpoolsConfig",
|
||||
"config",
|
||||
"configAccount",
|
||||
"whirlpoolConfig",
|
||||
],
|
||||
&["whirlpoolsConfig", "config", "configAccount", "whirlpoolConfig"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let creator = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&["funder", "creator", "payer", "user", "owner"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
if instruction_kind == KbOrcaWhirlpoolsInstructionKind::InitializePool
|
||||
|| instruction_kind == KbOrcaWhirlpoolsInstructionKind::InitializePoolV2
|
||||
{
|
||||
let used_v2 =
|
||||
instruction_kind == KbOrcaWhirlpoolsInstructionKind::InitializePoolV2;
|
||||
let used_v2 = instruction_kind == KbOrcaWhirlpoolsInstructionKind::InitializePoolV2;
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "orca_whirlpools",
|
||||
"eventKind": "create_pool",
|
||||
@@ -268,7 +242,7 @@ impl KbOrcaWhirlpoolsDecoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,17 +269,13 @@ fn kb_classify_instruction_kind(
|
||||
return KbOrcaWhirlpoolsInstructionKind::Swap;
|
||||
}
|
||||
}
|
||||
if kb_value_contains_any_key(
|
||||
parsed_json,
|
||||
&["tokenProgramA", "tokenProgramB", "memoProgram"],
|
||||
) && kb_log_messages_contain_keyword(log_messages, "initialize_pool")
|
||||
if kb_value_contains_any_key(parsed_json, &["tokenProgramA", "tokenProgramB", "memoProgram"])
|
||||
&& kb_log_messages_contain_keyword(log_messages, "initialize_pool")
|
||||
{
|
||||
return KbOrcaWhirlpoolsInstructionKind::InitializePoolV2;
|
||||
}
|
||||
if kb_value_contains_any_key(
|
||||
parsed_json,
|
||||
&["tokenProgramA", "tokenProgramB", "memoProgram"],
|
||||
) && kb_log_messages_contain_keyword(log_messages, "swap")
|
||||
if kb_value_contains_any_key(parsed_json, &["tokenProgramA", "tokenProgramB", "memoProgram"])
|
||||
&& kb_log_messages_contain_keyword(log_messages, "swap")
|
||||
{
|
||||
return KbOrcaWhirlpoolsInstructionKind::SwapV2;
|
||||
}
|
||||
@@ -327,7 +297,7 @@ fn kb_classify_instruction_kind(
|
||||
if kb_log_messages_contain_keyword(log_messages, "swap") {
|
||||
return KbOrcaWhirlpoolsInstructionKind::Swap;
|
||||
}
|
||||
KbOrcaWhirlpoolsInstructionKind::Unknown
|
||||
return KbOrcaWhirlpoolsInstructionKind::Unknown;
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
@@ -355,13 +325,10 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(
|
||||
log_messages: &[std::string::String],
|
||||
keyword: &str,
|
||||
) -> bool {
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
let keyword_normalized = kb_normalize_text(keyword);
|
||||
for log_message in log_messages {
|
||||
let log_normalized = kb_normalize_text(log_message.as_str());
|
||||
@@ -369,7 +336,7 @@ fn kb_log_messages_contain_keyword(
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
@@ -379,7 +346,7 @@ fn kb_normalize_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -393,7 +360,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -402,7 +369,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -414,11 +381,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +399,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -466,7 +435,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key(
|
||||
@@ -477,13 +446,10 @@ fn kb_value_contains_any_key(
|
||||
Some(value) => value,
|
||||
None => return false,
|
||||
};
|
||||
kb_value_contains_any_key_inner(value, candidate_keys)
|
||||
return kb_value_contains_any_key_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_value_contains_any_key_inner(
|
||||
value: &serde_json::Value,
|
||||
candidate_keys: &[&str],
|
||||
) -> bool {
|
||||
fn kb_value_contains_any_key_inner(value: &serde_json::Value, candidate_keys: &[&str]) -> bool {
|
||||
if let Some(object) = value.as_object() {
|
||||
for candidate_key in candidate_keys {
|
||||
if object.contains_key(*candidate_key) {
|
||||
@@ -504,7 +470,7 @@ fn kb_value_contains_any_key_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -514,19 +480,17 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_infer_trade_side(
|
||||
log_messages: &[std::string::String],
|
||||
) -> crate::KbSwapTradeSide {
|
||||
fn kb_infer_trade_side(log_messages: &[std::string::String]) -> crate::KbSwapTradeSide {
|
||||
if kb_log_messages_contain_keyword(log_messages, "buy") {
|
||||
return crate::KbSwapTradeSide::BuyBase;
|
||||
}
|
||||
if kb_log_messages_contain_keyword(log_messages, "sell") {
|
||||
return crate::KbSwapTradeSide::SellBase;
|
||||
}
|
||||
crate::KbSwapTradeSide::Unknown
|
||||
return crate::KbSwapTradeSide::Unknown;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -556,7 +520,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(601);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_create_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -595,7 +559,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(602);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_transaction() -> crate::KbChainTransactionDto {
|
||||
@@ -623,7 +587,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(603);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_swap_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -656,7 +620,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(604);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -682,10 +646,10 @@ mod tests {
|
||||
);
|
||||
assert_eq!(event.config_account, Some("OrcaConfig111".to_string()));
|
||||
assert!(event.used_v2);
|
||||
}
|
||||
},
|
||||
crate::KbOrcaWhirlpoolsDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,10 +675,10 @@ mod tests {
|
||||
Some("So11111111111111111111111111111111111111112".to_string())
|
||||
);
|
||||
assert!(event.used_v2);
|
||||
}
|
||||
},
|
||||
crate::KbOrcaWhirlpoolsDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ pub struct KbPumpFunDecoder;
|
||||
impl KbPumpFunDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Pump.fun events.
|
||||
@@ -97,7 +97,7 @@ impl KbPumpFunDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
if transaction.err_json.is_some() {
|
||||
return Ok(std::vec::Vec::new());
|
||||
@@ -111,7 +111,7 @@ impl KbPumpFunDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let has_create_v2_log = kb_log_messages_contain_keyword(&log_messages, "create_v2")
|
||||
@@ -250,7 +250,7 @@ impl KbPumpFunDecoder {
|
||||
},
|
||||
));
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,8 +271,8 @@ fn kb_decode_optional_instruction_data(
|
||||
};
|
||||
let decode_result = bs58::decode(encoded.as_str()).into_vec();
|
||||
match decode_result {
|
||||
Ok(decoded) => Some(decoded),
|
||||
Err(_) => None,
|
||||
Ok(decoded) => return Some(decoded),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ fn kb_instruction_data_starts_with(
|
||||
if instruction_data.len() < discriminator.len() {
|
||||
return false;
|
||||
}
|
||||
&instruction_data[0..discriminator.len()] == discriminator
|
||||
return &instruction_data[0..discriminator.len()] == discriminator;
|
||||
}
|
||||
|
||||
fn kb_extract_u64_argument(
|
||||
@@ -304,7 +304,7 @@ fn kb_extract_u64_argument(
|
||||
}
|
||||
let mut bytes = [0u8; 8];
|
||||
bytes.copy_from_slice(&instruction_data[offset..end]);
|
||||
Some(u64::from_le_bytes(bytes).to_string())
|
||||
return Some(u64::from_le_bytes(bytes).to_string());
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
@@ -332,7 +332,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -343,7 +343,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -357,7 +357,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -366,7 +366,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -376,7 +376,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_normalize_log_text(value: &str) -> std::string::String {
|
||||
@@ -386,7 +386,7 @@ fn kb_normalize_log_text(value: &str) -> std::string::String {
|
||||
normalized.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
normalized
|
||||
return normalized;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -416,7 +416,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(91);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -445,7 +445,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
dto.id = Some(17);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -470,13 +470,13 @@ mod tests {
|
||||
Some("AssociatedBondingCurve111".to_string())
|
||||
);
|
||||
assert_eq!(event.creator, Some("Creator111".to_string()));
|
||||
}
|
||||
},
|
||||
crate::KbPumpFunDecodedEvent::BuyTrade(_) => {
|
||||
panic!("unexpected pump_fun buy trade event");
|
||||
}
|
||||
},
|
||||
crate::KbPumpFunDecodedEvent::SellTrade(_) => {
|
||||
panic!("unexpected pump_fun sell trade event");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ pub struct KbPumpSwapDecoder;
|
||||
impl KbPumpSwapDecoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more PumpSwap events.
|
||||
@@ -63,7 +63,7 @@ impl KbPumpSwapDecoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -74,7 +74,7 @@ impl KbPumpSwapDecoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let mut decoded_events = std::vec::Vec::new();
|
||||
@@ -110,40 +110,20 @@ impl KbPumpSwapDecoder {
|
||||
parsed_json.as_ref(),
|
||||
&["pool", "poolAccount", "amm", "ammPool", "poolState"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 0));
|
||||
.or_else(|| return kb_extract_account(&accounts, 0));
|
||||
let token_a_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenAMint",
|
||||
"baseMint",
|
||||
"mintA",
|
||||
"coinMint",
|
||||
"token0Mint",
|
||||
"mint0",
|
||||
],
|
||||
&["tokenAMint", "baseMint", "mintA", "coinMint", "token0Mint", "mint0"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 3));
|
||||
.or_else(|| return kb_extract_account(&accounts, 3));
|
||||
let token_b_mint = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"tokenBMint",
|
||||
"quoteMint",
|
||||
"mintB",
|
||||
"pcMint",
|
||||
"token1Mint",
|
||||
"mint1",
|
||||
],
|
||||
&["tokenBMint", "quoteMint", "mintB", "pcMint", "token1Mint", "mint1"],
|
||||
)
|
||||
.or_else(|| kb_extract_account(&accounts, 4));
|
||||
.or_else(|| return kb_extract_account(&accounts, 4));
|
||||
let pool_v2 = kb_extract_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"poolV2",
|
||||
"pool_v2",
|
||||
"ammV2",
|
||||
"bondingCurveV2",
|
||||
"bonding_curve_v2",
|
||||
],
|
||||
&["poolV2", "pool_v2", "ammV2", "bondingCurveV2", "bonding_curve_v2"],
|
||||
);
|
||||
let pool_base_token_account = kb_extract_account(&accounts, 7);
|
||||
let pool_quote_token_account = kb_extract_account(&accounts, 8);
|
||||
@@ -200,7 +180,7 @@ impl KbPumpSwapDecoder {
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +210,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages;
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
@@ -241,7 +221,7 @@ fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -255,7 +235,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -264,7 +244,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -274,7 +254,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone());
|
||||
}
|
||||
|
||||
fn kb_parse_optional_parsed_json(
|
||||
@@ -286,11 +266,13 @@ fn kb_parse_optional_parsed_json(
|
||||
};
|
||||
let value_result = serde_json::from_str::<serde_json::Value>(parsed_json.as_str());
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot parse instruction parsed_json '{}': {}",
|
||||
parsed_json, error
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +284,7 @@ fn kb_extract_string_by_candidate_keys(
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
kb_extract_string_by_candidate_keys_inner(value, candidate_keys)
|
||||
return kb_extract_string_by_candidate_keys_inner(value, candidate_keys);
|
||||
}
|
||||
|
||||
fn kb_extract_string_by_candidate_keys_inner(
|
||||
@@ -337,7 +319,7 @@ fn kb_extract_string_by_candidate_keys_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -367,7 +349,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(92);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
fn make_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -389,7 +371,8 @@ mod tests {
|
||||
"UserQuoteAta111",
|
||||
"PoolBaseVault111",
|
||||
"PoolQuoteVault111"
|
||||
]).to_string(),
|
||||
])
|
||||
.to_string(),
|
||||
None,
|
||||
None,
|
||||
Some(
|
||||
@@ -405,7 +388,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
dto.id = Some(18);
|
||||
dto
|
||||
return dto;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -436,10 +419,10 @@ mod tests {
|
||||
Some(&serde_json::Value::String("PoolQuoteVault111".to_string()))
|
||||
);
|
||||
assert_eq!(event.trade_side, crate::KbSwapTradeSide::BuyBase);
|
||||
}
|
||||
},
|
||||
crate::KbPumpSwapDecodedEvent::SellTrade(_) => {
|
||||
panic!("unexpected sell event")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ pub struct KbRaydiumAmmV4Decoder;
|
||||
impl KbRaydiumAmmV4Decoder {
|
||||
/// Creates a new decoder.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
return Self;
|
||||
}
|
||||
|
||||
/// Decodes one projected transaction into zero or more Raydium AmmV4 events.
|
||||
@@ -61,7 +61,7 @@ impl KbRaydiumAmmV4Decoder {
|
||||
"chain transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
@@ -72,7 +72,7 @@ impl KbRaydiumAmmV4Decoder {
|
||||
"cannot parse transaction_json for signature '{}': {}",
|
||||
transaction.signature, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let log_messages = kb_extract_log_messages(&transaction_json);
|
||||
let has_initialize2_log = kb_log_messages_contain_initialize2(&log_messages);
|
||||
@@ -139,7 +139,7 @@ impl KbRaydiumAmmV4Decoder {
|
||||
},
|
||||
));
|
||||
}
|
||||
Ok(decoded_events)
|
||||
return Ok(decoded_events)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ fn kb_extract_log_messages(
|
||||
messages.push(text.to_string());
|
||||
}
|
||||
}
|
||||
messages
|
||||
return messages
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_initialize2(log_messages: &[std::string::String]) -> bool {
|
||||
@@ -177,7 +177,7 @@ fn kb_log_messages_contain_initialize2(log_messages: &[std::string::String]) ->
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
return false
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -191,7 +191,7 @@ fn kb_parse_accounts_json(
|
||||
"cannot parse instruction accounts_json '{}': {}",
|
||||
accounts_json, error
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut accounts = std::vec::Vec::new();
|
||||
for value in values {
|
||||
@@ -200,7 +200,7 @@ fn kb_parse_accounts_json(
|
||||
accounts.push(text.to_string());
|
||||
}
|
||||
}
|
||||
Ok(accounts)
|
||||
return Ok(accounts)
|
||||
}
|
||||
|
||||
fn kb_extract_account(
|
||||
@@ -210,7 +210,7 @@ fn kb_extract_account(
|
||||
if index >= accounts.len() {
|
||||
return None;
|
||||
}
|
||||
Some(accounts[index].clone())
|
||||
return Some(accounts[index].clone())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -240,7 +240,7 @@ mod tests {
|
||||
.to_string(),
|
||||
);
|
||||
dto.id = Some(42);
|
||||
dto
|
||||
return dto
|
||||
}
|
||||
|
||||
fn make_instruction() -> crate::KbChainInstructionDto {
|
||||
@@ -277,7 +277,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
dto.id = Some(7);
|
||||
dto
|
||||
return dto
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -300,7 +300,7 @@ mod tests {
|
||||
assert_eq!(event.token_a_mint, Some("TokenA111".to_string()));
|
||||
assert_eq!(event.token_b_mint, Some("TokenB111".to_string()));
|
||||
assert_eq!(event.market_account, Some("Market111".to_string()));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,28 +20,28 @@ impl KbRaydiumClmmDecodedEvent {
|
||||
/// Returns the normalized event kind.
|
||||
pub fn event_kind(&self) -> &'static str {
|
||||
match self {
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(_) => "raydium_clmm.swap_v2",
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(_) => return "raydium_clmm.swap_v2",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pool account.
|
||||
pub fn pool_account(&self) -> &str {
|
||||
match self {
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => event.pool_state.as_str(),
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => return event.pool_state.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the normalized base mint.
|
||||
pub fn base_mint(&self) -> &str {
|
||||
match self {
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => event.base_mint.as_str(),
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => return event.base_mint.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the normalized quote mint.
|
||||
pub fn quote_mint(&self) -> &str {
|
||||
match self {
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => event.quote_mint.as_str(),
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => return event.quote_mint.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ impl KbRaydiumClmmDecodedEvent {
|
||||
crate::KbRaydiumClmmDecodedEvent::SwapV2(event) => {
|
||||
let result = serde_json::to_string(event);
|
||||
match result {
|
||||
Ok(payload_json) => Some(payload_json),
|
||||
Err(_) => None,
|
||||
Ok(payload_json) => return Some(payload_json),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ pub fn kb_decode_raydium_clmm_instruction(
|
||||
None => return decoded,
|
||||
};
|
||||
decoded.push(crate::KbRaydiumClmmDecodedEvent::SwapV2(event));
|
||||
decoded
|
||||
return decoded;
|
||||
}
|
||||
|
||||
fn kb_decode_swap_v2(
|
||||
@@ -219,7 +219,7 @@ fn kb_decode_swap_v2(
|
||||
quote_vault = input_vault.clone();
|
||||
trade_side = "BuyBase".to_string();
|
||||
}
|
||||
Some(crate::KbRaydiumClmmSwapV2Decoded {
|
||||
return Some(crate::KbRaydiumClmmSwapV2Decoded {
|
||||
payer,
|
||||
amm_config,
|
||||
pool_state,
|
||||
@@ -239,7 +239,7 @@ fn kb_decode_swap_v2(
|
||||
other_amount_threshold,
|
||||
sqrt_price_limit_x64: sqrt_price_limit_x64.to_string(),
|
||||
is_base_input,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn kb_clone_account(
|
||||
@@ -248,8 +248,8 @@ fn kb_clone_account(
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let account_option = accounts.get(index);
|
||||
match account_option {
|
||||
Some(account) => Some(account.clone()),
|
||||
None => None,
|
||||
Some(account) => return Some(account.clone()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ fn kb_read_discriminator(data: &[u8]) -> std::option::Option<[u8; 8]> {
|
||||
bytes[index] = data[index];
|
||||
index += 1;
|
||||
}
|
||||
Some(bytes)
|
||||
return Some(bytes);
|
||||
}
|
||||
|
||||
fn kb_read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
|
||||
@@ -276,7 +276,7 @@ fn kb_read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
|
||||
bytes[index] = data[offset + index];
|
||||
index += 1;
|
||||
}
|
||||
Some(u64::from_le_bytes(bytes))
|
||||
return Some(u64::from_le_bytes(bytes));
|
||||
}
|
||||
|
||||
fn kb_read_u128_le(data: &[u8], offset: usize) -> std::option::Option<u128> {
|
||||
@@ -289,7 +289,7 @@ fn kb_read_u128_le(data: &[u8], offset: usize) -> std::option::Option<u128> {
|
||||
bytes[index] = data[offset + index];
|
||||
index += 1;
|
||||
}
|
||||
Some(u128::from_le_bytes(bytes))
|
||||
return Some(u128::from_le_bytes(bytes));
|
||||
}
|
||||
|
||||
fn kb_read_bool(data: &[u8], offset: usize) -> std::option::Option<bool> {
|
||||
@@ -297,9 +297,9 @@ fn kb_read_bool(data: &[u8], offset: usize) -> std::option::Option<bool> {
|
||||
return None;
|
||||
}
|
||||
match data[offset] {
|
||||
0 => Some(false),
|
||||
1 => Some(true),
|
||||
_ => None,
|
||||
0 => return Some(false),
|
||||
1 => return Some(true),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,13 +349,13 @@ fn kb_decode_base58(input: &str) -> std::option::Option<std::vec::Vec<u8>> {
|
||||
for byte in bytes {
|
||||
result.push(byte);
|
||||
}
|
||||
Some(result)
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn sample_swap_v2_accounts_json() -> &'static str {
|
||||
r#"[
|
||||
return r#"[
|
||||
"8NQ32SyFKD1d5kenq4oM8Da6C6J9TQSMW1uAgFRveEQr",
|
||||
"A1BBtTYJd4i3xU8D6Tc2FzU6ZN4oXZWXKZnCxwbHXr8x",
|
||||
"GUrRxvnWVQSnbcz1eP9D5BqXwPZtRhmrqVfm5wY9meWR",
|
||||
@@ -371,7 +371,7 @@ mod tests {
|
||||
"7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs",
|
||||
"8ovxZR2Gv9Mr73aoXLQYTMaZvHCSpnEohzgjVHQwmyHr",
|
||||
"9MssDxndh2Rn8DmGWL94hXVv22zxfDYHV7tvzfPgcaWe"
|
||||
]"#
|
||||
]"#;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -388,48 +388,21 @@ mod tests {
|
||||
events[0].pool_account(),
|
||||
"GUrRxvnWVQSnbcz1eP9D5BqXwPZtRhmrqVfm5wY9meWR"
|
||||
);
|
||||
assert_eq!(
|
||||
event.pool_state,
|
||||
"GUrRxvnWVQSnbcz1eP9D5BqXwPZtRhmrqVfm5wY9meWR"
|
||||
);
|
||||
assert_eq!(
|
||||
event.input_vault,
|
||||
"AvRzvwpSVnxsinLGQS3vZLqkZxhXZDM8F2qKccAo7rSq"
|
||||
);
|
||||
assert_eq!(
|
||||
event.output_vault,
|
||||
"CTkc4xDrpzjWcFLC1cxmUZZjZLSRV46HZa8wu5eKTbuh"
|
||||
);
|
||||
assert_eq!(
|
||||
event.input_vault_mint,
|
||||
"CKvjP8FrZpaKXjASEtX2nEU9w7M4RKskfnLQbKJBodV"
|
||||
);
|
||||
assert_eq!(
|
||||
event.output_vault_mint,
|
||||
"7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"
|
||||
);
|
||||
assert_eq!(
|
||||
event.base_mint,
|
||||
"7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"
|
||||
);
|
||||
assert_eq!(
|
||||
event.quote_mint,
|
||||
"CKvjP8FrZpaKXjASEtX2nEU9w7M4RKskfnLQbKJBodV"
|
||||
);
|
||||
assert_eq!(
|
||||
event.base_vault,
|
||||
"CTkc4xDrpzjWcFLC1cxmUZZjZLSRV46HZa8wu5eKTbuh"
|
||||
);
|
||||
assert_eq!(
|
||||
event.quote_vault,
|
||||
"AvRzvwpSVnxsinLGQS3vZLqkZxhXZDM8F2qKccAo7rSq"
|
||||
);
|
||||
assert_eq!(event.pool_state, "GUrRxvnWVQSnbcz1eP9D5BqXwPZtRhmrqVfm5wY9meWR");
|
||||
assert_eq!(event.input_vault, "AvRzvwpSVnxsinLGQS3vZLqkZxhXZDM8F2qKccAo7rSq");
|
||||
assert_eq!(event.output_vault, "CTkc4xDrpzjWcFLC1cxmUZZjZLSRV46HZa8wu5eKTbuh");
|
||||
assert_eq!(event.input_vault_mint, "CKvjP8FrZpaKXjASEtX2nEU9w7M4RKskfnLQbKJBodV");
|
||||
assert_eq!(event.output_vault_mint, "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs");
|
||||
assert_eq!(event.base_mint, "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs");
|
||||
assert_eq!(event.quote_mint, "CKvjP8FrZpaKXjASEtX2nEU9w7M4RKskfnLQbKJBodV");
|
||||
assert_eq!(event.base_vault, "CTkc4xDrpzjWcFLC1cxmUZZjZLSRV46HZa8wu5eKTbuh");
|
||||
assert_eq!(event.quote_vault, "AvRzvwpSVnxsinLGQS3vZLqkZxhXZDM8F2qKccAo7rSq");
|
||||
assert_eq!(event.trade_side, "BuyBase");
|
||||
assert_eq!(event.amount, 148441657491969);
|
||||
assert_eq!(event.other_amount_threshold, 0);
|
||||
assert_eq!(event.sqrt_price_limit_x64, "0");
|
||||
assert_eq!(event.is_base_input, true);
|
||||
}
|
||||
assert!(event.is_base_input);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,15 +449,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ignores_legacy_swap_for_now() {
|
||||
let mut data = std::vec::Vec::<u8>::new();
|
||||
data.push(248);
|
||||
data.push(198);
|
||||
data.push(158);
|
||||
data.push(145);
|
||||
data.push(225);
|
||||
data.push(117);
|
||||
data.push(135);
|
||||
data.push(200);
|
||||
let mut data: std::vec::Vec<u8> = vec![248, 198, 158, 145, 225, 117, 135, 200];
|
||||
while data.len() < 41 {
|
||||
data.push(0);
|
||||
}
|
||||
|
||||
@@ -91,32 +91,32 @@ impl KbRaydiumCpmmDecodedEvent {
|
||||
/// Returns the storage event kind.
|
||||
pub fn event_kind(&self) -> &'static str {
|
||||
match self {
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(_) => "raydium_cpmm.swap_base_input",
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(_) => "raydium_cpmm.swap_base_output",
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(_) => return "raydium_cpmm.swap_base_input",
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(_) => return "raydium_cpmm.swap_base_output",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pool account.
|
||||
pub fn pool_account(&self) -> &str {
|
||||
match self {
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => event.pool_state.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => event.pool_state.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.pool_state.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.pool_state.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the normalized base mint.
|
||||
pub fn base_mint(&self) -> &str {
|
||||
match self {
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => event.base_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => event.base_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.base_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.base_mint.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the normalized quote mint.
|
||||
pub fn quote_mint(&self) -> &str {
|
||||
match self {
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => event.quote_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => event.quote_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.quote_mint.as_str(),
|
||||
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.quote_mint.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,17 +126,17 @@ impl KbRaydiumCpmmDecodedEvent {
|
||||
crate::KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
|
||||
let result = serde_json::to_string(event);
|
||||
match result {
|
||||
Ok(payload) => Some(payload),
|
||||
Err(_) => None,
|
||||
Ok(payload) => return Some(payload),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
crate::KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
|
||||
let result = serde_json::to_string(event);
|
||||
match result {
|
||||
Ok(payload) => Some(payload),
|
||||
Err(_) => None,
|
||||
Ok(payload) => return Some(payload),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,9 +161,7 @@ pub fn kb_decode_raydium_cpmm_instruction(
|
||||
if data.len() < 24 {
|
||||
return std::vec::Vec::new();
|
||||
}
|
||||
let discriminator = [
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
];
|
||||
let discriminator = [data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]];
|
||||
if discriminator == KB_RAYDIUM_CPMM_SWAP_BASE_INPUT_DISCRIMINATOR {
|
||||
let amount_in = match kb_read_u64_le(data.as_slice(), 8) {
|
||||
Some(value) => value,
|
||||
@@ -208,7 +206,7 @@ pub fn kb_decode_raydium_cpmm_instruction(
|
||||
};
|
||||
return vec![KbRaydiumCpmmDecodedEvent::SwapBaseOutput(swap)];
|
||||
}
|
||||
std::vec::Vec::new()
|
||||
return std::vec::Vec::new();
|
||||
}
|
||||
|
||||
fn kb_build_raydium_cpmm_swap(
|
||||
@@ -233,12 +231,8 @@ fn kb_build_raydium_cpmm_swap(
|
||||
output_vault.as_str(),
|
||||
);
|
||||
let input_is_base = normalized.input_is_base;
|
||||
let trade_side = if input_is_base {
|
||||
"sell".to_string()
|
||||
} else {
|
||||
"buy".to_string()
|
||||
};
|
||||
Some(KbRaydiumCpmmSwapDecoded {
|
||||
let trade_side = if input_is_base { "sell".to_string() } else { "buy".to_string() };
|
||||
return Some(KbRaydiumCpmmSwapDecoded {
|
||||
swap_mode,
|
||||
payer: accounts[0].clone(),
|
||||
authority: accounts[1].clone(),
|
||||
@@ -263,7 +257,7 @@ fn kb_build_raydium_cpmm_swap(
|
||||
minimum_amount_out_raw,
|
||||
max_amount_in_raw,
|
||||
amount_out_raw,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
struct KbRaydiumCpmmNormalizedPair {
|
||||
@@ -307,13 +301,13 @@ fn kb_normalize_raydium_cpmm_pair(
|
||||
input_is_base: true,
|
||||
};
|
||||
}
|
||||
KbRaydiumCpmmNormalizedPair {
|
||||
return KbRaydiumCpmmNormalizedPair {
|
||||
base_mint: output_mint.to_string(),
|
||||
quote_mint: input_mint.to_string(),
|
||||
base_vault: output_vault.to_string(),
|
||||
quote_vault: input_vault.to_string(),
|
||||
input_is_base: false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn kb_is_quote_mint(mint: &str) -> bool {
|
||||
@@ -329,7 +323,7 @@ fn kb_is_quote_mint(mint: &str) -> bool {
|
||||
if mint == "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB" {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
fn kb_parse_accounts_json(
|
||||
@@ -337,16 +331,15 @@ fn kb_parse_accounts_json(
|
||||
) -> std::option::Option<std::vec::Vec<std::string::String>> {
|
||||
let result = serde_json::from_str::<std::vec::Vec<std::string::String>>(accounts_json);
|
||||
match result {
|
||||
Ok(accounts) => Some(accounts),
|
||||
Err(_) => None,
|
||||
Ok(accounts) => return Some(accounts),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
fn kb_parse_data_json_as_base58(data_json: &str) -> std::option::Option<std::string::String> {
|
||||
let json_string_result = serde_json::from_str::<std::string::String>(data_json);
|
||||
match json_string_result {
|
||||
Ok(value) => return Some(value),
|
||||
Err(_) => {}
|
||||
if let Ok(value) = json_string_result {
|
||||
return Some(value);
|
||||
}
|
||||
let trimmed = data_json.trim();
|
||||
if trimmed.is_empty() {
|
||||
@@ -356,7 +349,7 @@ fn kb_parse_data_json_as_base58(data_json: &str) -> std::option::Option<std::str
|
||||
if without_quotes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(without_quotes.to_string())
|
||||
return Some(without_quotes.to_string());
|
||||
}
|
||||
|
||||
fn kb_read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
|
||||
@@ -373,7 +366,7 @@ fn kb_read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
|
||||
data[offset + 6],
|
||||
data[offset + 7],
|
||||
];
|
||||
Some(u64::from_le_bytes(bytes))
|
||||
return Some(u64::from_le_bytes(bytes));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -402,26 +395,17 @@ mod tests {
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
crate::KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
|
||||
assert_eq!(
|
||||
event.pool_state,
|
||||
"2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV"
|
||||
);
|
||||
assert_eq!(
|
||||
event.base_mint,
|
||||
"Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk"
|
||||
);
|
||||
assert_eq!(
|
||||
event.quote_mint,
|
||||
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"
|
||||
);
|
||||
assert_eq!(event.input_is_base, true);
|
||||
assert_eq!(event.pool_state, "2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV");
|
||||
assert_eq!(event.base_mint, "Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk");
|
||||
assert_eq!(event.quote_mint, "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
|
||||
assert!(event.input_is_base);
|
||||
assert_eq!(event.trade_side, "sell");
|
||||
assert_eq!(event.amount_in_raw.is_some(), true);
|
||||
assert_eq!(event.minimum_amount_out_raw.is_some(), true);
|
||||
}
|
||||
assert!(event.amount_in_raw.is_some());
|
||||
assert!(event.minimum_amount_out_raw.is_some());
|
||||
},
|
||||
_ => {
|
||||
panic!("expected swap base input");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -448,26 +432,17 @@ mod tests {
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
crate::KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
|
||||
assert_eq!(
|
||||
event.pool_state,
|
||||
"2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV"
|
||||
);
|
||||
assert_eq!(
|
||||
event.base_mint,
|
||||
"Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk"
|
||||
);
|
||||
assert_eq!(
|
||||
event.quote_mint,
|
||||
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"
|
||||
);
|
||||
assert_eq!(event.input_is_base, false);
|
||||
assert_eq!(event.pool_state, "2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV");
|
||||
assert_eq!(event.base_mint, "Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk");
|
||||
assert_eq!(event.quote_mint, "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
|
||||
assert!(!event.input_is_base);
|
||||
assert_eq!(event.trade_side, "buy");
|
||||
assert_eq!(event.max_amount_in_raw.is_some(), true);
|
||||
assert_eq!(event.amount_out_raw.is_some(), true);
|
||||
}
|
||||
assert!(event.max_amount_in_raw.is_some());
|
||||
assert!(event.amount_out_raw.is_some());
|
||||
},
|
||||
_ => {
|
||||
panic!("expected swap base output");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -33,36 +33,18 @@ pub enum KbError {
|
||||
impl std::fmt::Display for KbError {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Config(message) => {
|
||||
write!(formatter, "configuration error: {message}")
|
||||
}
|
||||
Self::Io(message) => {
|
||||
write!(formatter, "io error: {message}")
|
||||
}
|
||||
Self::Json(message) => {
|
||||
write!(formatter, "json error: {message}")
|
||||
}
|
||||
Self::Tracing(message) => {
|
||||
write!(formatter, "tracing error: {message}")
|
||||
}
|
||||
Self::Http(message) => {
|
||||
write!(formatter, "http error: {message}")
|
||||
}
|
||||
Self::Ws(message) => {
|
||||
write!(formatter, "websocket error: {message}")
|
||||
}
|
||||
Self::Db(message) => {
|
||||
write!(formatter, "db error: {}", message)
|
||||
}
|
||||
Self::InvalidState(message) => {
|
||||
write!(formatter, "invalid state: {message}")
|
||||
}
|
||||
Self::NotConnected(message) => {
|
||||
write!(formatter, "not connected: {message}")
|
||||
}
|
||||
Self::Config(message) => return write!(formatter, "configuration error: {message}"),
|
||||
Self::Io(message) => return write!(formatter, "io error: {message}"),
|
||||
Self::Json(message) => return write!(formatter, "json error: {message}"),
|
||||
Self::Tracing(message) => return write!(formatter, "tracing error: {message}"),
|
||||
Self::Http(message) => return write!(formatter, "http error: {message}"),
|
||||
Self::Ws(message) => return write!(formatter, "websocket error: {message}"),
|
||||
Self::Db(message) => return write!(formatter, "db error: {}", message),
|
||||
Self::InvalidState(message) => return write!(formatter, "invalid state: {message}"),
|
||||
Self::NotConnected(message) => return write!(formatter, "not connected: {message}"),
|
||||
Self::NotImplemented(message) => {
|
||||
write!(formatter, "not implemented: {message}")
|
||||
}
|
||||
return write!(formatter, "not implemented: {message}");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,10 +44,10 @@ impl HttpEndpointPool {
|
||||
"http endpoint pool requires at least one client".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(Self {
|
||||
return Ok(Self {
|
||||
clients,
|
||||
next_index: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates a pool from endpoint configurations.
|
||||
@@ -66,22 +66,22 @@ impl HttpEndpointPool {
|
||||
};
|
||||
clients.push(client);
|
||||
}
|
||||
Self::new(clients)
|
||||
return Self::new(clients);
|
||||
}
|
||||
|
||||
/// Creates a pool from the global configuration.
|
||||
pub fn from_config(config: &crate::KbConfig) -> Result<Self, crate::KbError> {
|
||||
Self::from_endpoint_configs(config.solana.http_endpoints.clone())
|
||||
return Self::from_endpoint_configs(config.solana.http_endpoints.clone());
|
||||
}
|
||||
|
||||
/// Returns the number of pooled clients.
|
||||
pub fn len(&self) -> usize {
|
||||
self.clients.len()
|
||||
return self.clients.len();
|
||||
}
|
||||
|
||||
/// Returns whether the pool is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.clients.is_empty()
|
||||
return self.clients.is_empty();
|
||||
}
|
||||
|
||||
/// Returns a live snapshot of pooled endpoints.
|
||||
@@ -94,7 +94,7 @@ impl HttpEndpointPool {
|
||||
crate::KbHttpEndpointStatus::Disabled => ("Disabled".to_string(), None),
|
||||
crate::KbHttpEndpointStatus::Paused { remaining_ms } => {
|
||||
("Paused".to_string(), Some(remaining_ms))
|
||||
}
|
||||
},
|
||||
};
|
||||
snapshots.push(KbHttpPoolClientSnapshot {
|
||||
endpoint_name: client.endpoint_name().to_string(),
|
||||
@@ -106,7 +106,7 @@ impl HttpEndpointPool {
|
||||
available_concurrency_slots: client.available_concurrency_slots(),
|
||||
});
|
||||
}
|
||||
snapshots
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
/// Selects one client for a role and method.
|
||||
@@ -134,13 +134,13 @@ impl HttpEndpointPool {
|
||||
match status {
|
||||
crate::KbHttpEndpointStatus::Active => {
|
||||
active_indices.push(index);
|
||||
}
|
||||
},
|
||||
crate::KbHttpEndpointStatus::Paused { .. } => {
|
||||
paused_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbHttpEndpointStatus::Disabled => {
|
||||
disabled_count += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
@@ -150,9 +150,7 @@ impl HttpEndpointPool {
|
||||
required_role, method, paused_count, disabled_count, role_mismatch_count
|
||||
)));
|
||||
}
|
||||
let rotation_seed = self
|
||||
.next_index
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let rotation_seed = self.next_index.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let active_len = active_indices.len();
|
||||
let mut offset = 0usize;
|
||||
while offset < active_len {
|
||||
@@ -164,7 +162,7 @@ impl HttpEndpointPool {
|
||||
offset += 1;
|
||||
}
|
||||
let fallback_index = active_indices[rotation_seed % active_len];
|
||||
Ok(self.clients[fallback_index].clone())
|
||||
return Ok(self.clients[fallback_index].clone());
|
||||
}
|
||||
|
||||
/// Executes one raw JSON-RPC request through the pool.
|
||||
@@ -174,14 +172,12 @@ impl HttpEndpointPool {
|
||||
method: std::string::String,
|
||||
params: std::vec::Vec<serde_json::Value>,
|
||||
) -> Result<serde_json::Value, crate::KbError> {
|
||||
let client_result = self
|
||||
.select_client_for_role_and_method(required_role, &method)
|
||||
.await;
|
||||
let client_result = self.select_client_for_role_and_method(required_role, &method).await;
|
||||
let client = match client_result {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client.execute_json_rpc_result_raw(method, params).await
|
||||
return client.execute_json_rpc_result_raw(method, params).await;
|
||||
}
|
||||
|
||||
/// Executes one typed JSON-RPC request through the pool.
|
||||
@@ -194,28 +190,25 @@ impl HttpEndpointPool {
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
let client_result = self
|
||||
.select_client_for_role_and_method(required_role, &method)
|
||||
.await;
|
||||
let client_result = self.select_client_for_role_and_method(required_role, &method).await;
|
||||
let client = match client_result {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client
|
||||
.execute_json_rpc_request_typed::<T>(method, params)
|
||||
.await
|
||||
return client.execute_json_rpc_request_typed::<T>(method, params).await;
|
||||
}
|
||||
/// Executes `getHealth` through the pool.
|
||||
pub async fn get_health_for_role(
|
||||
&self,
|
||||
required_role: &str,
|
||||
) -> Result<std::string::String, crate::KbError> {
|
||||
self.execute_json_rpc_request_typed_for_role::<std::string::String>(
|
||||
required_role,
|
||||
"getHealth".to_string(),
|
||||
std::vec::Vec::new(),
|
||||
)
|
||||
.await
|
||||
return self
|
||||
.execute_json_rpc_request_typed_for_role::<std::string::String>(
|
||||
required_role,
|
||||
"getHealth".to_string(),
|
||||
std::vec::Vec::new(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Executes `getSlot` through the pool.
|
||||
@@ -231,12 +224,13 @@ impl HttpEndpointPool {
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let params = kb_pool_build_optional_config_only_params(config_value);
|
||||
self.execute_json_rpc_request_typed_for_role::<u64>(
|
||||
required_role,
|
||||
"getSlot".to_string(),
|
||||
params,
|
||||
)
|
||||
.await
|
||||
return self
|
||||
.execute_json_rpc_request_typed_for_role::<u64>(
|
||||
required_role,
|
||||
"getSlot".to_string(),
|
||||
params,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Executes `sendTransaction` through the pool.
|
||||
@@ -254,12 +248,13 @@ impl HttpEndpointPool {
|
||||
};
|
||||
let params =
|
||||
kb_pool_build_first_string_optional_config_params(encoded_transaction, config_value);
|
||||
self.execute_json_rpc_request_typed_for_role::<std::string::String>(
|
||||
required_role,
|
||||
"sendTransaction".to_string(),
|
||||
params,
|
||||
)
|
||||
.await
|
||||
return self
|
||||
.execute_json_rpc_request_typed_for_role::<std::string::String>(
|
||||
required_role,
|
||||
"sendTransaction".to_string(),
|
||||
params,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Executes `getTransaction` through the pool and returns the raw result value.
|
||||
@@ -269,14 +264,29 @@ impl HttpEndpointPool {
|
||||
signature: std::string::String,
|
||||
config: std::option::Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value, crate::KbError> {
|
||||
let client_result = self
|
||||
.select_client_for_role_and_method(required_role, "getTransaction")
|
||||
.await;
|
||||
let client_result =
|
||||
self.select_client_for_role_and_method(required_role, "getTransaction").await;
|
||||
let client = match client_result {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client.get_transaction_raw(signature, config).await
|
||||
return client.get_transaction_raw(signature, config).await;
|
||||
}
|
||||
|
||||
/// Executes `getAccountInfo` through the pool and returns the raw result value.
|
||||
pub async fn get_account_info_raw_for_role(
|
||||
&self,
|
||||
required_role: &str,
|
||||
address: std::string::String,
|
||||
config: std::option::Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value, crate::KbError> {
|
||||
let client_result =
|
||||
self.select_client_for_role_and_method(required_role, "getAccountInfo").await;
|
||||
let client = match client_result {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
return client.get_account_info_raw(address, config).await;
|
||||
}
|
||||
|
||||
/// Executes `getProgramAccounts` through the pool and returns the raw result value.
|
||||
@@ -293,7 +303,7 @@ impl HttpEndpointPool {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client.get_program_accounts_raw(program_id, config).await
|
||||
return client.get_program_accounts_raw(program_id, config).await;
|
||||
}
|
||||
|
||||
/// Executes `getSignaturesForAddress` through the pool and returns the raw result value.
|
||||
@@ -310,7 +320,7 @@ impl HttpEndpointPool {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client.get_signatures_for_address_raw(address, config).await
|
||||
return client.get_signatures_for_address_raw(address, config).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getSignaturesForAddress` through the pool.
|
||||
@@ -330,7 +340,7 @@ impl HttpEndpointPool {
|
||||
Ok(client) => client,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
client.get_signatures_for_address(address, config).await
|
||||
return client.get_signatures_for_address(address, config).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +352,7 @@ fn kb_pool_build_optional_config_only_params(
|
||||
if let Some(config) = config {
|
||||
params.push(config);
|
||||
}
|
||||
params
|
||||
return params;
|
||||
}
|
||||
|
||||
fn kb_pool_build_first_string_optional_config_params(
|
||||
@@ -350,11 +360,10 @@ fn kb_pool_build_first_string_optional_config_params(
|
||||
config: std::option::Option<serde_json::Value>,
|
||||
) -> std::vec::Vec<serde_json::Value> {
|
||||
let mut params = vec![serde_json::Value::String(first)];
|
||||
|
||||
if let Some(config) = config {
|
||||
params.push(config);
|
||||
}
|
||||
params
|
||||
return params;
|
||||
}
|
||||
|
||||
fn kb_pool_serialize_optional_json_value<T>(
|
||||
@@ -368,14 +377,16 @@ where
|
||||
Some(value) => {
|
||||
let value_result = serde_json::to_value(value);
|
||||
match value_result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) => Err(crate::KbError::Json(format!(
|
||||
"cannot serialize {}: {error}",
|
||||
label
|
||||
))),
|
||||
Ok(value) => return Ok(Some(value)),
|
||||
Err(error) => {
|
||||
return Err(crate::KbError::Json(format!(
|
||||
"cannot serialize {}: {error}",
|
||||
label
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,11 +483,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
});
|
||||
Self {
|
||||
return Self {
|
||||
url: format!("http://{}", local_addr),
|
||||
shutdown_tx: Some(shutdown_tx),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async fn shutdown(mut self) {
|
||||
if let Some(shutdown_tx) = self.shutdown_tx.take() {
|
||||
let _ = shutdown_tx.send(());
|
||||
@@ -490,7 +502,7 @@ mod tests {
|
||||
url: std::string::String,
|
||||
roles: std::vec::Vec<std::string::String>,
|
||||
) -> crate::KbHttpEndpointConfig {
|
||||
crate::KbHttpEndpointConfig {
|
||||
return crate::KbHttpEndpointConfig {
|
||||
name: name.to_string(),
|
||||
enabled: true,
|
||||
provider: provider.to_string(),
|
||||
@@ -508,7 +520,7 @@ mod tests {
|
||||
max_idle_connections_per_host: 4,
|
||||
max_concurrent_requests_per_endpoint: 2,
|
||||
pause_after_http_429_ms: Some(1500),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -595,12 +607,8 @@ mod tests {
|
||||
.expect("pool creation must succeed");
|
||||
let snapshots = pool.snapshot().await;
|
||||
assert_eq!(snapshots.len(), 2);
|
||||
assert!(snapshots.iter().any(|snapshot| snapshot.status == "Active"));
|
||||
assert!(
|
||||
snapshots
|
||||
.iter()
|
||||
.any(|snapshot| snapshot.status == "Disabled")
|
||||
);
|
||||
assert!(snapshots.iter().any(|snapshot| return snapshot.status == "Active"));
|
||||
assert!(snapshots.iter().any(|snapshot| return snapshot.status == "Disabled"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -669,18 +677,16 @@ mod tests {
|
||||
client_a.disable().await;
|
||||
let pool =
|
||||
crate::HttpEndpointPool::new(vec![client_a]).expect("pool creation must succeed");
|
||||
let result = pool
|
||||
.select_client_for_role_and_method("http_queries", "getSlot")
|
||||
.await;
|
||||
let result = pool.select_client_for_role_and_method("http_queries", "getSlot").await;
|
||||
assert!(result.is_err());
|
||||
let error = result.expect_err("selection must fail");
|
||||
match error {
|
||||
crate::KbError::Http(message) => {
|
||||
assert!(message.contains("no active http endpoint available"));
|
||||
}
|
||||
},
|
||||
other => {
|
||||
panic!("unexpected error: {other:?}");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user