This commit is contained in:
2026-05-14 17:44:01 +02:00
parent 403f271083
commit 3f6d2e9f7f
21 changed files with 775 additions and 88 deletions

View File

@@ -13,6 +13,67 @@ const WRAPPED_SOL_SYMBOL: &str = "WSOL";
const WRAPPED_SOL_NAME: &str = "Wrapped SOL";
const METAPLEX_TOKEN_METADATA_PROGRAM_ID: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
#[derive(Debug, Clone, Copy)]
struct KnownLocalTokenMetadata {
mint: &'static str,
symbol: &'static str,
name: &'static str,
decimals: u8,
token_program: &'static str,
is_quote_token: std::option::Option<bool>,
}
const KNOWN_LOCAL_TOKEN_METADATA: &[KnownLocalTokenMetadata] = &[
KnownLocalTokenMetadata {
mint: crate::WSOL_MINT_ID,
symbol: WRAPPED_SOL_SYMBOL,
name: WRAPPED_SOL_NAME,
decimals: 9,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: Some(true),
},
KnownLocalTokenMetadata {
mint: crate::USDC_MINT_ID,
symbol: "USDC",
name: "USD Coin",
decimals: 6,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: Some(true),
},
KnownLocalTokenMetadata {
mint: crate::USDT_MINT_ID,
symbol: "USDT",
name: "Tether USD",
decimals: 6,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: Some(true),
},
KnownLocalTokenMetadata {
mint: crate::JUP_MINT_ID,
symbol: "JUP",
name: "Jupiter",
decimals: 6,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: None,
},
KnownLocalTokenMetadata {
mint: crate::RAY_MINT_ID,
symbol: "RAY",
name: "Raydium",
decimals: 6,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: None,
},
KnownLocalTokenMetadata {
mint: crate::BONK_MINT_ID,
symbol: "BONK",
name: "Bonk",
decimals: 5,
token_program: crate::SPL_TOKEN_PROGRAM_ID,
is_quote_token: None,
},
];
/// Summary produced by a token metadata backfill pass.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -321,15 +382,16 @@ fn resolve_local_token_metadata(mint: &str) -> std::option::Option<ResolvedToken
is_quote_token: Some(true),
});
}
let wsol_mint = crate::WSOL_MINT_ID.to_string();
if mint == wsol_mint {
return Some(ResolvedTokenMetadata {
symbol: Some(WRAPPED_SOL_SYMBOL.to_string()),
name: Some(WRAPPED_SOL_NAME.to_string()),
decimals: Some(9),
token_program: Some(crate::SPL_TOKEN_PROGRAM_ID.to_string()),
is_quote_token: Some(true),
});
for known_token in KNOWN_LOCAL_TOKEN_METADATA {
if mint == known_token.mint {
return Some(ResolvedTokenMetadata {
symbol: Some(known_token.symbol.to_string()),
name: Some(known_token.name.to_string()),
decimals: Some(known_token.decimals),
token_program: Some(known_token.token_program.to_string()),
is_quote_token: known_token.is_quote_token,
});
}
}
return None;
}
@@ -820,6 +882,26 @@ mod tests {
assert_eq!(metadata.is_quote_token, Some(true));
}
#[test]
fn local_metadata_resolves_known_ecosystem_tokens_without_forcing_quote_flag() {
let cases = [
(crate::JUP_MINT_ID, "JUP", "Jupiter", 6_u8),
(crate::RAY_MINT_ID, "RAY", "Raydium", 6_u8),
(crate::BONK_MINT_ID, "BONK", "Bonk", 5_u8),
];
for (mint, expected_symbol, expected_name, expected_decimals) in cases {
let metadata_option = super::resolve_local_token_metadata(mint);
let metadata = match metadata_option {
Some(metadata) => metadata,
None => panic!("known ecosystem token metadata must resolve"),
};
assert_eq!(metadata.symbol.as_deref(), Some(expected_symbol));
assert_eq!(metadata.name.as_deref(), Some(expected_name));
assert_eq!(metadata.decimals, Some(expected_decimals));
assert_eq!(metadata.is_quote_token, None);
}
}
#[test]
fn pump_fun_payload_metadata_extracts_name_and_symbol() {
let payload = serde_json::json!({
@@ -932,6 +1014,144 @@ mod tests {
assert!(fetched.is_quote_token);
}
#[tokio::test]
async fn local_backfill_updates_stable_quotes_without_http() {
let database = make_database().await;
let usdc = crate::TokenDto::new(
crate::USDC_MINT_ID.to_string(),
None,
None,
None,
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
false,
);
let usdt = crate::TokenDto::new(
crate::USDT_MINT_ID.to_string(),
None,
None,
None,
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
false,
);
let usdc_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdc).await;
if let Err(error) = usdc_upsert_result {
panic!("usdc token upsert must succeed: {}", error);
}
let usdt_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdt).await;
if let Err(error) = usdt_upsert_result {
panic!("usdt token upsert must succeed: {}", error);
}
let service = crate::TokenMetadataBackfillService::new_local(database.clone());
let result = service.backfill_missing_token_metadata(Some(10)).await;
let result = match result {
Ok(result) => result,
Err(error) => panic!("metadata backfill must succeed: {}", error),
};
assert_eq!(result.updated_token_count, 2);
let usdc_result =
crate::query_tokens_get_by_mint(database.as_ref(), crate::USDC_MINT_ID).await;
let usdc_option = match usdc_result {
Ok(usdc_option) => usdc_option,
Err(error) => panic!("usdc token fetch must succeed: {}", error),
};
let usdc = match usdc_option {
Some(usdc) => usdc,
None => panic!("usdc token must exist"),
};
assert_eq!(usdc.symbol.as_deref(), Some("USDC"));
assert_eq!(usdc.name.as_deref(), Some("USD Coin"));
assert_eq!(usdc.decimals, Some(6));
assert!(usdc.is_quote_token);
let usdt_result =
crate::query_tokens_get_by_mint(database.as_ref(), crate::USDT_MINT_ID).await;
let usdt_option = match usdt_result {
Ok(usdt_option) => usdt_option,
Err(error) => panic!("usdt token fetch must succeed: {}", error),
};
let usdt = match usdt_option {
Some(usdt) => usdt,
None => panic!("usdt token must exist"),
};
assert_eq!(usdt.symbol.as_deref(), Some("USDT"));
assert_eq!(usdt.name.as_deref(), Some("Tether USD"));
assert_eq!(usdt.decimals, Some(6));
assert!(usdt.is_quote_token);
}
#[tokio::test]
async fn local_backfill_updates_known_ecosystem_tokens_without_http() {
let database = make_database().await;
let token_mints = [crate::JUP_MINT_ID, crate::RAY_MINT_ID, crate::BONK_MINT_ID];
for mint in token_mints {
let token = crate::TokenDto::new(
mint.to_string(),
None,
None,
None,
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
false,
);
let upsert_result = crate::query_tokens_upsert(database.as_ref(), &token).await;
if let Err(error) = upsert_result {
panic!("known token upsert must succeed: {}", error);
}
}
let service = crate::TokenMetadataBackfillService::new_local(database.clone());
let result = service.backfill_missing_token_metadata(Some(10)).await;
let result = match result {
Ok(result) => result,
Err(error) => panic!("metadata backfill must succeed: {}", error),
};
assert_eq!(result.updated_token_count, 3);
let jup_result =
crate::query_tokens_get_by_mint(database.as_ref(), crate::JUP_MINT_ID).await;
let jup_option = match jup_result {
Ok(jup_option) => jup_option,
Err(error) => panic!("jup token fetch must succeed: {}", error),
};
let jup = match jup_option {
Some(jup) => jup,
None => panic!("jup token must exist"),
};
assert_eq!(jup.symbol.as_deref(), Some("JUP"));
assert_eq!(jup.name.as_deref(), Some("Jupiter"));
assert_eq!(jup.decimals, Some(6));
assert!(!jup.is_quote_token);
let ray_result =
crate::query_tokens_get_by_mint(database.as_ref(), crate::RAY_MINT_ID).await;
let ray_option = match ray_result {
Ok(ray_option) => ray_option,
Err(error) => panic!("ray token fetch must succeed: {}", error),
};
let ray = match ray_option {
Some(ray) => ray,
None => panic!("ray token must exist"),
};
assert_eq!(ray.symbol.as_deref(), Some("RAY"));
assert_eq!(ray.name.as_deref(), Some("Raydium"));
assert_eq!(ray.decimals, Some(6));
assert!(!ray.is_quote_token);
let bonk_result =
crate::query_tokens_get_by_mint(database.as_ref(), crate::BONK_MINT_ID).await;
let bonk_option = match bonk_result {
Ok(bonk_option) => bonk_option,
Err(error) => panic!("bonk token fetch must succeed: {}", error),
};
let bonk = match bonk_option {
Some(bonk) => bonk,
None => panic!("bonk token must exist"),
};
assert_eq!(bonk.symbol.as_deref(), Some("BONK"));
assert_eq!(bonk.name.as_deref(), Some("Bonk"));
assert_eq!(bonk.decimals, Some(5));
assert!(!bonk.is_quote_token);
}
#[tokio::test]
async fn local_backfill_does_not_overwrite_existing_display_metadata() {
let database = make_database().await;