0.7.38
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user