-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add environment configuration, update Pyth SDK, implement optio… #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ pub struct HumidifiCfg { | |
| pub market: Pubkey, | ||
| pub base_ta: Pubkey, | ||
| pub quote_ta: Pubkey, | ||
| pub reserve_mints: [(Pubkey, u8); 2], | ||
| pub reserve_mints: [(Pubkey, Option<u8>); 2], | ||
| } | ||
|
|
||
| impl TryFrom<&serde_json::Value> for HumidifiCfg { | ||
|
|
@@ -58,21 +58,32 @@ impl TryFrom<&serde_json::Value> for HumidifiCfg { | |
| return Err("reserve_mints must have exactly 2 elements".to_string()); | ||
| } | ||
|
|
||
| let mint0 = reserve_mints[0].as_array().ok_or("reserve_mints[0] not a string")?; | ||
| let (mint0_addr, mint0_dec) = (mint0[0].as_str().ok_or("reserve_mints[0][0] not a string")?, mint0[1].as_u64().ok_or("reserve_mints[0][1] not a u64")?); | ||
| let parse_mint = |val: &serde_json::Value| -> Result<(Pubkey, Option<u8>), String> { | ||
| if let Some(arr) = val.as_array() { | ||
| if arr.len() < 2 { | ||
| return Err("reserve_mint array must have at least 2 elements".to_string()); | ||
| } | ||
| let addr_str = arr[0].as_str().ok_or("reserve_mint address not a string")?; | ||
| let addr = Pubkey::from_str(addr_str).map_err(|e| e.to_string())?; | ||
| let dec = arr[1].as_u64().ok_or("reserve_mint decimals not a u64")? as u8; | ||
| Ok((addr, Some(dec))) | ||
| } else if let Some(addr_str) = val.as_str() { | ||
| let addr = Pubkey::from_str(addr_str).map_err(|e| e.to_string())?; | ||
| Ok((addr, None)) | ||
| } else { | ||
| Err("reserve_mint must be an array [address, decimals] or a string address".to_string()) | ||
| } | ||
| }; | ||
|
|
||
| let mint1 = reserve_mints[1].as_array().ok_or("reserve_mints[1] not a string")?; | ||
| let (mint1_addr, mint1_dec) = (mint1[0].as_str().ok_or("reserve_mints[1][0] not a string")?, mint1[1].as_u64().ok_or("reserve_mints[1][1] not a u64")?); | ||
| let mint0 = parse_mint(&reserve_mints[0])?; | ||
| let mint1 = parse_mint(&reserve_mints[1])?; | ||
|
|
||
| Ok(HumidifiCfg { | ||
| pubkey: Pubkey::from_str(pubkey).map_err(|e| e.to_string())?, | ||
| market: Pubkey::from_str(market).map_err(|e| e.to_string())?, | ||
| base_ta: Pubkey::from_str(base_ta).map_err(|e| e.to_string())?, | ||
| quote_ta: Pubkey::from_str(quote_ta).map_err(|e| e.to_string())?, | ||
| reserve_mints: [ | ||
| (Pubkey::from_str(mint0_addr).map_err(|e| e.to_string())?, mint0_dec as u8), | ||
| (Pubkey::from_str(mint1_addr).map_err(|e| e.to_string())?, mint1_dec as u8), | ||
| ], | ||
| reserve_mints: [mint0, mint1], | ||
| }) | ||
| } | ||
| } | ||
|
|
@@ -81,14 +92,29 @@ impl Adapter for Humidifi {} | |
|
|
||
| impl Humidifi { | ||
| pub fn new(cfg: HumidifiCfg, client: &RpcClient) -> eyre::Result<Humidifi> { | ||
| let mut chroot = Chroot::new(cfg.reserve_mints); | ||
| use solana_sdk::program_pack::Pack; | ||
|
|
||
| let mut reserve_mints_with_decimals = [(Pubkey::default(), 0u8); 2]; | ||
| for (i, &(mint, opt_dec)) in cfg.reserve_mints.iter().enumerate() { | ||
| let dec = match opt_dec { | ||
| Some(d) => d, | ||
| None => { | ||
| let account = client.get_account(&mint)?; | ||
| let mint_state = spl_token::state::Mint::unpack(&account.data)?; | ||
| mint_state.decimals | ||
| } | ||
| }; | ||
| reserve_mints_with_decimals[i] = (mint, dec); | ||
| } | ||
|
|
||
| let mut chroot = Chroot::new(reserve_mints_with_decimals); | ||
| chroot.load_program(ROUTER_ID, "./cfg/programs/magnus-router.so")?; | ||
| chroot.load_program(Pubkey::from_str_const(&pmm_humidifi::id().to_string()), "./cfg/programs/humidifi.so")?; | ||
|
|
||
| let accs = client.get_multiple_accounts(&[cfg.market, cfg.base_ta, cfg.quote_ta])?; | ||
| chroot.load_accounts(vec![(cfg.market, accs[0].clone().unwrap()), (cfg.base_ta, accs[1].clone().unwrap()), (cfg.quote_ta, accs[2].clone().unwrap())])?; | ||
|
Comment on lines
114
to
115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Map the target file and inspect the relevant section with line numbers.
git ls-files crates/magnus/src/adapters/amms/humidifi.rs
wc -l crates/magnus/src/adapters/amms/humidifi.rs
sed -n '90,140p' crates/magnus/src/adapters/amms/humidifi.rs
# Find the surrounding types and helper methods involved in this path.
rg -n "get_multiple_accounts|load_accounts|struct .*Config|impl .*humidifi|new\(" crates/magnus/src/adapters/amms/humidifi.rs crates/magnus/src -g '!target'Repository: LimeChain/magnus Length of output: 15133 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the bootstrap path and account-loading helper to understand how failures surface.
sed -n '1,120p' crates/magnus/src/bootstrap.rs
sed -n '110,150p' crates/magnus/src/adapters/amms.rsRepository: LimeChain/magnus Length of output: 4459 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Confirm every call site of Humidifi::new and how its Result is handled.
rg -n "Humidifi::new\(" crates/magnus/src
rg -n "unable to initialise humidifi|expect\\(\"unable to initialise humidifi\"\\)" crates/magnus/srcRepository: LimeChain/magnus Length of output: 426 Replace the 🤖 Prompt for AI Agents |
||
|
|
||
| cfg.reserve_mints.iter().try_for_each(|(mint_addr, _)| -> eyre::Result<()> { | ||
| reserve_mints_with_decimals.iter().try_for_each(|(mint_addr, _)| -> eyre::Result<()> { | ||
| let ata = Chroot::mk_ata(mint_addr, &chroot.wallet_pubkey(), 0); | ||
| let addr = chroot.wallet_ata(mint_addr); | ||
| chroot.load_accounts(vec![(addr, ata)]) | ||
|
|
@@ -250,8 +276,8 @@ mod tests { | |
|
|
||
| assert_eq!(cfg.pubkey, Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap()); | ||
| assert_eq!(cfg.market, Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap()); | ||
| assert_eq!(cfg.reserve_mints[0].1, 6); | ||
| assert_eq!(cfg.reserve_mints[1].1, 9); | ||
| assert_eq!(cfg.reserve_mints[0].1, Some(6)); | ||
| assert_eq!(cfg.reserve_mints[1].1, Some(9)); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,20 +21,22 @@ pub fn load(file: &str, client: &RpcClient) -> eyre::Result<Vec<Box<dyn Amm>>> { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let pmms = if let serde_json::Value::Array(items) = &cfgs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| items | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(|item| -> Box<dyn Amm> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dex = Dex::from_str(item.get("dex").and_then(|dex| dex.as_str()).expect("no DEX provided")).map_err(|e| eyre!(e)).expect(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter_map(|item| -> Option<Box<dyn Amm>> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dex_str = item.get("dex").and_then(|dex| dex.as_str()).expect("no DEX provided"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dex = Dex::from_str(dex_str).map_err(|e| eyre!(e)).expect(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let init: Box<dyn Amm> = match dex { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match dex { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dex::HumidiFi => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cfg = HumidifiCfg::try_from(item).map_err(|e| eyre!(e)).expect(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let amm = Humidifi::new(cfg, client).expect("unable to initialise humidifi"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Box::new(amm) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(Box::new(amm) as Box<dyn Amm>) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ => panic!("Unsupported DEX: {}", dex), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| init | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tracing::info!("Skipping unsupported/non-bootstrapped DEX in bootstrap load: {}", dex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
The PR prevents panics for unsupported DEX types, but line 31's 🛡️ Proposed fix: skip failed Humidifi entries instead of panicking match dex {
Dex::HumidiFi => {
- let cfg = HumidifiCfg::try_from(item).map_err(|e| eyre!(e)).expect("");
- let amm = Humidifi::new(cfg, client).expect("unable to initialise humidifi");
-
- Some(Box::new(amm) as Box<dyn Amm>)
+ match HumidifiCfg::try_from(item).map_err(|e| eyre!(e)) {
+ Ok(cfg) => match Humidifi::new(cfg, client) {
+ Ok(amm) => Some(Box::new(amm) as Box<dyn Amm>),
+ Err(e) => {
+ tracing::warn!("Failed to initialise Humidifi AMM: {}", e);
+ None
+ }
+ },
+ Err(e) => {
+ tracing::warn!("Failed to parse Humidifi config: {}", e);
+ None
+ }
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .collect() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
spl-token 9.0.0 Mint::unpack Token-2022 extensions compatibility💡 Result:
In SPL Token 9.0.0, the
Mint::unpackmethod remains designed specifically for the original SPL Token program's fixed-size 82-byte data structure [1][2]. It is not compatible with Token-2022 mint accounts that utilize extensions [3]. Key technical distinctions include: 1. Data Layout: The original SPL Token program stores mint data in a fixed 82-byte layout [1][2]. Token-2022 utilizes a Type-Length-Value (TLV) structure to support extensions, meaning mint accounts can have a variable size greater than 82 bytes [3][4]. 2. Unpacking Mechanism: BecauseMint::unpackexpects exactly 82 bytes, attempting to use it on a Token-2022 mint account (which contains additional TLV data) will fail if the data length validation is strictly enforced, or it will ignore the extension data entirely if only the first 82 bytes are read [4][1]. 3. Recommended Approach: To interact with Token-2022 mints, you must use thespl-token-2022crate, which providesPodStateWithExtensionsor equivalent helpers to correctly parse the base state along with the appended TLV extension data [3][4]. For developers needing a unified approach without deep dependencies, generic tools likespl-generic-tokenmay be used for basic fields (supply, decimals) [5], but they intentionally exclude access to extensions. Any logic requiring extension support (e.g., transfer hooks, metadata, or confidential transfers) must utilize thespl-token-2022library structures [3][4].Citations:
🏁 Script executed:
Repository: LimeChain/magnus
Length of output: 12471
Handle Token-2022 mints here too
Mint::unpackonly reads the legacy 82-byte mint layout, so any reserve mint with Token-2022 extensions will fail whendecimalsis omitted. Either require decimals in config for those mints or branch on the mint program and parse Token-2022 state accordingly.🤖 Prompt for AI Agents