Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,22 @@ jobs:
${{ runner.os }}-cargo-

- name: Clippy
run: RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings
run: |
if rg -n -U --multiline '#\[(allow|expect)\([\s\S]*?reason\s*=\s*"(TODO|FIXME|fix later|later|temporary|hack)' token amm ata integration_tests tools -g '*.rs'; then
echo "Found non-actionable lint suppression reason"
exit 1
fi
Comment on lines +63 to +66
RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings

- name: Guest Clippy
run: |
for manifest in \
token/methods/guest/Cargo.toml \
amm/methods/guest/Cargo.toml \
ata/methods/guest/Cargo.toml
do
cargo clippy --manifest-path "$manifest" --all-targets -- -D warnings
done

unit-tests:
name: Unit Tests
Expand Down
38 changes: 38 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,41 @@ borsh = { version = "1.0", features = ["derive"] }
risc0-zkvm = { version = "=3.0.5" }
serde_json = "1.0"
tokio = { version = "1.28.2", features = ["net", "rt-multi-thread", "sync", "macros"] }

[workspace.lints.rust]
rust_2018_idioms = { level = "deny", priority = -1 }
unsafe_code = "forbid"

[workspace.lints.clippy]
all = { level = "deny", priority = -1 }

# Generated-code / placeholder blockers.
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"
unwrap_used = "deny"

# Lint suppression hygiene.
allow_attributes = "warn"
allow_attributes_without_reason = "deny"

# Determinism, panic-safety, and arithmetic correctness.
arithmetic_side_effects = "deny"
indexing_slicing = "deny"
integer_division = "warn"

# Cast discipline.
as_conversions = "deny"
cast_possible_truncation = "deny"
cast_possible_wrap = "deny"
cast_precision_loss = "warn"
cast_sign_loss = "deny"

# API and enum evolution.
large_enum_variant = "deny"
match_wildcard_for_single_variants = "warn"
wildcard_enum_match_arm = "deny"

# Too noisy for this codebase unless enforced selectively.
module_name_repetitions = "allow"
similar_names = "allow"
3 changes: 3 additions & 0 deletions amm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "amm_program"
version = "0.1.0"
edition = "2021"

[lints]
workspace = true

[dependencies]
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1", features = ["host"] }
amm_core = { path = "core" }
Expand Down
3 changes: 3 additions & 0 deletions amm/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "amm_core"
version = "0.1.0"
edition = "2021"

[lints]
workspace = true

[dependencies]
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1", features = ["host"] }
token_core = { path = "../../token/core" }
Expand Down
20 changes: 12 additions & 8 deletions amm/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,9 @@ pub fn compute_pool_pda_seed(
};

let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&token_1.to_bytes());
bytes[32..].copy_from_slice(&token_2.to_bytes());
let (token_1_bytes, token_2_bytes) = bytes.split_at_mut(32);
token_1_bytes.copy_from_slice(&token_1.to_bytes());
token_2_bytes.copy_from_slice(&token_2.to_bytes());

PdaSeed::new(
Impl::hash_bytes(&bytes)
Expand All @@ -229,8 +230,9 @@ pub fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId
use risc0_zkvm::sha::{Impl, Sha256};

let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
bytes[32..].copy_from_slice(&definition_token_id.to_bytes());
let (pool_bytes, definition_bytes) = bytes.split_at_mut(32);
pool_bytes.copy_from_slice(&pool_id.to_bytes());
definition_bytes.copy_from_slice(&definition_token_id.to_bytes());

PdaSeed::new(
Impl::hash_bytes(&bytes)
Expand All @@ -248,8 +250,9 @@ pub fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256};

let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
bytes[32..].copy_from_slice(&LIQUIDITY_TOKEN_PDA_SEED);
let (pool_bytes, seed_bytes) = bytes.split_at_mut(32);
pool_bytes.copy_from_slice(&pool_id.to_bytes());
seed_bytes.copy_from_slice(&LIQUIDITY_TOKEN_PDA_SEED);

PdaSeed::new(
Impl::hash_bytes(&bytes)
Expand All @@ -267,8 +270,9 @@ pub fn compute_lp_lock_holding_pda_seed(pool_id: AccountId) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256};

let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
bytes[32..].copy_from_slice(&LP_LOCK_HOLDING_PDA_SEED);
let (pool_bytes, seed_bytes) = bytes.split_at_mut(32);
pool_bytes.copy_from_slice(&pool_id.to_bytes());
seed_bytes.copy_from_slice(&LP_LOCK_HOLDING_PDA_SEED);

PdaSeed::new(
Impl::hash_bytes(&bytes)
Expand Down
3 changes: 3 additions & 0 deletions amm/methods/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "amm-methods"
version = "0.1.0"
edition = "2021"

[lints]
workspace = true

[build-dependencies]
risc0-build = "=3.0.5"

Expand Down
26 changes: 26 additions & 0 deletions amm/methods/guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ edition = "2021"

[workspace]

[lints.rust]
rust_2018_idioms = { level = "deny", priority = -1 }
unsafe_code = "forbid"

[lints.clippy]
all = { level = "deny", priority = -1 }
allow_attributes = "warn"
allow_attributes_without_reason = "deny"
arithmetic_side_effects = "deny"
as_conversions = "deny"
cast_possible_truncation = "deny"
cast_possible_wrap = "deny"
cast_precision_loss = "warn"
cast_sign_loss = "deny"
dbg_macro = "deny"
indexing_slicing = "deny"
integer_division = "warn"
large_enum_variant = "deny"
match_wildcard_for_single_variants = "warn"
module_name_repetitions = "allow"
similar_names = "allow"
todo = "deny"
unimplemented = "deny"
unwrap_used = "deny"
wildcard_enum_match_arm = "deny"

[[bin]]
name = "amm"
path = "src/bin/amm.rs"
Expand Down
52 changes: 50 additions & 2 deletions amm/methods/guest/src/bin/amm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![no_main]
#![cfg_attr(not(test), no_main)]

use std::num::NonZeroU128;

Expand All @@ -8,15 +8,27 @@ use nssa_core::{
program::ProgramId,
};

#[cfg(not(test))]
risc0_zkvm::guest::entry!(main);

#[lez_program(instruction = "amm_core::Instruction")]
mod amm {
#[allow(unused_imports)]
#[expect(
unused_imports,
reason = "SPEL instruction macro requires importing parent-scope handler types"
)]
use super::*;

/// Initializes a new Pool (or re-initializes an existing zero-supply Pool).
/// A fresh user LP holding must be explicitly authorized by the caller.
#[expect(
clippy::too_many_arguments,
reason = "instruction ABI exposes explicit pool, vault, mint, lock, and user accounts"
)]
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn new_definition(
pool: AccountWithMetadata,
Expand Down Expand Up @@ -52,6 +64,14 @@ mod amm {
}

/// Adds liquidity to the Pool.
#[expect(
clippy::too_many_arguments,
reason = "instruction ABI exposes explicit pool, vault, and user accounts"
)]
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn add_liquidity(
pool: AccountWithMetadata,
Expand Down Expand Up @@ -83,6 +103,14 @@ mod amm {
}

/// Removes liquidity from the Pool.
#[expect(
clippy::too_many_arguments,
reason = "instruction ABI exposes explicit pool, vault, and user accounts"
)]
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn remove_liquidity(
pool: AccountWithMetadata,
Expand Down Expand Up @@ -115,6 +143,14 @@ mod amm {
}

/// Swap some quantity of tokens while maintaining the pool constant product.
#[expect(
clippy::too_many_arguments,
reason = "instruction ABI exposes explicit pool, vault, user accounts, and bounds"
)]
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn swap_exact_input(
pool: AccountWithMetadata,
Expand Down Expand Up @@ -142,6 +178,14 @@ mod amm {
}

/// Swap tokens specifying the exact desired output amount.
#[expect(
clippy::too_many_arguments,
reason = "instruction ABI exposes explicit pool, vault, user accounts, and bounds"
)]
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn swap_exact_output(
pool: AccountWithMetadata,
Expand Down Expand Up @@ -169,6 +213,10 @@ mod amm {
}

/// Sync pool reserves with current vault balances.
#[expect(
deprecated,
reason = "AMM guest chained-call wrapper remains until auto-claim migration is scoped"
)]
#[instruction]
pub fn sync_reserves(
pool: AccountWithMetadata,
Expand Down
17 changes: 12 additions & 5 deletions amm/src/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use nssa_core::{
program::{AccountPostState, ChainedCall},
};

#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
#[expect(
clippy::too_many_arguments,
reason = "instruction surface passes explicit pool, vault, and user accounts"
)]
pub fn add_liquidity(
pool: AccountWithMetadata,
vault_a: AccountWithMetadata,
Expand Down Expand Up @@ -67,12 +70,14 @@ pub fn add_liquidity(
.reserve_a
.checked_mul(max_amount_to_add_token_b)
.expect("reserve_a * max_amount_b overflows u128")
/ pool_def_data.reserve_b;
.checked_div(pool_def_data.reserve_b)
.expect("reserve_b must be nonzero after validation");
let ideal_b: u128 = pool_def_data
.reserve_b
.checked_mul(max_amount_to_add_token_a)
.expect("reserve_b * max_amount_a overflows u128")
/ pool_def_data.reserve_a;
.checked_div(pool_def_data.reserve_a)
.expect("reserve_a must be nonzero after validation");

let actual_amount_a = if ideal_a > max_amount_to_add_token_a {
max_amount_to_add_token_a
Expand Down Expand Up @@ -104,12 +109,14 @@ pub fn add_liquidity(
.liquidity_pool_supply
.checked_mul(actual_amount_a)
.expect("liquidity_pool_supply * actual_amount_a overflows u128")
/ pool_def_data.reserve_a,
.checked_div(pool_def_data.reserve_a)
.expect("reserve_a must be nonzero after validation"),
pool_def_data
.liquidity_pool_supply
.checked_mul(actual_amount_b)
.expect("liquidity_pool_supply * actual_amount_b overflows u128")
/ pool_def_data.reserve_b,
.checked_div(pool_def_data.reserve_b)
.expect("reserve_b must be nonzero after validation"),
);

assert!(delta_lp != 0, "Payable LP must be nonzero");
Expand Down
9 changes: 7 additions & 2 deletions amm/src/new_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use nssa_core::{
};
use token_core::TokenDefinition;

#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
#[expect(
clippy::too_many_arguments,
reason = "instruction surface passes explicit pool, vault, mint, lock, and user accounts"
)]
pub fn new_definition(
pool: AccountWithMetadata,
vault_a: AccountWithMetadata,
Expand Down Expand Up @@ -94,7 +97,9 @@ pub fn new_definition(
initial_lp > MINIMUM_LIQUIDITY,
"Initial liquidity must exceed minimum liquidity lock"
);
let user_lp = initial_lp - MINIMUM_LIQUIDITY;
let user_lp = initial_lp
.checked_sub(MINIMUM_LIQUIDITY)
.expect("initial liquidity must exceed minimum liquidity after validation");

// Update pool account
let mut pool_post = pool.account.clone();
Expand Down
16 changes: 12 additions & 4 deletions amm/src/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use nssa_core::{
program::{AccountPostState, ChainedCall},
};

#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
#[expect(
clippy::too_many_arguments,
reason = "instruction surface passes explicit pool, vault, and user accounts"
)]
pub fn remove_liquidity(
pool: AccountWithMetadata,
vault_a: AccountWithMetadata,
Expand Down Expand Up @@ -91,7 +94,10 @@ pub fn remove_liquidity(
pool_def_data.liquidity_pool_supply > MINIMUM_LIQUIDITY,
"Pool only contains locked liquidity"
);
let unlocked_liquidity = pool_def_data.liquidity_pool_supply - MINIMUM_LIQUIDITY;
let unlocked_liquidity = pool_def_data
.liquidity_pool_supply
.checked_sub(MINIMUM_LIQUIDITY)
.expect("liquidity supply must be at least the locked minimum after validation");
// The remove instruction never sees the LP lock account directly, so we must still refuse any
// request that would burn through the permanent floor even if ownership is already corrupted.
assert!(
Expand All @@ -103,12 +109,14 @@ pub fn remove_liquidity(
.reserve_a
.checked_mul(remove_liquidity_amount)
.expect("reserve_a * remove_liquidity_amount overflows u128")
/ pool_def_data.liquidity_pool_supply;
.checked_div(pool_def_data.liquidity_pool_supply)
.expect("liquidity supply must be nonzero after validation");
let withdraw_amount_b = pool_def_data
.reserve_b
.checked_mul(remove_liquidity_amount)
.expect("reserve_b * remove_liquidity_amount overflows u128")
/ pool_def_data.liquidity_pool_supply;
.checked_div(pool_def_data.liquidity_pool_supply)
.expect("liquidity supply must be nonzero after validation");

// 3. Validate and slippage check
assert!(
Expand Down
Loading