Skip to content
Draft
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
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
members = [
"programs/common/revert",
"programs/common/revert-macros",
"modules/amm/ffi",
"modules/stablecoin/ffi",
"modules/token/ffi",
Expand Down Expand Up @@ -93,4 +95,4 @@ wildcard_enum_match_arm = "deny"

# Too noisy for this codebase unless enforced selectively.
module_name_repetitions = "allow"
similar_names = "allow"
similar_names = "allow"
1 change: 1 addition & 0 deletions programs/amm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
workspace = true

[dependencies]
program-revert = { path = "../common/revert" }
lee_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.4", features = ["host"] }
clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.4" }
amm_core = { path = "core" }
Expand Down
1 change: 1 addition & 0 deletions programs/amm/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
workspace = true

[dependencies]
program-revert = { path = "../../common/revert" }
lee_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.4", features = ["host"] }
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", rev = "1ef0500f8fc8ce3ddf95523726ae159db83f744f", package = "spel-framework-macros" }
token_core = { path = "../../token/core" }
Expand Down
13 changes: 13 additions & 0 deletions programs/amm/core/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Nonzero exit codes for the amm program.
//!
//! Codes are local to this program. Keep existing values stable when adding errors.
//! Diagnostic logs identify the failed check within each category.

use std::num::NonZeroU8;

/// Invalid instruction, account, authorization, configuration, or operation state.
pub const INVALID_INPUT: NonZeroU8 = NonZeroU8::MIN;
/// Requested amount exceeds the available balance, debt, or collateral.
pub const INSUFFICIENT_BALANCE: NonZeroU8 = NonZeroU8::new(2).expect("nonzero error code");
/// Requested operation exceeds the representable arithmetic range.
pub const ARITHMETIC: NonZeroU8 = NonZeroU8::new(3).expect("nonzero error code");
67 changes: 49 additions & 18 deletions programs/amm/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! This crate contains core data structures and utilities for the AMM Program.

use program_revert::UnwrapOrRevert as _;
pub mod error;

use borsh::{BorshDeserialize, BorshSerialize};
use lee_core::{
account::{AccountId, AccountWithMetadata, Data},
Expand Down Expand Up @@ -276,7 +279,8 @@ pub fn is_supported_fee_tier(fees: u128) -> bool {
}

pub fn assert_supported_fee_tier(fees: u128) {
assert!(
program_revert::require!(
error::INVALID_INPUT,
is_supported_fee_tier(fees),
"Fee tier must be one of 1, 5, 30, or 100 basis points"
);
Expand All @@ -290,13 +294,16 @@ pub fn assert_supported_fee_tier(fees: u128) {
/// oracle consumes exactly this representation (it converts the `Q64.64` price to a tick), so the
/// AMM owns the reserves → price mapping and the oracle stays agnostic to how the price is formed.
///
/// # Panics
/// Panics if `reserve_base` is zero.
/// # Failures
/// Reverts in the zkVM; panics on native targets.
///
/// Reverts in the zkVM (panics on native targets) if `reserve_base` is zero.
#[must_use]
pub fn spot_price_q64_64(reserve_base: u128, reserve_quote: u128) -> u128 {
use alloy_primitives::U256;

assert!(
program_revert::require!(
error::INVALID_INPUT,
reserve_base != 0,
"spot_price_q64_64: reserve_base must be non-zero"
);
Expand All @@ -314,35 +321,47 @@ pub fn spot_price_q64_64(reserve_base: u128, reserve_quote: u128) -> u128 {
/// `floor(a * b / c)` computed in U256 so the `a * b` product can't overflow u128.
/// (Storage stays u128; only the intermediate widens.)
///
/// # Panics
/// Panics if `c` is zero, or if the result exceeds u128.
/// # Failures
/// Reverts in the zkVM; panics on native targets.
///
/// Reverts in the zkVM (panics on native targets) if `c` is zero, or if the result exceeds u128.
#[must_use]
pub fn mul_div_floor(a: u128, b: u128, c: u128) -> u128 {
use alloy_primitives::U256;
assert!(c != 0, "mul_div_floor: divisor must be non-zero");
program_revert::require!(
error::INVALID_INPUT,
c != 0,
"mul_div_floor: divisor must be non-zero"
);
let product = U256::from(a)
.checked_mul(U256::from(b))
.expect("u128 * u128 always fits in U256");
let result = product
.checked_div(U256::from(c))
.expect("mul_div_floor: divisor is non-zero after the assertion above");
u128::try_from(result).expect("mul_div_floor result exceeds u128")
u128::try_from(result).unwrap_or_revert(error::ARITHMETIC, "mul_div_floor result exceeds u128")
}

/// `ceil(a * b / c)` computed in U256 so the `a * b` product can't overflow u128.
/// (Storage stays u128; only the intermediate widens.)
///
/// # Panics
/// Panics if `c` is zero, or if the result exceeds u128.
/// # Failures
/// Reverts in the zkVM; panics on native targets.
///
/// Reverts in the zkVM (panics on native targets) if `c` is zero, or if the result exceeds u128.
#[must_use]
pub fn mul_div_ceil(a: u128, b: u128, c: u128) -> u128 {
use alloy_primitives::U256;
assert!(c != 0, "mul_div_ceil: divisor must be non-zero");
program_revert::require!(
error::INVALID_INPUT,
c != 0,
"mul_div_ceil: divisor must be non-zero"
);
let product = U256::from(a)
.checked_mul(U256::from(b))
.expect("u128 * u128 always fits in U256");
let result = product.div_ceil(U256::from(c));
u128::try_from(result).expect("mul_div_ceil result exceeds u128")
u128::try_from(result).unwrap_or_revert(error::ARITHMETIC, "mul_div_ceil result exceeds u128")
}

/// Adverse price impact of a swap in basis points: how far `amount_out` falls
Expand Down Expand Up @@ -458,8 +477,10 @@ pub fn swap_exact_out_amounts(

/// `floor(sqrt(a * b))` computed in U256 so the `a * b` product can't overflow u128.
///
/// # Panics
/// Panics if the result exceeds u128.
/// # Failures
/// Reverts in the zkVM; panics on native targets.
///
/// Reverts in the zkVM (panics on native targets) if the result exceeds u128.
#[must_use]
pub fn isqrt_product(a: u128, b: u128) -> u128 {
use alloy_primitives::U256;
Expand Down Expand Up @@ -572,7 +593,9 @@ pub fn compute_pool_pda_seed(
{
std::cmp::Ordering::Less => (definition_token_b_id, definition_token_a_id),
std::cmp::Ordering::Greater => (definition_token_a_id, definition_token_b_id),
std::cmp::Ordering::Equal => panic!("Definitions match"),
std::cmp::Ordering::Equal => {
program_revert::revert!(error::INVALID_INPUT, "Definitions match")
}
};

let mut bytes = [0; 64];
Expand Down Expand Up @@ -654,15 +677,23 @@ pub fn compute_lp_lock_holding_pda_seed(pool_id: AccountId) -> PdaSeed {
}

fn read_fungible_holding(account: &AccountWithMetadata, context: &str) -> (AccountId, u128) {
let token_holding = token_core::TokenHolding::try_from(&account.account.data)
.unwrap_or_else(|_| panic!("{context}: AMM Program expects a valid Token Holding Account"));
let token_holding =
token_core::TokenHolding::try_from(&account.account.data).unwrap_or_else(|_| {
program_revert::revert!(
error::INVALID_INPUT,
"{context}: AMM Program expects a valid Token Holding Account"
)
});

let token_core::TokenHolding::Fungible {
definition_id,
balance,
} = token_holding
else {
panic!("{context}: AMM Program expects a valid Fungible Token Holding Account");
program_revert::revert!(
error::INVALID_INPUT,
"{context}: AMM Program expects a valid Fungible Token Holding Account"
);
};

(definition_id, balance)
Expand Down
22 changes: 22 additions & 0 deletions programs/amm/methods/guest/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion programs/amm/methods/guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ name = "amm"
path = "src/bin/amm.rs"

[dependencies]
program-revert-macros = { path = "../../../common/revert-macros" }
program-revert = { path = "../../../common/revert" }
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "1ef0500f8fc8ce3ddf95523726ae159db83f744f", package = "spel-framework" }
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.4", package = "lee_core" }
risc0-zkvm = { version = "=3.0.5", default-features = false }
amm_core = { path = "../../core" }
amm_program = { path = "../..", package = "amm_program" }
token_core = { path = "../../../token/core" }
serde = { version = "1.0", features = ["derive"] }
borsh = "1.5"
borsh = "1.5"
Loading
Loading