mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-26 22:51:14 +00:00
feat(amm): create TWAP price observations on behalf of the pool
Add a `CreatePriceObservations` instruction that registers a TWAP price-observations account for a pool over a time window, via a chained call to the configured TWAP oracle program. The pool acts as the price source: the AMM authorizes it with its pool PDA seed so the oracle ties the feed to that pool. The feed's initial tick is read from the pool's authoritative `CurrentTickAccount` (validated against its pool-derived PDA) rather than being supplied by the caller, so the feed cannot be seeded at a forged price — mirroring what `RecordTick` does. The clock is verified to be the canonical 1-block LEZ clock, and creation is rejected if the observations account already exists. To support the chained call, `AmmConfig` and the `Initialize` instruction are extended with a `twap_oracle_program_id` that the instruction reads.
This commit is contained in:
@@ -7,10 +7,11 @@ use nssa_core::{
|
||||
/// Initializes the AMM Program by creating its singleton configuration account.
|
||||
///
|
||||
/// The config account is a PDA derived from the constant `"CONFIG"` seed
|
||||
/// (`compute_config_pda(amm_program_id)`) and stores `token_program_id` (the Token Program the
|
||||
/// AMM issues every chained call to) and `authority` (the admin allowed to change configuration
|
||||
/// later via `update_config`). Its existence is the Program's "initialized" flag: the
|
||||
/// chained-call instructions read the Token Program ID from it and reject calls until it exists.
|
||||
/// (`compute_config_pda(amm_program_id)`) and stores the program IDs the AMM issues chained calls
|
||||
/// to — `token_program_id` (the Token Program) and `twap_oracle_program_id` (the TWAP oracle) —
|
||||
/// plus `authority` (the admin allowed to change configuration later via `update_config`). Its
|
||||
/// existence is the Program's "initialized" flag: the chained-call instructions read these
|
||||
/// program IDs from it and reject calls until it exists.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if:
|
||||
@@ -19,6 +20,7 @@ use nssa_core::{
|
||||
pub fn initialize(
|
||||
config: AccountWithMetadata,
|
||||
token_program_id: ProgramId,
|
||||
twap_oracle_program_id: ProgramId,
|
||||
authority: AccountId,
|
||||
amm_program_id: ProgramId,
|
||||
) -> Vec<AccountPostState> {
|
||||
@@ -36,6 +38,7 @@ pub fn initialize(
|
||||
let mut config_post = config.account.clone();
|
||||
config_post.data = Data::from(&AmmConfig {
|
||||
token_program_id,
|
||||
twap_oracle_program_id,
|
||||
authority,
|
||||
});
|
||||
|
||||
@@ -54,6 +57,7 @@ mod tests {
|
||||
|
||||
const AMM_PROGRAM_ID: ProgramId = [42; 8];
|
||||
const TOKEN_PROGRAM_ID: ProgramId = [15; 8];
|
||||
const TWAP_ORACLE_PROGRAM_ID: ProgramId = [77; 8];
|
||||
|
||||
fn authority() -> AccountId {
|
||||
AccountId::new([9; 32])
|
||||
@@ -72,6 +76,7 @@ mod tests {
|
||||
let post_states = initialize(
|
||||
config_uninit(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
TWAP_ORACLE_PROGRAM_ID,
|
||||
authority(),
|
||||
AMM_PROGRAM_ID,
|
||||
);
|
||||
@@ -83,16 +88,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stores_token_program_id_and_authority() {
|
||||
fn stores_program_ids_and_authority() {
|
||||
let post_states = initialize(
|
||||
config_uninit(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
TWAP_ORACLE_PROGRAM_ID,
|
||||
authority(),
|
||||
AMM_PROGRAM_ID,
|
||||
);
|
||||
let config = AmmConfig::try_from(&post_states[0].account().data)
|
||||
.expect("post state must contain a valid AmmConfig");
|
||||
assert_eq!(config.token_program_id, TOKEN_PROGRAM_ID);
|
||||
assert_eq!(config.twap_oracle_program_id, TWAP_ORACLE_PROGRAM_ID);
|
||||
assert_eq!(config.authority, authority());
|
||||
}
|
||||
|
||||
@@ -101,7 +108,13 @@ mod tests {
|
||||
fn wrong_config_account_id_panics() {
|
||||
let mut wrong = config_uninit();
|
||||
wrong.account_id = AccountId::new([0; 32]);
|
||||
initialize(wrong, TOKEN_PROGRAM_ID, authority(), AMM_PROGRAM_ID);
|
||||
initialize(
|
||||
wrong,
|
||||
TOKEN_PROGRAM_ID,
|
||||
TWAP_ORACLE_PROGRAM_ID,
|
||||
authority(),
|
||||
AMM_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -110,9 +123,16 @@ mod tests {
|
||||
let mut initialized = config_uninit();
|
||||
initialized.account.data = Data::from(&AmmConfig {
|
||||
token_program_id: TOKEN_PROGRAM_ID,
|
||||
twap_oracle_program_id: TWAP_ORACLE_PROGRAM_ID,
|
||||
authority: authority(),
|
||||
});
|
||||
initialized.account.nonce = Nonce(0);
|
||||
initialize(initialized, TOKEN_PROGRAM_ID, authority(), AMM_PROGRAM_ID);
|
||||
initialize(
|
||||
initialized,
|
||||
TOKEN_PROGRAM_ID,
|
||||
TWAP_ORACLE_PROGRAM_ID,
|
||||
authority(),
|
||||
AMM_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user