mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-03 05:29:50 +00:00
chore: add helper examples to calculate program PDAs
These is needed because SPEL currently doesn't support PDA calculation the way our programs do (we wrap our seeds in SHA256)
This commit is contained in:
parent
2308681dcf
commit
0fa2b49880
90
programs/amm/examples/amm_pdas.rs
Normal file
90
programs/amm/examples/amm_pdas.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//! Print the AMM PDAs for a deployment (and, given a token pair, a pool's PDAs).
|
||||||
|
//!
|
||||||
|
//! Usage:
|
||||||
|
//! cargo run -q -p amm_program --example amm_pdas -- <amm_pid> [<twap_pid> <defA> <defB>]
|
||||||
|
//!
|
||||||
|
//! `*_pid` are ProgramIds as 8 comma-separated u32 limbs (as printed by `spel program-id`);
|
||||||
|
//! `defA`/`defB` are base58 token-definition account ids. With only `<amm_pid>` it prints the
|
||||||
|
//! singleton config PDA; with all four args it also prints the pool/vault/LP/lock/tick PDAs.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use amm_core::{
|
||||||
|
compute_config_pda, compute_liquidity_token_pda, compute_lp_lock_holding_pda, compute_pool_pda,
|
||||||
|
compute_vault_pda,
|
||||||
|
};
|
||||||
|
use nssa_core::{account::AccountId, program::ProgramId};
|
||||||
|
use twap_oracle_core::compute_current_tick_account_pda;
|
||||||
|
|
||||||
|
// Accepts a ProgramId as 8 comma-separated u32 limbs, a 64-char ImageID hex, or a base58
|
||||||
|
// ImageID. Hex/base58 are decoded as the 32 ImageID bytes read little-endian per u32 word,
|
||||||
|
// matching how `spel program-id` maps the ImageID to limbs.
|
||||||
|
fn parse_pid(s: &str) -> ProgramId {
|
||||||
|
if s.contains(',') {
|
||||||
|
let limbs: Vec<u32> = s
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.trim().parse().expect("ProgramId limb must be a u32"))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(limbs.len(), 8, "ProgramId must be 8 u32 limbs");
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
pid.copy_from_slice(&limbs);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
let bytes: [u8; 32] = if s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||||||
|
let mut out = [0u8; 32];
|
||||||
|
for (byte, pair) in out.iter_mut().zip(s.as_bytes().chunks_exact(2)) {
|
||||||
|
let pair: [u8; 2] = pair.try_into().expect("hex pair");
|
||||||
|
let hex = std::str::from_utf8(&pair).expect("ascii hex");
|
||||||
|
*byte = u8::from_str_radix(hex, 16).expect("invalid hex digit");
|
||||||
|
}
|
||||||
|
out
|
||||||
|
} else {
|
||||||
|
AccountId::from_str(s)
|
||||||
|
.expect("ProgramId must be 8 u32 limbs, a 64-char hex ImageID, or base58")
|
||||||
|
.into_value()
|
||||||
|
};
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
for (limb, chunk) in pid.iter_mut().zip(bytes.chunks_exact(4)) {
|
||||||
|
*limb = u32::from_le_bytes(chunk.try_into().expect("4-byte chunk"));
|
||||||
|
}
|
||||||
|
pid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
|
let Some((amm_s, rest)) = args.split_first() else {
|
||||||
|
eprintln!("usage: amm_pdas <amm_pid> [<twap_pid> <defA> <defB>]");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
let amm = parse_pid(amm_s);
|
||||||
|
let config = compute_config_pda(amm);
|
||||||
|
println!("config {config}");
|
||||||
|
|
||||||
|
if let [twap_s, def_a_s, def_b_s] = rest {
|
||||||
|
let twap = parse_pid(twap_s);
|
||||||
|
let def_a = AccountId::from_str(def_a_s).expect("defA must be base58");
|
||||||
|
let def_b = AccountId::from_str(def_b_s).expect("defB must be base58");
|
||||||
|
let pool = compute_pool_pda(amm, def_a, def_b);
|
||||||
|
println!("pool {pool}");
|
||||||
|
println!(
|
||||||
|
"vault_a {}",
|
||||||
|
compute_vault_pda(amm, pool, def_a)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"vault_b {}",
|
||||||
|
compute_vault_pda(amm, pool, def_b)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"pool_definition_lp {}",
|
||||||
|
compute_liquidity_token_pda(amm, pool)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"lp_lock_holding {}",
|
||||||
|
compute_lp_lock_holding_pda(amm, pool)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"current_tick_account {}",
|
||||||
|
compute_current_tick_account_pda(twap, pool)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
programs/ata/examples/ata_pdas.rs
Normal file
61
programs/ata/examples/ata_pdas.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//! Print the Associated Token Account (ATA) address for an owner + token definition.
|
||||||
|
//!
|
||||||
|
//! Usage:
|
||||||
|
//! cargo run -q -p ata_program --example ata_pdas -- <ata_pid> <token_pid> <owner> <definition>
|
||||||
|
//!
|
||||||
|
//! `*_pid` are ProgramIds as 8 comma-separated u32 limbs (as printed by `spel program-id`);
|
||||||
|
//! `owner` and `definition` are base58 account ids.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use ata_core::{compute_ata_seed, get_associated_token_account_id};
|
||||||
|
use nssa_core::{account::AccountId, program::ProgramId};
|
||||||
|
|
||||||
|
// Accepts a ProgramId as 8 comma-separated u32 limbs, a 64-char ImageID hex, or a base58
|
||||||
|
// ImageID. Hex/base58 are decoded as the 32 ImageID bytes read little-endian per u32 word,
|
||||||
|
// matching how `spel program-id` maps the ImageID to limbs.
|
||||||
|
fn parse_pid(s: &str) -> ProgramId {
|
||||||
|
if s.contains(',') {
|
||||||
|
let limbs: Vec<u32> = s
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.trim().parse().expect("ProgramId limb must be a u32"))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(limbs.len(), 8, "ProgramId must be 8 u32 limbs");
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
pid.copy_from_slice(&limbs);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
let bytes: [u8; 32] = if s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||||||
|
let mut out = [0u8; 32];
|
||||||
|
for (byte, pair) in out.iter_mut().zip(s.as_bytes().chunks_exact(2)) {
|
||||||
|
let pair: [u8; 2] = pair.try_into().expect("hex pair");
|
||||||
|
let hex = std::str::from_utf8(&pair).expect("ascii hex");
|
||||||
|
*byte = u8::from_str_radix(hex, 16).expect("invalid hex digit");
|
||||||
|
}
|
||||||
|
out
|
||||||
|
} else {
|
||||||
|
AccountId::from_str(s)
|
||||||
|
.expect("ProgramId must be 8 u32 limbs, a 64-char hex ImageID, or base58")
|
||||||
|
.into_value()
|
||||||
|
};
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
for (limb, chunk) in pid.iter_mut().zip(bytes.chunks_exact(4)) {
|
||||||
|
*limb = u32::from_le_bytes(chunk.try_into().expect("4-byte chunk"));
|
||||||
|
}
|
||||||
|
pid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
|
let [ata_s, token_s, owner_s, def_s] = args.as_slice() else {
|
||||||
|
eprintln!("usage: ata_pdas <ata_pid> <token_pid> <owner> <definition>");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
let ata = parse_pid(ata_s);
|
||||||
|
let token = parse_pid(token_s);
|
||||||
|
let owner = AccountId::from_str(owner_s).expect("owner must be base58");
|
||||||
|
let definition = AccountId::from_str(def_s).expect("definition must be base58");
|
||||||
|
|
||||||
|
let seed = compute_ata_seed(token, owner, definition);
|
||||||
|
println!("ata {}", get_associated_token_account_id(&ata, &seed));
|
||||||
|
}
|
||||||
82
programs/twap_oracle/examples/twap_oracle_pdas.rs
Normal file
82
programs/twap_oracle/examples/twap_oracle_pdas.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//! Print the TWAP oracle PDAs for a price source (current-tick always; the windowed
|
||||||
|
//! price-observations / oracle-price accounts when a window duration is given).
|
||||||
|
//!
|
||||||
|
//! Usage:
|
||||||
|
//! cargo run -q -p twap_oracle_program --example twap_oracle_pdas -- <oracle_pid> <price_source>
|
||||||
|
//! [<window_duration>]
|
||||||
|
//!
|
||||||
|
//! `oracle_pid` is a ProgramId as 8 comma-separated u32 limbs (as printed by `spel program-id`);
|
||||||
|
//! `price_source` is a base58 account id (e.g. an AMM pool); `window_duration` is a u64.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use nssa_core::{account::AccountId, program::ProgramId};
|
||||||
|
use twap_oracle_core::{
|
||||||
|
compute_current_tick_account_pda, compute_oracle_price_account_pda,
|
||||||
|
compute_price_observations_pda,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accepts a ProgramId as 8 comma-separated u32 limbs, a 64-char ImageID hex, or a base58
|
||||||
|
// ImageID. Hex/base58 are decoded as the 32 ImageID bytes read little-endian per u32 word,
|
||||||
|
// matching how `spel program-id` maps the ImageID to limbs.
|
||||||
|
fn parse_pid(s: &str) -> ProgramId {
|
||||||
|
if s.contains(',') {
|
||||||
|
let limbs: Vec<u32> = s
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.trim().parse().expect("ProgramId limb must be a u32"))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(limbs.len(), 8, "ProgramId must be 8 u32 limbs");
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
pid.copy_from_slice(&limbs);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
let bytes: [u8; 32] = if s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||||||
|
let mut out = [0u8; 32];
|
||||||
|
for (byte, pair) in out.iter_mut().zip(s.as_bytes().chunks_exact(2)) {
|
||||||
|
let pair: [u8; 2] = pair.try_into().expect("hex pair");
|
||||||
|
let hex = std::str::from_utf8(&pair).expect("ascii hex");
|
||||||
|
*byte = u8::from_str_radix(hex, 16).expect("invalid hex digit");
|
||||||
|
}
|
||||||
|
out
|
||||||
|
} else {
|
||||||
|
AccountId::from_str(s)
|
||||||
|
.expect("ProgramId must be 8 u32 limbs, a 64-char hex ImageID, or base58")
|
||||||
|
.into_value()
|
||||||
|
};
|
||||||
|
let mut pid: ProgramId = [0u32; 8];
|
||||||
|
for (limb, chunk) in pid.iter_mut().zip(bytes.chunks_exact(4)) {
|
||||||
|
*limb = u32::from_le_bytes(chunk.try_into().expect("4-byte chunk"));
|
||||||
|
}
|
||||||
|
pid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
|
let (oracle_s, source_s, window_s) = match args.as_slice() {
|
||||||
|
[oracle, source] => (oracle, source, None),
|
||||||
|
[oracle, source, window] => (oracle, source, Some(window)),
|
||||||
|
_ => {
|
||||||
|
eprintln!("usage: twap_oracle_pdas <oracle_pid> <price_source> [<window_duration>]");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let oracle = parse_pid(oracle_s);
|
||||||
|
let source = AccountId::from_str(source_s).expect("price source must be base58");
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"current_tick_account {}",
|
||||||
|
compute_current_tick_account_pda(oracle, source)
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(window_s) = window_s {
|
||||||
|
let window: u64 = window_s.parse().expect("window_duration must be a u64");
|
||||||
|
println!(
|
||||||
|
"price_observations {}",
|
||||||
|
compute_price_observations_pda(oracle, source, window)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"oracle_price_account {}",
|
||||||
|
compute_oracle_price_account_pda(oracle, source, window)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user