0.7.13
This commit is contained in:
@@ -43,3 +43,4 @@
|
|||||||
0.7.10 - Ajout du premier support Orca Whirlpools avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
0.7.10 - Ajout du premier support Orca Whirlpools avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
||||||
0.7.11 - Ajout du premier support FluxBeam avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
0.7.11 - Ajout du premier support FluxBeam avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
||||||
0.7.12 - Ajout du premier support DexLab Swap/Pool avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
0.7.12 - Ajout du premier support DexLab Swap/Pool avec décodage create_pool/swap, persistance des événements décodés et détection métier automatique pool/pair/listing
|
||||||
|
0.7.13 - Extension de la couche launch origins avec Bags et Moonit, ajout d’un enregistrement programmatique des mappings Bags, et détection automatique Moonit via suffixe de token mint
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.7.12"
|
version = "0.7.13"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
||||||
|
|||||||
14
ROADMAP.md
14
ROADMAP.md
@@ -578,15 +578,13 @@ Réalisé :
|
|||||||
- conservation d’une séparation entre pool DexLab natif et éventuel `OpenBook Market ID` créé ensuite.
|
- conservation d’une séparation entre pool DexLab natif et éventuel `OpenBook Market ID` créé ensuite.
|
||||||
|
|
||||||
### 6.045. Version `0.7.13` — Bags / Moonit comme origines de lancement
|
### 6.045. Version `0.7.13` — Bags / Moonit comme origines de lancement
|
||||||
Objectif : étendre la couche `launch origins` à d’autres surfaces au-dessus des protocoles DEX déjà intégrés.
|
Réalisé :
|
||||||
|
|
||||||
À faire :
|
- extension de la couche `launch origins` à `Bags` et `Moonit`,
|
||||||
|
- ajout d’un enregistrement programmatique des mappings `Bags` à partir des champs `tokenMint`, `dbcConfigKey`, `dbcPoolKey` et `dammV2PoolKey`,
|
||||||
- ajouter `Bags` comme surface de lancement détectable,
|
- prise en charge de l’attribution `Bags` par matching exact sur `config_account`, `pool_account` et `token_mint`,
|
||||||
- ajouter `Moonit` comme surface de lancement détectable,
|
- prise en charge de l’attribution `Moonit` par détection automatique des token mints se terminant par `moon`,
|
||||||
- relier ces surfaces aux pools et paires finalement créés,
|
- conservation d’une séparation stricte entre origine de lancement et protocole on-chain.
|
||||||
- conserver une séparation stricte entre origine de lancement et protocole on-chain,
|
|
||||||
- préparer l’extension future à d’autres launchpads ou surfaces dérivées.
|
|
||||||
|
|
||||||
### 6.046. Version `0.7.14` — Consolidation multi-DEX
|
### 6.046. Version `0.7.14` — Consolidation multi-DEX
|
||||||
Objectif : unifier le comportement des connecteurs DEX v1 avant l’ouverture des couches analytiques plus riches.
|
Objectif : unifier le comportement des connecteurs DEX v1 avant l’ouverture des couches analytiques plus riches.
|
||||||
|
|||||||
@@ -30,7 +30,154 @@ impl KbLaunchOriginService {
|
|||||||
/// Creates a new launch-origin service.
|
/// Creates a new launch-origin service.
|
||||||
pub fn new(database: std::sync::Arc<crate::KbDatabase>) -> Self {
|
pub fn new(database: std::sync::Arc<crate::KbDatabase>) -> Self {
|
||||||
let persistence = crate::KbDetectionPersistenceService::new(database.clone());
|
let persistence = crate::KbDetectionPersistenceService::new(database.clone());
|
||||||
Self { database, persistence }
|
Self {
|
||||||
|
database,
|
||||||
|
persistence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the built-in `moonit` launch surface exists and returns its id.
|
||||||
|
pub async fn ensure_moonit_surface(&self) -> Result<i64, crate::KbError> {
|
||||||
|
let existing_result =
|
||||||
|
crate::get_launch_surface_by_code(self.database.as_ref(), "moonit").await;
|
||||||
|
let existing_option = match existing_result {
|
||||||
|
Ok(existing_option) => existing_option,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let surface_id = match existing_option {
|
||||||
|
Some(existing) => match existing.id {
|
||||||
|
Some(surface_id) => surface_id,
|
||||||
|
None => {
|
||||||
|
return Err(crate::KbError::InvalidState(
|
||||||
|
"moonit launch surface has no internal id".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let dto = crate::KbLaunchSurfaceDto::new(
|
||||||
|
"moonit".to_string(),
|
||||||
|
"Moonit".to_string(),
|
||||||
|
Some("launchpad".to_string()),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let insert_result =
|
||||||
|
crate::upsert_launch_surface(self.database.as_ref(), &dto).await;
|
||||||
|
match insert_result {
|
||||||
|
Ok(surface_id) => surface_id,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let suffix_key_result = crate::upsert_launch_surface_key(
|
||||||
|
self.database.as_ref(),
|
||||||
|
&crate::KbLaunchSurfaceKeyDto::new(
|
||||||
|
surface_id,
|
||||||
|
"token_mint_suffix".to_string(),
|
||||||
|
"moon".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = suffix_key_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
Ok(surface_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the built-in `bags` launch surface exists and returns its id.
|
||||||
|
pub async fn ensure_bags_surface(&self) -> Result<i64, crate::KbError> {
|
||||||
|
let existing_result =
|
||||||
|
crate::get_launch_surface_by_code(self.database.as_ref(), "bags").await;
|
||||||
|
let existing_option = match existing_result {
|
||||||
|
Ok(existing_option) => existing_option,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
match existing_option {
|
||||||
|
Some(existing) => match existing.id {
|
||||||
|
Some(surface_id) => Ok(surface_id),
|
||||||
|
None => Err(crate::KbError::InvalidState(
|
||||||
|
"bags launch surface has no internal id".to_string(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let dto = crate::KbLaunchSurfaceDto::new(
|
||||||
|
"bags".to_string(),
|
||||||
|
"Bags".to_string(),
|
||||||
|
Some("launchpad".to_string()),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
crate::upsert_launch_surface(self.database.as_ref(), &dto).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers one Bags pool mapping from the documented Bags API fields.
|
||||||
|
pub async fn register_bags_pool_mapping(
|
||||||
|
&self,
|
||||||
|
token_mint: &str,
|
||||||
|
dbc_config_key: std::option::Option<std::string::String>,
|
||||||
|
dbc_pool_key: std::option::Option<std::string::String>,
|
||||||
|
damm_v2_pool_key: std::option::Option<std::string::String>,
|
||||||
|
) -> Result<i64, crate::KbError> {
|
||||||
|
let surface_id_result = self.ensure_bags_surface().await;
|
||||||
|
let surface_id = match surface_id_result {
|
||||||
|
Ok(surface_id) => surface_id,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let token_key_result = crate::upsert_launch_surface_key(
|
||||||
|
self.database.as_ref(),
|
||||||
|
&crate::KbLaunchSurfaceKeyDto::new(
|
||||||
|
surface_id,
|
||||||
|
"token_mint".to_string(),
|
||||||
|
token_mint.to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = token_key_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
if let Some(dbc_config_key) = dbc_config_key {
|
||||||
|
let key_result = crate::upsert_launch_surface_key(
|
||||||
|
self.database.as_ref(),
|
||||||
|
&crate::KbLaunchSurfaceKeyDto::new(
|
||||||
|
surface_id,
|
||||||
|
"config_account".to_string(),
|
||||||
|
dbc_config_key,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = key_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(dbc_pool_key) = dbc_pool_key {
|
||||||
|
let key_result = crate::upsert_launch_surface_key(
|
||||||
|
self.database.as_ref(),
|
||||||
|
&crate::KbLaunchSurfaceKeyDto::new(
|
||||||
|
surface_id,
|
||||||
|
"pool_account".to_string(),
|
||||||
|
dbc_pool_key,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = key_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(damm_v2_pool_key) = damm_v2_pool_key {
|
||||||
|
let key_result = crate::upsert_launch_surface_key(
|
||||||
|
self.database.as_ref(),
|
||||||
|
&crate::KbLaunchSurfaceKeyDto::new(
|
||||||
|
surface_id,
|
||||||
|
"pool_account".to_string(),
|
||||||
|
damm_v2_pool_key,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = key_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(surface_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attributes one transaction to known launch surfaces from decoded events.
|
/// Attributes one transaction to known launch surfaces from decoded events.
|
||||||
@@ -62,6 +209,10 @@ impl KbLaunchOriginService {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let ensure_moonit_result = self.ensure_moonit_surface().await;
|
||||||
|
if let Err(error) = ensure_moonit_result {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
let decoded_events_result = crate::list_dex_decoded_events_by_transaction_id(
|
let decoded_events_result = crate::list_dex_decoded_events_by_transaction_id(
|
||||||
self.database.as_ref(),
|
self.database.as_ref(),
|
||||||
transaction_id,
|
transaction_id,
|
||||||
@@ -73,9 +224,7 @@ impl KbLaunchOriginService {
|
|||||||
};
|
};
|
||||||
let mut results = std::vec::Vec::new();
|
let mut results = std::vec::Vec::new();
|
||||||
for decoded_event in &decoded_events {
|
for decoded_event in &decoded_events {
|
||||||
let candidate_result = self
|
let candidate_result = self.match_surface_for_decoded_event(decoded_event).await;
|
||||||
.match_surface_for_decoded_event(decoded_event)
|
|
||||||
.await;
|
|
||||||
let candidate = match candidate_result {
|
let candidate = match candidate_result {
|
||||||
Ok(candidate) => candidate,
|
Ok(candidate) => candidate,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -188,10 +337,7 @@ impl KbLaunchOriginService {
|
|||||||
let signal_result = self
|
let signal_result = self
|
||||||
.persistence
|
.persistence
|
||||||
.record_signal(&crate::KbDetectionSignalInput::new(
|
.record_signal(&crate::KbDetectionSignalInput::new(
|
||||||
format!(
|
format!("signal.launch_surface.{}.detected", candidate.surface_code),
|
||||||
"signal.launch_surface.{}.detected",
|
|
||||||
candidate.surface_code
|
|
||||||
),
|
|
||||||
crate::KbAnalysisSignalSeverity::Low,
|
crate::KbAnalysisSignalSeverity::Low,
|
||||||
transaction.signature.clone(),
|
transaction.signature.clone(),
|
||||||
Some(observation_id),
|
Some(observation_id),
|
||||||
@@ -224,6 +370,15 @@ impl KbLaunchOriginService {
|
|||||||
if let Some(config_account) = decoded_event.market_account.clone() {
|
if let Some(config_account) = decoded_event.market_account.clone() {
|
||||||
candidates.push(("config_account".to_string(), config_account));
|
candidates.push(("config_account".to_string(), config_account));
|
||||||
}
|
}
|
||||||
|
if let Some(pool_account) = decoded_event.pool_account.clone() {
|
||||||
|
candidates.push(("pool_account".to_string(), pool_account));
|
||||||
|
}
|
||||||
|
if let Some(token_a_mint) = decoded_event.token_a_mint.clone() {
|
||||||
|
candidates.push(("token_mint".to_string(), token_a_mint));
|
||||||
|
}
|
||||||
|
if let Some(token_b_mint) = decoded_event.token_b_mint.clone() {
|
||||||
|
candidates.push(("token_mint".to_string(), token_b_mint));
|
||||||
|
}
|
||||||
let payload_result = serde_json::from_str::<serde_json::Value>(
|
let payload_result = serde_json::from_str::<serde_json::Value>(
|
||||||
decoded_event.payload_json.as_str(),
|
decoded_event.payload_json.as_str(),
|
||||||
);
|
);
|
||||||
@@ -234,13 +389,13 @@ impl KbLaunchOriginService {
|
|||||||
if let Some(payload) = payload.as_ref() {
|
if let Some(payload) = payload.as_ref() {
|
||||||
let creator = kb_extract_payload_string(
|
let creator = kb_extract_payload_string(
|
||||||
payload,
|
payload,
|
||||||
&["creator", "poolCreator", "user", "owner"],
|
&["creator", "poolCreator", "user", "owner", "payer"],
|
||||||
);
|
);
|
||||||
if let Some(creator) = creator {
|
if let Some(creator) = creator {
|
||||||
candidates.push(("creator".to_string(), creator));
|
candidates.push(("creator".to_string(), creator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (match_kind, matched_value) in candidates {
|
for (match_kind, matched_value) in &candidates {
|
||||||
let key_result = crate::get_launch_surface_key_by_match(
|
let key_result = crate::get_launch_surface_key_by_match(
|
||||||
self.database.as_ref(),
|
self.database.as_ref(),
|
||||||
match_kind.as_str(),
|
match_kind.as_str(),
|
||||||
@@ -255,34 +410,109 @@ impl KbLaunchOriginService {
|
|||||||
Some(key) => key,
|
Some(key) => key,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
let surface_id = key.launch_surface_id;
|
let matched_result = self
|
||||||
let surface_result = crate::list_launch_surfaces(self.database.as_ref()).await;
|
.build_matched_surface_from_key(
|
||||||
let surfaces = match surface_result {
|
key,
|
||||||
Ok(surfaces) => surfaces,
|
match_kind.clone(),
|
||||||
|
matched_value.clone(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let matched_option = match matched_result {
|
||||||
|
Ok(matched_option) => matched_option,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
for surface in surfaces {
|
if matched_option.is_some() {
|
||||||
let surface_id_option = surface.id;
|
return Ok(matched_option);
|
||||||
let current_surface_id = match surface_id_option {
|
|
||||||
Some(current_surface_id) => current_surface_id,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
if current_surface_id != surface_id || !surface.is_enabled {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let matched_key_id = key.id;
|
|
||||||
return Ok(Some(KbMatchedLaunchSurface {
|
|
||||||
launch_surface_id: current_surface_id,
|
|
||||||
matched_key_id,
|
|
||||||
surface_code: surface.code,
|
|
||||||
surface_name: surface.name,
|
|
||||||
match_kind,
|
|
||||||
matched_value,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let moonit_result = self.match_moonit_by_suffix(decoded_event).await;
|
||||||
|
match moonit_result {
|
||||||
|
Ok(moonit_result) => Ok(moonit_result),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn build_matched_surface_from_key(
|
||||||
|
&self,
|
||||||
|
key: crate::KbLaunchSurfaceKeyDto,
|
||||||
|
match_kind: std::string::String,
|
||||||
|
matched_value: std::string::String,
|
||||||
|
) -> Result<std::option::Option<KbMatchedLaunchSurface>, crate::KbError> {
|
||||||
|
let surface_result = crate::list_launch_surfaces(self.database.as_ref()).await;
|
||||||
|
let surfaces = match surface_result {
|
||||||
|
Ok(surfaces) => surfaces,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
for surface in surfaces {
|
||||||
|
let current_surface_id = match surface.id {
|
||||||
|
Some(current_surface_id) => current_surface_id,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
if current_surface_id != key.launch_surface_id || !surface.is_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Ok(Some(KbMatchedLaunchSurface {
|
||||||
|
launch_surface_id: current_surface_id,
|
||||||
|
matched_key_id: key.id,
|
||||||
|
surface_code: surface.code,
|
||||||
|
surface_name: surface.name,
|
||||||
|
match_kind,
|
||||||
|
matched_value,
|
||||||
|
}));
|
||||||
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn match_moonit_by_suffix(
|
||||||
|
&self,
|
||||||
|
decoded_event: &crate::KbDexDecodedEventDto,
|
||||||
|
) -> Result<std::option::Option<KbMatchedLaunchSurface>, crate::KbError> {
|
||||||
|
let mut token_mints = std::vec::Vec::new();
|
||||||
|
if let Some(token_a_mint) = decoded_event.token_a_mint.clone() {
|
||||||
|
token_mints.push(token_a_mint);
|
||||||
|
}
|
||||||
|
if let Some(token_b_mint) = decoded_event.token_b_mint.clone() {
|
||||||
|
token_mints.push(token_b_mint);
|
||||||
|
}
|
||||||
|
let mut matched_token_option = None;
|
||||||
|
for token_mint in token_mints {
|
||||||
|
if token_mint.ends_with("moon") {
|
||||||
|
matched_token_option = Some(token_mint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let matched_token = match matched_token_option {
|
||||||
|
Some(matched_token) => matched_token,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
let surface_id_result = self.ensure_moonit_surface().await;
|
||||||
|
let surface_id = match surface_id_result {
|
||||||
|
Ok(surface_id) => surface_id,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let key_result = crate::get_launch_surface_key_by_match(
|
||||||
|
self.database.as_ref(),
|
||||||
|
"token_mint_suffix",
|
||||||
|
"moon",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let key_option = match key_result {
|
||||||
|
Ok(key_option) => key_option,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let matched_key_id = match key_option {
|
||||||
|
Some(key) => key.id,
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
Ok(Some(KbMatchedLaunchSurface {
|
||||||
|
launch_surface_id: surface_id,
|
||||||
|
matched_key_id,
|
||||||
|
surface_code: "moonit".to_string(),
|
||||||
|
surface_name: "Moonit".to_string(),
|
||||||
|
match_kind: "token_mint_suffix".to_string(),
|
||||||
|
matched_value: matched_token,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -357,9 +587,7 @@ mod tests {
|
|||||||
std::sync::Arc::new(database)
|
std::sync::Arc::new(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn seed_fun_launch_registry(
|
async fn seed_fun_launch_registry(database: std::sync::Arc<crate::KbDatabase>) {
|
||||||
database: std::sync::Arc<crate::KbDatabase>,
|
|
||||||
) {
|
|
||||||
let surface_id_result = crate::upsert_launch_surface(
|
let surface_id_result = crate::upsert_launch_surface(
|
||||||
database.as_ref(),
|
database.as_ref(),
|
||||||
&crate::KbLaunchSurfaceDto::new(
|
&crate::KbLaunchSurfaceDto::new(
|
||||||
@@ -469,8 +697,11 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert!(results[0].created_attribution);
|
assert!(results[0].created_attribution);
|
||||||
let list_result =
|
let list_result = crate::list_launch_attributions_by_pool_id(
|
||||||
crate::list_launch_attributions_by_pool_id(database.as_ref(), results[0].pool_id.unwrap()).await;
|
database.as_ref(),
|
||||||
|
results[0].pool_id.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let listed = match list_result {
|
let listed = match list_result {
|
||||||
Ok(listed) => listed,
|
Ok(listed) => listed,
|
||||||
Err(error) => panic!("launch attribution list must succeed: {}", error),
|
Err(error) => panic!("launch attribution list must succeed: {}", error),
|
||||||
@@ -479,4 +710,171 @@ mod tests {
|
|||||||
assert_eq!(listed[0].match_kind, "config_account".to_string());
|
assert_eq!(listed[0].match_kind, "config_account".to_string());
|
||||||
assert_eq!(listed[0].matched_value, "DbcDetectConfig111".to_string());
|
assert_eq!(listed[0].matched_value, "DbcDetectConfig111".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn seed_decoded_meteora_damm_v2_event_with_moon_token(
|
||||||
|
database: std::sync::Arc<crate::KbDatabase>,
|
||||||
|
signature: &str,
|
||||||
|
) {
|
||||||
|
let transaction_model = crate::KbTransactionModelService::new(database.clone());
|
||||||
|
let dex_decode = crate::KbDexDecodeService::new(database.clone());
|
||||||
|
let dex_detect = crate::KbDexDetectService::new(database);
|
||||||
|
let resolved_transaction = serde_json::json!({
|
||||||
|
"slot": 910105,
|
||||||
|
"blockTime": 1779100105,
|
||||||
|
"version": 0,
|
||||||
|
"transaction": {
|
||||||
|
"message": {
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"programId": crate::KB_METEORA_DAMM_V2_PROGRAM_ID,
|
||||||
|
"program": "meteora-damm-v2",
|
||||||
|
"stackHeight": 1,
|
||||||
|
"accounts": [
|
||||||
|
"MoonitDammV2Pool111",
|
||||||
|
"ExampleTokenmoon",
|
||||||
|
"So11111111111111111111111111111111111111112",
|
||||||
|
"MoonitDammV2Config111",
|
||||||
|
"MoonitCreator111"
|
||||||
|
],
|
||||||
|
"parsed": {
|
||||||
|
"info": {
|
||||||
|
"instruction": "initialize_customizable_pool",
|
||||||
|
"pool": "MoonitDammV2Pool111",
|
||||||
|
"tokenAMint": "ExampleTokenmoon",
|
||||||
|
"tokenBMint": "So11111111111111111111111111111111111111112",
|
||||||
|
"creator": "MoonitCreator111",
|
||||||
|
"isCustomizablePool": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": "opaque"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"err": null,
|
||||||
|
"logMessages": [
|
||||||
|
"Program log: Instruction: InitializeCustomizablePool"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let project_result = transaction_model
|
||||||
|
.persist_resolved_transaction(
|
||||||
|
signature,
|
||||||
|
Some("helius_primary_http".to_string()),
|
||||||
|
&resolved_transaction,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = project_result {
|
||||||
|
panic!("projection must succeed: {}", error);
|
||||||
|
}
|
||||||
|
let decode_result = dex_decode.decode_transaction_by_signature(signature).await;
|
||||||
|
if let Err(error) = decode_result {
|
||||||
|
panic!("dex decode must succeed: {}", error);
|
||||||
|
}
|
||||||
|
let detect_result = dex_detect.detect_transaction_by_signature(signature).await;
|
||||||
|
if let Err(error) = detect_result {
|
||||||
|
panic!("dex detect must succeed: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn seed_bags_registry(
|
||||||
|
database: std::sync::Arc<crate::KbDatabase>,
|
||||||
|
) {
|
||||||
|
let service = crate::KbLaunchOriginService::new(database);
|
||||||
|
let register_result = service
|
||||||
|
.register_bags_pool_mapping(
|
||||||
|
"DbcDetectTokenA111",
|
||||||
|
Some("DbcDetectConfig111".to_string()),
|
||||||
|
Some("DbcDetectPool111".to_string()),
|
||||||
|
Some("BagsMigratedPool111".to_string()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Err(error) = register_result {
|
||||||
|
panic!("bags registry must succeed: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn attribute_transaction_by_signature_detects_bags_from_registered_mapping() {
|
||||||
|
let database = make_database().await;
|
||||||
|
seed_bags_registry(database.clone()).await;
|
||||||
|
seed_decoded_meteora_dbc_event(database.clone(), "sig-launch-origin-bags-1").await;
|
||||||
|
let service = crate::KbLaunchOriginService::new(database.clone());
|
||||||
|
let result = service
|
||||||
|
.attribute_transaction_by_signature("sig-launch-origin-bags-1")
|
||||||
|
.await;
|
||||||
|
let results = match result {
|
||||||
|
Ok(results) => results,
|
||||||
|
Err(error) => panic!("bags attribution must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
assert!(results[0].created_attribution);
|
||||||
|
let surface_result =
|
||||||
|
crate::get_launch_surface_by_code(database.as_ref(), "bags").await;
|
||||||
|
let surface_option = match surface_result {
|
||||||
|
Ok(surface_option) => surface_option,
|
||||||
|
Err(error) => panic!("bags surface fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let surface = match surface_option {
|
||||||
|
Some(surface) => surface,
|
||||||
|
None => panic!("bags surface must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(surface.id, Some(results[0].launch_surface_id));
|
||||||
|
let listed_result = crate::list_launch_attributions_by_pool_id(
|
||||||
|
database.as_ref(),
|
||||||
|
results[0].pool_id.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let listed = match listed_result {
|
||||||
|
Ok(listed) => listed,
|
||||||
|
Err(error) => panic!("bags attribution list must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(listed.len(), 1);
|
||||||
|
assert_eq!(listed[0].match_kind, "config_account".to_string());
|
||||||
|
assert_eq!(listed[0].matched_value, "DbcDetectConfig111".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn attribute_transaction_by_signature_detects_moonit_from_token_suffix() {
|
||||||
|
let database = make_database().await;
|
||||||
|
seed_decoded_meteora_damm_v2_event_with_moon_token(
|
||||||
|
database.clone(),
|
||||||
|
"sig-launch-origin-moonit-1",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let service = crate::KbLaunchOriginService::new(database.clone());
|
||||||
|
let result = service
|
||||||
|
.attribute_transaction_by_signature("sig-launch-origin-moonit-1")
|
||||||
|
.await;
|
||||||
|
let results = match result {
|
||||||
|
Ok(results) => results,
|
||||||
|
Err(error) => panic!("moonit attribution must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
assert!(results[0].created_attribution);
|
||||||
|
let surface_result =
|
||||||
|
crate::get_launch_surface_by_code(database.as_ref(), "moonit").await;
|
||||||
|
let surface_option = match surface_result {
|
||||||
|
Ok(surface_option) => surface_option,
|
||||||
|
Err(error) => panic!("moonit surface fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let surface = match surface_option {
|
||||||
|
Some(surface) => surface,
|
||||||
|
None => panic!("moonit surface must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(surface.id, Some(results[0].launch_surface_id));
|
||||||
|
let listed_result = crate::list_launch_attributions_by_pool_id(
|
||||||
|
database.as_ref(),
|
||||||
|
results[0].pool_id.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let listed = match listed_result {
|
||||||
|
Ok(listed) => listed,
|
||||||
|
Err(error) => panic!("moonit attribution list must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(listed.len(), 1);
|
||||||
|
assert_eq!(listed[0].match_kind, "token_mint_suffix".to_string());
|
||||||
|
assert_eq!(listed[0].matched_value, "ExampleTokenmoon".to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user