feat(amm)!: add transaction deadlines to swap and liquidity instructions

All mutable AMM instructions now require a `deadline: u64` field (Unix
timestamp in milliseconds). Enforcement uses the LEZ-native timestamp
validity window set on ProgramOutput; the runtime rejects the
transaction if the sequencer submission timestamp is at or past the
deadline.

BREAKING CHANGE: AddLiquidity, RemoveLiquidity, SwapExactInput,
SwapExactOutput, and NewDefinition instruction variants now require a
`deadline` field.

Closes #8
This commit is contained in:
r4bbit 2026-04-23 17:19:15 +02:00
parent 37fc2ea088
commit 216e92d25a
No known key found for this signature in database
GPG Key ID: E95F1E9447DC91A9
10 changed files with 280 additions and 21 deletions

View File

@ -35,6 +35,8 @@ pub enum Instruction {
token_b_amount: u128, token_b_amount: u128,
fees: u128, fees: u128,
amm_program_id: ProgramId, amm_program_id: ProgramId,
/// Unix timestamp (milliseconds) after which this transaction is invalid.
deadline: u64,
}, },
/// Adds liquidity to the Pool /// Adds liquidity to the Pool
@ -51,6 +53,8 @@ pub enum Instruction {
min_amount_liquidity: u128, min_amount_liquidity: u128,
max_amount_to_add_token_a: u128, max_amount_to_add_token_a: u128,
max_amount_to_add_token_b: u128, max_amount_to_add_token_b: u128,
/// Unix timestamp (milliseconds) after which this transaction is invalid.
deadline: u64,
}, },
/// Removes liquidity from the Pool /// Removes liquidity from the Pool
@ -67,6 +71,8 @@ pub enum Instruction {
remove_liquidity_amount: u128, remove_liquidity_amount: u128,
min_amount_to_remove_token_a: u128, min_amount_to_remove_token_a: u128,
min_amount_to_remove_token_b: u128, min_amount_to_remove_token_b: u128,
/// Unix timestamp (milliseconds) after which this transaction is invalid.
deadline: u64,
}, },
/// Swap some quantity of Tokens (either Token A or Token B) /// Swap some quantity of Tokens (either Token A or Token B)
@ -77,12 +83,13 @@ pub enum Instruction {
/// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token A (initialized)
/// - Vault Holding Account for Token B (initialized) /// - Vault Holding Account for Token B (initialized)
/// - User Holding Account for Token A /// - User Holding Account for Token A
/// - User Holding Account for Token B Either User Holding Account for Token A or Token B is /// - User Holding Account for Token B; either is authorized.
/// authorized.
SwapExactInput { SwapExactInput {
swap_amount_in: u128, swap_amount_in: u128,
min_amount_out: u128, min_amount_out: u128,
token_definition_id_in: AccountId, token_definition_id_in: AccountId,
/// Unix timestamp (milliseconds) after which this transaction is invalid.
deadline: u64,
}, },
/// Swap tokens specifying the exact desired output amount, /// Swap tokens specifying the exact desired output amount,
@ -93,12 +100,13 @@ pub enum Instruction {
/// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token A (initialized)
/// - Vault Holding Account for Token B (initialized) /// - Vault Holding Account for Token B (initialized)
/// - User Holding Account for Token A /// - User Holding Account for Token A
/// - User Holding Account for Token B Either User Holding Account for Token A or Token B is /// - User Holding Account for Token B; either is authorized.
/// authorized.
SwapExactOutput { SwapExactOutput {
exact_amount_out: u128, exact_amount_out: u128,
max_amount_in: u128, max_amount_in: u128,
token_definition_id_in: AccountId, token_definition_id_in: AccountId,
/// Unix timestamp (milliseconds) after which this transaction is invalid.
deadline: u64,
}, },
/// Sync pool reserves with current vault balances. /// Sync pool reserves with current vault balances.

View File

@ -2898,10 +2898,11 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework" name = "spel-framework"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde_json",
"spel-framework-core", "spel-framework-core",
"spel-framework-macros", "spel-framework-macros",
] ]
@ -2909,24 +2910,27 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework-core" name = "spel-framework-core"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"syn 2.0.117",
"thiserror 1.0.69", "thiserror 1.0.69",
] ]
[[package]] [[package]]
name = "spel-framework-macros" name = "spel-framework-macros"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_json",
"sha2", "sha2",
"spel-framework-core",
"syn 2.0.117", "syn 2.0.117",
] ]

View File

@ -10,7 +10,7 @@ name = "amm"
path = "src/bin/amm.rs" path = "src/bin/amm.rs"
[dependencies] [dependencies]
spel-framework = { git = "https://github.com/logos-co/spel.git", tag = "v0.2.0-rc.2", package = "spel-framework" } spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3", package = "spel-framework" }
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" } nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" }
risc0-zkvm = { version = "=3.0.5", default-features = false } risc0-zkvm = { version = "=3.0.5", default-features = false }
amm_core = { path = "../../core" } amm_core = { path = "../../core" }

View File

@ -31,6 +31,7 @@ mod amm {
token_b_amount: u128, token_b_amount: u128,
fees: u128, fees: u128,
amm_program_id: ProgramId, amm_program_id: ProgramId,
deadline: u64,
) -> SpelResult { ) -> SpelResult {
let (post_states, chained_calls) = amm_program::new_definition::new_definition( let (post_states, chained_calls) = amm_program::new_definition::new_definition(
pool, pool,
@ -46,7 +47,8 @@ mod amm {
fees, fees,
amm_program_id, amm_program_id,
); );
Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) Ok(SpelOutput::with_chained_calls(post_states, chained_calls)
.with_timestamp_validity_window(..deadline))
} }
/// Adds liquidity to the Pool. /// Adds liquidity to the Pool.
@ -62,6 +64,7 @@ mod amm {
min_amount_liquidity: u128, min_amount_liquidity: u128,
max_amount_to_add_token_a: u128, max_amount_to_add_token_a: u128,
max_amount_to_add_token_b: u128, max_amount_to_add_token_b: u128,
deadline: u64,
) -> SpelResult { ) -> SpelResult {
let (post_states, chained_calls) = amm_program::add::add_liquidity( let (post_states, chained_calls) = amm_program::add::add_liquidity(
pool, pool,
@ -75,7 +78,8 @@ mod amm {
max_amount_to_add_token_a, max_amount_to_add_token_a,
max_amount_to_add_token_b, max_amount_to_add_token_b,
); );
Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) Ok(SpelOutput::with_chained_calls(post_states, chained_calls)
.with_timestamp_validity_window(..deadline))
} }
/// Removes liquidity from the Pool. /// Removes liquidity from the Pool.
@ -91,6 +95,7 @@ mod amm {
remove_liquidity_amount: u128, remove_liquidity_amount: u128,
min_amount_to_remove_token_a: u128, min_amount_to_remove_token_a: u128,
min_amount_to_remove_token_b: u128, min_amount_to_remove_token_b: u128,
deadline: u64,
) -> SpelResult { ) -> SpelResult {
let (post_states, chained_calls) = amm_program::remove::remove_liquidity( let (post_states, chained_calls) = amm_program::remove::remove_liquidity(
pool, pool,
@ -105,7 +110,8 @@ mod amm {
min_amount_to_remove_token_a, min_amount_to_remove_token_a,
min_amount_to_remove_token_b, min_amount_to_remove_token_b,
); );
Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) Ok(SpelOutput::with_chained_calls(post_states, chained_calls)
.with_timestamp_validity_window(..deadline))
} }
/// Swap some quantity of tokens while maintaining the pool constant product. /// Swap some quantity of tokens while maintaining the pool constant product.
@ -119,6 +125,7 @@ mod amm {
swap_amount_in: u128, swap_amount_in: u128,
min_amount_out: u128, min_amount_out: u128,
token_definition_id_in: AccountId, token_definition_id_in: AccountId,
deadline: u64,
) -> SpelResult { ) -> SpelResult {
let (post_states, chained_calls) = amm_program::swap::swap_exact_input( let (post_states, chained_calls) = amm_program::swap::swap_exact_input(
pool, pool,
@ -130,7 +137,8 @@ mod amm {
min_amount_out, min_amount_out,
token_definition_id_in, token_definition_id_in,
); );
Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) Ok(SpelOutput::with_chained_calls(post_states, chained_calls)
.with_timestamp_validity_window(..deadline))
} }
/// Swap tokens specifying the exact desired output amount. /// Swap tokens specifying the exact desired output amount.
@ -144,6 +152,7 @@ mod amm {
exact_amount_out: u128, exact_amount_out: u128,
max_amount_in: u128, max_amount_in: u128,
token_definition_id_in: AccountId, token_definition_id_in: AccountId,
deadline: u64,
) -> SpelResult { ) -> SpelResult {
let (post_states, chained_calls) = amm_program::swap::swap_exact_output( let (post_states, chained_calls) = amm_program::swap::swap_exact_output(
pool, pool,
@ -155,7 +164,8 @@ mod amm {
max_amount_in, max_amount_in,
token_definition_id_in, token_definition_id_in,
); );
Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) Ok(SpelOutput::with_chained_calls(post_states, chained_calls)
.with_timestamp_validity_window(..deadline))
} }
/// Sync pool reserves with current vault balances. /// Sync pool reserves with current vault balances.

View File

@ -70,6 +70,10 @@
{ {
"name": "amm_program_id", "name": "amm_program_id",
"type": "program_id" "type": "program_id"
},
{
"name": "deadline",
"type": "u64"
} }
] ]
}, },
@ -131,6 +135,10 @@
{ {
"name": "max_amount_to_add_token_b", "name": "max_amount_to_add_token_b",
"type": "u128" "type": "u128"
},
{
"name": "deadline",
"type": "u64"
} }
] ]
}, },
@ -192,6 +200,10 @@
{ {
"name": "min_amount_to_remove_token_b", "name": "min_amount_to_remove_token_b",
"type": "u128" "type": "u128"
},
{
"name": "deadline",
"type": "u64"
} }
] ]
}, },
@ -241,6 +253,10 @@
{ {
"name": "token_definition_id_in", "name": "token_definition_id_in",
"type": "account_id" "type": "account_id"
},
{
"name": "deadline",
"type": "u64"
} }
] ]
}, },
@ -290,6 +306,10 @@
{ {
"name": "token_definition_id_in", "name": "token_definition_id_in",
"type": "account_id" "type": "account_id"
},
{
"name": "deadline",
"type": "u64"
} }
] ]
}, },

View File

@ -2897,10 +2897,11 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework" name = "spel-framework"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde_json",
"spel-framework-core", "spel-framework-core",
"spel-framework-macros", "spel-framework-macros",
] ]
@ -2908,24 +2909,27 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework-core" name = "spel-framework-core"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"syn 2.0.117",
"thiserror 1.0.69", "thiserror 1.0.69",
] ]
[[package]] [[package]]
name = "spel-framework-macros" name = "spel-framework-macros"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_json",
"sha2", "sha2",
"spel-framework-core",
"syn 2.0.117", "syn 2.0.117",
] ]

View File

@ -10,7 +10,7 @@ name = "ata"
path = "src/bin/ata.rs" path = "src/bin/ata.rs"
[dependencies] [dependencies]
spel-framework = { git = "https://github.com/logos-co/spel.git", tag = "v0.2.0-rc.2", package = "spel-framework" } spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3", package = "spel-framework" }
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" } nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" }
risc0-zkvm = { version = "=3.0.5", default-features = false } risc0-zkvm = { version = "=3.0.5", default-features = false }
ata_core = { path = "../../core" } ata_core = { path = "../../core" }

View File

@ -966,6 +966,7 @@ fn try_execute_new_definition(
token_b_amount: Balances::vault_b_init(), token_b_amount: Balances::vault_b_init(),
fees, fees,
amm_program_id: Ids::amm_program(), amm_program_id: Ids::amm_program(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1018,6 +1019,7 @@ fn execute_swap_a_to_b(state: &mut V03State, swap_amount_in: u128, min_amount_ou
swap_amount_in, swap_amount_in,
min_amount_out, min_amount_out,
token_definition_id_in: Ids::token_a_definition(), token_definition_id_in: Ids::token_a_definition(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1045,6 +1047,7 @@ fn execute_swap_b_to_a(state: &mut V03State, swap_amount_in: u128, min_amount_ou
swap_amount_in, swap_amount_in,
min_amount_out, min_amount_out,
token_definition_id_in: Ids::token_b_definition(), token_definition_id_in: Ids::token_b_definition(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1077,6 +1080,7 @@ fn execute_add_liquidity(
min_amount_liquidity, min_amount_liquidity,
max_amount_to_add_token_a, max_amount_to_add_token_a,
max_amount_to_add_token_b, max_amount_to_add_token_b,
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1115,6 +1119,7 @@ fn execute_remove_liquidity(
remove_liquidity_amount, remove_liquidity_amount,
min_amount_to_remove_token_a, min_amount_to_remove_token_a,
min_amount_to_remove_token_b, min_amount_to_remove_token_b,
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1178,6 +1183,7 @@ fn amm_remove_liquidity() {
remove_liquidity_amount: Balances::remove_lp(), remove_liquidity_amount: Balances::remove_lp(),
min_amount_to_remove_token_a: Balances::remove_min_a(), min_amount_to_remove_token_a: Balances::remove_min_a(),
min_amount_to_remove_token_b: Balances::remove_min_b(), min_amount_to_remove_token_b: Balances::remove_min_b(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1240,6 +1246,7 @@ fn amm_remove_liquidity_insufficient_user_lp_fails() {
remove_liquidity_amount: Balances::remove_lp(), remove_liquidity_amount: Balances::remove_lp(),
min_amount_to_remove_token_a: Balances::remove_min_a(), min_amount_to_remove_token_a: Balances::remove_min_a(),
min_amount_to_remove_token_b: Balances::remove_min_b(), min_amount_to_remove_token_b: Balances::remove_min_b(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1464,6 +1471,7 @@ fn amm_add_liquidity() {
min_amount_liquidity: Balances::add_min_lp(), min_amount_liquidity: Balances::add_min_lp(),
max_amount_to_add_token_a: Balances::add_max_a(), max_amount_to_add_token_a: Balances::add_max_a(),
max_amount_to_add_token_b: Balances::add_max_b(), max_amount_to_add_token_b: Balances::add_max_b(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1526,6 +1534,7 @@ fn amm_swap_b_to_a() {
swap_amount_in: Balances::swap_amount_in(), swap_amount_in: Balances::swap_amount_in(),
min_amount_out: Balances::swap_min_out(), min_amount_out: Balances::swap_min_out(),
token_definition_id_in: Ids::token_b_definition(), token_definition_id_in: Ids::token_b_definition(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1577,6 +1586,7 @@ fn amm_swap_a_to_b() {
swap_amount_in: Balances::swap_amount_in(), swap_amount_in: Balances::swap_amount_in(),
min_amount_out: Balances::swap_min_out(), min_amount_out: Balances::swap_min_out(),
token_definition_id_in: Ids::token_a_definition(), token_definition_id_in: Ids::token_a_definition(),
deadline: u64::MAX,
}; };
let message = public_transaction::Message::try_new( let message = public_transaction::Message::try_new(
@ -1671,6 +1681,205 @@ fn amm_fee_accumulates_across_multiple_swaps_and_pays_out_on_remove() {
); );
} }
#[test]
fn amm_swap_rejects_expired_deadline() {
let mut state = state_for_amm_tests();
let deadline_ms = 1_000u64;
let block_timestamp_ms = 2_000u64;
let instruction = amm_core::Instruction::SwapExactInput {
swap_amount_in: Balances::swap_amount_in(),
min_amount_out: Balances::swap_min_out(),
token_definition_id_in: Ids::token_a_definition(),
deadline: deadline_ms,
};
let message = public_transaction::Message::try_new(
Ids::amm_program(),
vec![
Ids::pool_definition(),
Ids::vault_a(),
Ids::vault_b(),
Ids::user_a(),
Ids::user_b(),
],
vec![Nonce(0)],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a()]);
let tx = PublicTransaction::new(message, witness_set);
assert!(matches!(
state.transition_from_public_transaction(&tx, 0, block_timestamp_ms),
Err(NssaError::OutOfValidityWindow)
));
}
#[test]
fn amm_swap_exact_output_rejects_expired_deadline() {
let mut state = state_for_amm_tests();
let deadline_ms = 1_000u64;
let block_timestamp_ms = 2_000u64;
let instruction = amm_core::Instruction::SwapExactOutput {
exact_amount_out: Balances::swap_min_out(),
max_amount_in: Balances::swap_amount_in(),
token_definition_id_in: Ids::token_a_definition(),
deadline: deadline_ms,
};
let message = public_transaction::Message::try_new(
Ids::amm_program(),
vec![
Ids::pool_definition(),
Ids::vault_a(),
Ids::vault_b(),
Ids::user_a(),
Ids::user_b(),
],
vec![current_nonce(&state, Ids::user_a())],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a()]);
let tx = PublicTransaction::new(message, witness_set);
assert!(matches!(
state.transition_from_public_transaction(&tx, 0, block_timestamp_ms),
Err(NssaError::OutOfValidityWindow)
));
}
#[test]
fn amm_add_liquidity_rejects_expired_deadline() {
let mut state = state_for_amm_tests();
let deadline_ms = 1_000u64;
let block_timestamp_ms = 2_000u64;
let instruction = amm_core::Instruction::AddLiquidity {
min_amount_liquidity: Balances::add_min_lp(),
max_amount_to_add_token_a: Balances::add_max_a(),
max_amount_to_add_token_b: Balances::add_max_b(),
deadline: deadline_ms,
};
let message = public_transaction::Message::try_new(
Ids::amm_program(),
vec![
Ids::pool_definition(),
Ids::vault_a(),
Ids::vault_b(),
Ids::token_lp_definition(),
Ids::user_a(),
Ids::user_b(),
Ids::user_lp(),
],
vec![
current_nonce(&state, Ids::user_a()),
current_nonce(&state, Ids::user_b()),
],
instruction,
)
.unwrap();
let witness_set =
public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a(), &Keys::user_b()]);
let tx = PublicTransaction::new(message, witness_set);
assert!(matches!(
state.transition_from_public_transaction(&tx, 0, block_timestamp_ms),
Err(NssaError::OutOfValidityWindow)
));
}
#[test]
fn amm_remove_liquidity_rejects_expired_deadline() {
let mut state = state_for_amm_tests();
let deadline_ms = 1_000u64;
let block_timestamp_ms = 2_000u64;
let instruction = amm_core::Instruction::RemoveLiquidity {
remove_liquidity_amount: Balances::remove_lp(),
min_amount_to_remove_token_a: Balances::remove_min_a(),
min_amount_to_remove_token_b: Balances::remove_min_b(),
deadline: deadline_ms,
};
let message = public_transaction::Message::try_new(
Ids::amm_program(),
vec![
Ids::pool_definition(),
Ids::vault_a(),
Ids::vault_b(),
Ids::token_lp_definition(),
Ids::user_a(),
Ids::user_b(),
Ids::user_lp(),
],
vec![current_nonce(&state, Ids::user_lp())],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::user_lp()]);
let tx = PublicTransaction::new(message, witness_set);
assert!(matches!(
state.transition_from_public_transaction(&tx, 0, block_timestamp_ms),
Err(NssaError::OutOfValidityWindow)
));
}
#[test]
fn amm_new_definition_rejects_expired_deadline() {
let mut state = state_for_amm_tests_with_precreated_user_lp_for_new_def();
let deadline_ms = 1_000u64;
let block_timestamp_ms = 2_000u64;
let instruction = amm_core::Instruction::NewDefinition {
token_a_amount: Balances::vault_a_init(),
token_b_amount: Balances::vault_b_init(),
fees: amm_core::FEE_TIER_BPS_30,
amm_program_id: Ids::amm_program(),
deadline: deadline_ms,
};
let message = public_transaction::Message::try_new(
Ids::amm_program(),
vec![
Ids::pool_definition(),
Ids::vault_a(),
Ids::vault_b(),
Ids::token_lp_definition(),
Ids::lp_lock_holding(),
Ids::user_a(),
Ids::user_b(),
Ids::user_lp(),
],
vec![
current_nonce(&state, Ids::user_a()),
current_nonce(&state, Ids::user_b()),
current_nonce(&state, Ids::user_lp()),
],
instruction,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(
&message,
&[&Keys::user_a(), &Keys::user_b(), &Keys::user_lp()],
);
let tx = PublicTransaction::new(message, witness_set);
assert!(matches!(
state.transition_from_public_transaction(&tx, 0, block_timestamp_ms),
Err(NssaError::OutOfValidityWindow)
));
}
#[test] #[test]
fn amm_add_liquidity_after_fee_accrual() { fn amm_add_liquidity_after_fee_accrual() {
let mut state = state_for_amm_tests(); let mut state = state_for_amm_tests();

View File

@ -2864,10 +2864,11 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework" name = "spel-framework"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde_json",
"spel-framework-core", "spel-framework-core",
"spel-framework-macros", "spel-framework-macros",
] ]
@ -2875,24 +2876,27 @@ dependencies = [
[[package]] [[package]]
name = "spel-framework-core" name = "spel-framework-core"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"borsh", "borsh",
"nssa_core", "nssa_core",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"syn 2.0.117",
"thiserror 1.0.69", "thiserror 1.0.69",
] ]
[[package]] [[package]]
name = "spel-framework-macros" name = "spel-framework-macros"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/logos-co/spel.git?tag=v0.2.0-rc.2#9005e9fbbd78b0530412f9987273f753ed32eb2d" source = "git+https://github.com/logos-co/spel.git?rev=9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3#9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_json",
"sha2", "sha2",
"spel-framework-core",
"syn 2.0.117", "syn 2.0.117",
] ]

View File

@ -10,7 +10,7 @@ name = "token"
path = "src/bin/token.rs" path = "src/bin/token.rs"
[dependencies] [dependencies]
spel-framework = { git = "https://github.com/logos-co/spel.git", tag = "v0.2.0-rc.2", package = "spel-framework" } spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "9e7f2754e1d4cdb3ea36e63b1ff86c3af55488d3", package = "spel-framework" }
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" } nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" }
risc0-zkvm = { version = "=3.0.5", default-features = false } risc0-zkvm = { version = "=3.0.5", default-features = false }
token_core = { path = "../../core" } token_core = { path = "../../core" }