mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-05-22 08:59:39 +00:00
chore(lint): add staged lint baseline
This commit is contained in:
parent
035f593f5e
commit
a3163dbe58
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@ -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
|
||||
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
|
||||
|
||||
38
Cargo.toml
38
Cargo.toml
@ -39,3 +39,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,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-rc3", features = ["host"] }
|
||||
amm_core = { path = "core" }
|
||||
|
||||
@ -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-rc3", features = ["host"] }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", tag = "v0.3.0", package = "spel-framework-macros" }
|
||||
|
||||
@ -204,8 +204,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)
|
||||
@ -230,8 +231,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)
|
||||
@ -249,8 +251,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)
|
||||
@ -268,8 +271,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)
|
||||
|
||||
@ -3,6 +3,9 @@ name = "amm-methods"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = "=3.0.5"
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![no_main]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
|
||||
use std::num::NonZeroU128;
|
||||
|
||||
@ -8,15 +8,23 @@ use nssa_core::{
|
||||
account::{AccountId, AccountWithMetadata},
|
||||
};
|
||||
|
||||
#[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 interface requires explicit pool, vault, mint, lock, and user accounts"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn new_definition(
|
||||
ctx: ProgramContext,
|
||||
@ -52,6 +60,10 @@ mod amm {
|
||||
}
|
||||
|
||||
/// Adds liquidity to the Pool.
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction interface requires explicit pool, vault, and user accounts"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn add_liquidity(
|
||||
pool: AccountWithMetadata,
|
||||
@ -83,6 +95,10 @@ mod amm {
|
||||
}
|
||||
|
||||
/// Removes liquidity from the Pool.
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction interface requires explicit pool, vault, and user accounts"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn remove_liquidity(
|
||||
pool: AccountWithMetadata,
|
||||
@ -115,6 +131,10 @@ mod amm {
|
||||
}
|
||||
|
||||
/// Swap some quantity of tokens while maintaining the pool constant product.
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction interface requires explicit pool, vault, user accounts, and bounds"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn swap_exact_input(
|
||||
pool: AccountWithMetadata,
|
||||
@ -142,6 +162,10 @@ mod amm {
|
||||
}
|
||||
|
||||
/// Swap tokens specifying the exact desired output amount.
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction interface requires explicit pool, vault, user accounts, and bounds"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn swap_exact_output(
|
||||
pool: AccountWithMetadata,
|
||||
|
||||
@ -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,
|
||||
@ -77,12 +80,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
|
||||
@ -114,12 +119,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");
|
||||
|
||||
@ -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,
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
@ -105,7 +108,10 @@ pub fn remove_liquidity(
|
||||
remove_liquidity_amount <= user_lp_balance,
|
||||
"Remove amount exceeds user LP balance"
|
||||
);
|
||||
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!(
|
||||
@ -117,12 +123,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!(
|
||||
|
||||
@ -46,7 +46,10 @@ fn validate_swap_setup(
|
||||
}
|
||||
|
||||
/// Creates post-state and returns reserves after swap.
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "post-state assembly keeps pool, vault, user account, and delta state explicit"
|
||||
)]
|
||||
#[expect(
|
||||
clippy::needless_pass_by_value,
|
||||
reason = "consistent with codebase style"
|
||||
@ -91,7 +94,10 @@ fn create_swap_post_states(
|
||||
]
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction surface passes explicit pool, vault, and user accounts"
|
||||
)]
|
||||
#[must_use]
|
||||
pub fn swap_exact_input(
|
||||
pool: AccountWithMetadata,
|
||||
@ -166,7 +172,10 @@ pub fn swap_exact_input(
|
||||
(post_states, chained_calls)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "swap calculation keeps account context and pricing parameters explicit"
|
||||
)]
|
||||
fn swap_logic(
|
||||
user_deposit: AccountWithMetadata,
|
||||
vault_deposit: AccountWithMetadata,
|
||||
@ -179,10 +188,14 @@ fn swap_logic(
|
||||
reserve_withdraw_vault_amount: u128,
|
||||
pool_id: AccountId,
|
||||
) -> (Vec<ChainedCall>, u128, u128) {
|
||||
let fee_multiplier = FEE_BPS_DENOMINATOR
|
||||
.checked_sub(fee_bps)
|
||||
.expect("fee_bps exceeds fee denominator");
|
||||
let effective_amount_in = swap_amount_in
|
||||
.checked_mul(FEE_BPS_DENOMINATOR - fee_bps)
|
||||
.checked_mul(fee_multiplier)
|
||||
.expect("swap_amount_in * (FEE_BPS_DENOMINATOR - fee_bps) overflows u128")
|
||||
/ FEE_BPS_DENOMINATOR;
|
||||
.checked_div(FEE_BPS_DENOMINATOR)
|
||||
.expect("fee denominator must be nonzero");
|
||||
assert!(
|
||||
effective_amount_in != 0,
|
||||
"Effective swap amount should be nonzero"
|
||||
@ -194,9 +207,12 @@ fn swap_logic(
|
||||
let withdraw_amount = reserve_withdraw_vault_amount
|
||||
.checked_mul(effective_amount_in)
|
||||
.expect("reserve * effective_amount_in overflows u128")
|
||||
/ reserve_deposit_vault_amount
|
||||
.checked_add(effective_amount_in)
|
||||
.expect("reserve + effective_amount_in overflows u128");
|
||||
.checked_div(
|
||||
reserve_deposit_vault_amount
|
||||
.checked_add(effective_amount_in)
|
||||
.expect("reserve + effective_amount_in overflows u128"),
|
||||
)
|
||||
.expect("reserve plus effective input must be nonzero");
|
||||
|
||||
// Slippage check
|
||||
assert!(
|
||||
@ -240,7 +256,10 @@ fn swap_logic(
|
||||
(chained_calls, swap_amount_in, withdraw_amount)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "instruction surface passes explicit pool, vault, and user accounts"
|
||||
)]
|
||||
#[must_use]
|
||||
pub fn swap_exact_output(
|
||||
pool: AccountWithMetadata,
|
||||
@ -315,7 +334,10 @@ pub fn swap_exact_output(
|
||||
(post_states, chained_calls)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "swap calculation keeps account context and pricing parameters explicit"
|
||||
)]
|
||||
fn exact_output_swap_logic(
|
||||
user_deposit: AccountWithMetadata,
|
||||
vault_deposit: AccountWithMetadata,
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
#![cfg(test)]
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
clippy::integer_division,
|
||||
reason = "test fixtures use fixed values to lock AMM math boundaries"
|
||||
)]
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
@ -3228,7 +3233,8 @@ fn remove_liquidity_overflow_protection() {
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
AccountWithMetadataForTests::user_holding_b(),
|
||||
user_lp,
|
||||
NonZero::new(2).unwrap(), // remove_amount=2 → reserve_a * 2 overflows
|
||||
NonZero::new(2).unwrap(), /* remove_amount=2 → reserve_a * 2
|
||||
* overflows */
|
||||
1,
|
||||
1,
|
||||
);
|
||||
@ -3365,7 +3371,7 @@ fn test_add_liquidity_rejects_user_holding_a_wrong_program() {
|
||||
AccountWithMetadataForTests::user_holding_a_wrong_program(),
|
||||
AccountWithMetadataForTests::user_holding_b(),
|
||||
AccountWithMetadataForTests::user_holding_lp_init(),
|
||||
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
|
||||
NonZero::new(BalanceForTests::add_min_amount_lp()).expect("test value must be nonzero"),
|
||||
BalanceForTests::add_max_amount_a(),
|
||||
BalanceForTests::add_max_amount_b(),
|
||||
);
|
||||
@ -3382,7 +3388,7 @@ fn test_add_liquidity_rejects_user_holding_b_wrong_program() {
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
AccountWithMetadataForTests::user_holding_b_wrong_program(),
|
||||
AccountWithMetadataForTests::user_holding_lp_init(),
|
||||
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
|
||||
NonZero::new(BalanceForTests::add_min_amount_lp()).expect("test value must be nonzero"),
|
||||
BalanceForTests::add_max_amount_a(),
|
||||
BalanceForTests::add_max_amount_b(),
|
||||
);
|
||||
@ -3401,7 +3407,7 @@ fn test_remove_liquidity_rejects_user_holding_a_wrong_program() {
|
||||
AccountWithMetadataForTests::user_holding_lp_with_balance(
|
||||
BalanceForTests::remove_amount_lp(),
|
||||
),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).expect("test value must be nonzero"),
|
||||
BalanceForTests::remove_min_amount_a(),
|
||||
BalanceForTests::remove_min_amount_b_low(),
|
||||
);
|
||||
@ -3420,7 +3426,7 @@ fn test_remove_liquidity_rejects_user_holding_b_wrong_program() {
|
||||
AccountWithMetadataForTests::user_holding_lp_with_balance(
|
||||
BalanceForTests::remove_amount_lp(),
|
||||
),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).expect("test value must be nonzero"),
|
||||
BalanceForTests::remove_min_amount_a(),
|
||||
BalanceForTests::remove_min_amount_b_low(),
|
||||
);
|
||||
@ -3438,7 +3444,7 @@ fn test_remove_liquidity_rejects_amount_exceeding_user_lp_balance() {
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
AccountWithMetadataForTests::user_holding_b(),
|
||||
AccountWithMetadataForTests::user_holding_lp_with_balance(lp_balance),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
|
||||
NonZero::new(BalanceForTests::remove_amount_lp()).expect("test value must be nonzero"),
|
||||
BalanceForTests::remove_min_amount_a(),
|
||||
BalanceForTests::remove_min_amount_b_low(),
|
||||
);
|
||||
|
||||
@ -3,6 +3,9 @@ name = "ata_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-rc3", features = ["host"] }
|
||||
ata_core = { path = "core" }
|
||||
|
||||
@ -3,6 +3,9 @@ name = "ata_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-rc3", features = ["host"] }
|
||||
borsh = { version = "1.5", features = ["derive"] }
|
||||
|
||||
@ -60,12 +60,16 @@ pub fn compute_ata_seed(
|
||||
) -> PdaSeed {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
let mut bytes = [0u8; 96];
|
||||
for (index, word) in token_program_id.iter().enumerate() {
|
||||
let offset = index * 4;
|
||||
bytes[offset..offset + 4].copy_from_slice(&word.to_le_bytes());
|
||||
let (program_id_bytes, rest) = bytes.split_at_mut(32);
|
||||
let (owner_bytes, definition_bytes) = rest.split_at_mut(32);
|
||||
for (chunk, word) in program_id_bytes
|
||||
.chunks_exact_mut(4)
|
||||
.zip(token_program_id.iter())
|
||||
{
|
||||
chunk.copy_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
bytes[32..64].copy_from_slice(&owner_id.to_bytes());
|
||||
bytes[64..96].copy_from_slice(&definition_id.to_bytes());
|
||||
owner_bytes.copy_from_slice(&owner_id.to_bytes());
|
||||
definition_bytes.copy_from_slice(&definition_id.to_bytes());
|
||||
PdaSeed::new(
|
||||
Impl::hash_bytes(&bytes)
|
||||
.as_bytes()
|
||||
|
||||
@ -3,6 +3,9 @@ name = "ata-methods"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = "=3.0.5"
|
||||
|
||||
|
||||
@ -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 = "ata"
|
||||
path = "src/bin/ata.rs"
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
#![no_main]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
|
||||
use spel_framework::prelude::*;
|
||||
use spel_framework::context::ProgramContext;
|
||||
use nssa_core::{account::AccountWithMetadata, program::ProgramId};
|
||||
|
||||
#[cfg(not(test))]
|
||||
risc0_zkvm::guest::entry!(main);
|
||||
|
||||
#[lez_program(instruction = "ata_core::Instruction")]
|
||||
mod ata {
|
||||
#[allow(unused_imports)]
|
||||
#[expect(
|
||||
unused_imports,
|
||||
reason = "SPEL instruction macro requires importing parent-scope handler types"
|
||||
)]
|
||||
use super::*;
|
||||
|
||||
/// Create the Associated Token Account for (token program, owner, definition).
|
||||
|
||||
11
clippy.toml
Normal file
11
clippy.toml
Normal file
@ -0,0 +1,11 @@
|
||||
msrv = "1.94.0"
|
||||
|
||||
avoid-breaking-exported-api = false
|
||||
check-private-items = true
|
||||
warn-on-all-wildcard-imports = true
|
||||
|
||||
allow-dbg-in-tests = false
|
||||
allow-expect-in-tests = true
|
||||
allow-indexing-slicing-in-tests = true
|
||||
allow-print-in-tests = true
|
||||
allow-unwrap-in-tests = true
|
||||
@ -3,6 +3,9 @@ name = "integration_tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nssa = { workspace = true }
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "integration fixtures use fixed balances to assert AMM state transitions"
|
||||
)]
|
||||
|
||||
use amm_core::{
|
||||
PoolDefinition, FEE_TIER_BPS_1, FEE_TIER_BPS_100, FEE_TIER_BPS_30, FEE_TIER_BPS_5,
|
||||
MINIMUM_LIQUIDITY,
|
||||
@ -956,6 +961,7 @@ fn state_for_amm_tests_with_precreated_user_lp_for_new_def() -> V03State {
|
||||
state
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn try_execute_new_definition(
|
||||
state: &mut V03State,
|
||||
fees: u128,
|
||||
@ -1009,10 +1015,12 @@ fn try_execute_new_definition(
|
||||
state.transition_from_public_transaction(&tx, 0, 0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn execute_new_definition(state: &mut V03State, fees: u128) {
|
||||
try_execute_new_definition(state, fees, true).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn execute_swap_a_to_b(state: &mut V03State, swap_amount_in: u128, min_amount_out: u128) {
|
||||
let instruction = amm_core::Instruction::SwapExactInput {
|
||||
swap_amount_in,
|
||||
@ -1041,6 +1049,7 @@ fn execute_swap_a_to_b(state: &mut V03State, swap_amount_in: u128, min_amount_ou
|
||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn execute_swap_b_to_a(state: &mut V03State, swap_amount_in: u128, min_amount_out: u128) {
|
||||
let instruction = amm_core::Instruction::SwapExactInput {
|
||||
swap_amount_in,
|
||||
@ -1069,6 +1078,7 @@ fn execute_swap_b_to_a(state: &mut V03State, swap_amount_in: u128, min_amount_ou
|
||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn execute_add_liquidity(
|
||||
state: &mut V03State,
|
||||
min_amount_liquidity: u128,
|
||||
@ -1108,6 +1118,7 @@ fn execute_add_liquidity(
|
||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn execute_remove_liquidity(
|
||||
state: &mut V03State,
|
||||
remove_liquidity_amount: u128,
|
||||
|
||||
@ -648,6 +648,7 @@ fn token_program() -> Program {
|
||||
/// Performs a shielded transfer (public → private) of `amount` tokens from
|
||||
/// `Ids::holder()` to a new private account keyed by `PrivateKeys::recipient_*`.
|
||||
/// Returns the resulting private recipient account.
|
||||
#[cfg(test)]
|
||||
fn shielded_token_transfer(amount: u128, state: &mut V03State) -> Account {
|
||||
let sender_id = Ids::holder();
|
||||
let sender_account = state.get_account_by_id(sender_id);
|
||||
|
||||
@ -3,6 +3,9 @@ name = "token_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-rc3", features = ["host"] }
|
||||
token_core = { path = "core" }
|
||||
|
||||
@ -3,6 +3,9 @@ name = "token_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-rc3", features = ["host"] }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", tag = "v0.3.0", package = "spel-framework-macros" }
|
||||
|
||||
@ -3,6 +3,9 @@ name = "token-methods"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = "=3.0.5"
|
||||
|
||||
|
||||
@ -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 = "token"
|
||||
path = "src/bin/token.rs"
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
#![no_main]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
|
||||
use spel_framework::prelude::*;
|
||||
use spel_framework::context::ProgramContext;
|
||||
use nssa_core::account::AccountWithMetadata;
|
||||
|
||||
#[cfg(not(test))]
|
||||
risc0_zkvm::guest::entry!(main);
|
||||
|
||||
#[lez_program(instruction = "token_core::Instruction")]
|
||||
mod token {
|
||||
#[allow(unused_imports)]
|
||||
#[expect(
|
||||
unused_imports,
|
||||
reason = "SPEL instruction macro requires importing parent-scope handler types"
|
||||
)]
|
||||
use super::*;
|
||||
|
||||
/// Transfer tokens from sender to recipient.
|
||||
@ -48,6 +52,10 @@ mod token {
|
||||
|
||||
/// Create a new fungible or non-fungible token definition with metadata.
|
||||
/// Definition, holding, and metadata targets must be uninitialized and authorized.
|
||||
#[expect(
|
||||
clippy::boxed_local,
|
||||
reason = "boxed metadata keeps the instruction argument size bounded on the stack"
|
||||
)]
|
||||
#[instruction]
|
||||
pub fn new_definition_with_metadata(
|
||||
definition_target_account: AccountWithMetadata,
|
||||
|
||||
@ -40,7 +40,9 @@ pub fn print_nft(
|
||||
*print_balance > 1,
|
||||
"Insufficient balance to print another NFT copy"
|
||||
);
|
||||
*print_balance -= 1;
|
||||
*print_balance = print_balance
|
||||
.checked_sub(1)
|
||||
.expect("print balance must be greater than one after validation");
|
||||
|
||||
let mut master_account_post = master_account.account;
|
||||
master_account_post.data = Data::from(&master_account_data);
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
#![cfg(test)]
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "test fixtures use fixed values to lock boundary behavior"
|
||||
)]
|
||||
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata, Data, Nonce},
|
||||
|
||||
@ -3,6 +3,9 @@ name = "idl-gen"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "idl-gen"
|
||||
path = "src/main.rs"
|
||||
|
||||
@ -16,7 +16,13 @@ fn main() {
|
||||
let dep_dirs = find_path_dep_dirs(&path);
|
||||
|
||||
match spel_framework_core::idl_gen::generate_idl_from_file_with_deps(&path, &dep_dirs) {
|
||||
Ok(idl) => println!("{}", serde_json::to_string_pretty(&idl).unwrap()),
|
||||
Ok(idl) => match serde_json::to_string_pretty(&idl) {
|
||||
Ok(json) => println!("{json}"),
|
||||
Err(e) => {
|
||||
eprintln!("Error serializing IDL to JSON: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
process::exit(1);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user