Skip to content

Commit

Permalink
Merge bb8c2b1 into da809c5
Browse files Browse the repository at this point in the history
  • Loading branch information
nventuro authored Dec 3, 2024
2 parents da809c5 + bb8c2b1 commit d8c2099
Show file tree
Hide file tree
Showing 15 changed files with 1,023 additions and 16 deletions.
4 changes: 2 additions & 2 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ library Constants {
uint256 internal constant ARGS_LENGTH = 16;
uint256 internal constant MAX_NOTE_HASHES_PER_CALL = 16;
uint256 internal constant MAX_NULLIFIERS_PER_CALL = 16;
uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL = 4;
uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL = 5;
uint256 internal constant MAX_ENQUEUED_CALLS_PER_CALL = 16;
uint256 internal constant MAX_L2_TO_L1_MSGS_PER_CALL = 2;
uint256 internal constant MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL = 64;
Expand Down Expand Up @@ -200,7 +200,7 @@ library Constants {
uint256 internal constant TOTAL_FEES_LENGTH = 1;
uint256 internal constant TOTAL_MANA_USED_LENGTH = 1;
uint256 internal constant HEADER_LENGTH = 25;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 731;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 739;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 867;
uint256 internal constant PRIVATE_CONTEXT_INPUTS_LENGTH = 38;
uint256 internal constant FEE_RECIPIENT_LENGTH = 2;
Expand Down
8 changes: 4 additions & 4 deletions noir-projects/aztec-nr/aztec/src/macros/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ comptime fn generate_contract_interface(m: Module) -> Quoted {
$fn_stubs_quote

pub fn at(
target_contract: aztec::protocol_types::address::AztecAddress
addr: aztec::protocol_types::address::AztecAddress
) -> Self {
Self { target_contract }
Self { target_contract: addr }
}

pub fn interface() -> Self {
Expand All @@ -92,9 +92,9 @@ comptime fn generate_contract_interface(m: Module) -> Quoted {

#[contract_library_method]
pub fn at(
target_contract: aztec::protocol_types::address::AztecAddress
addr: aztec::protocol_types::address::AztecAddress
) -> $module_name {
$module_name { target_contract }
$module_name { target_contract: addr }
}

#[contract_library_method]
Expand Down
1 change: 1 addition & 0 deletions noir-projects/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"contracts/amm_contract",
"contracts/app_subscription_contract",
"contracts/auth_contract",
"contracts/auth_registry_contract",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "amm_contract"
authors = [""]
compiler_version = ">=0.25.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
token = { path = "../token_contract" }
29 changes: 29 additions & 0 deletions noir-projects/noir-contracts/contracts/amm_contract/src/config.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use dep::aztec::protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}};

global CONFIG_LENGTH: u32 = 3;

/// We store the tokens of the pool in a struct such that to load it from SharedImmutable asserts only a single
/// merkle proof.
/// (Once we actually do the optimization. WIP in https://github.com/AztecProtocol/aztec-packages/pull/8022).
pub struct Config {
pub token0: AztecAddress,
pub token1: AztecAddress,
pub liquidity_token: AztecAddress,
}

// Note: I could not get #[derive(Serialize)] to work so I had to implement it manually.
impl Serialize<CONFIG_LENGTH> for Config {
fn serialize(self: Self) -> [Field; CONFIG_LENGTH] {
[self.token0.to_field(), self.token1.to_field(), self.liquidity_token.to_field()]
}
}

impl Deserialize<CONFIG_LENGTH> for Config {
fn deserialize(fields: [Field; CONFIG_LENGTH]) -> Self {
Self {
token0: AztecAddress::from_field(fields[0]),
token1: AztecAddress::from_field(fields[1]),
liquidity_token: AztecAddress::from_field(fields[2]),
}
}
}
96 changes: 96 additions & 0 deletions noir-projects/noir-contracts/contracts/amm_contract/src/lib.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/// Given an input amount of an asset and pair balances, returns the maximum output amount of the other asset.
pub fn get_amount_out(amount_in: U128, balance_in: U128, balance_out: U128) -> U128 {
assert(amount_in > U128::zero(), "INSUFFICIENT_INPUT_AMOUNT");
assert((balance_in > U128::zero()) & (balance_out > U128::zero()), "INSUFFICIENT_LIQUIDITY");

// The expression below is:
// (amount_in * 997 * balance_out) / (balance_in * 10000 + amount_in * 997)
// which is equivalent to:
// balance_out * ((amount_in * 0.997) / (balance_in + amount_in * 0.997))
// resulting in an implicit 0.3% fee on the amount in, as the fee tokens are not taken into consideration.

let amount_in_with_fee = amount_in * U128::from_integer(997);
let numerator = amount_in_with_fee * balance_out;
let denominator = balance_in * U128::from_integer(1000) + amount_in_with_fee;
numerator / denominator
}

/// Given an output amount of an asset and pair balances, returns a required input amount of the other asset.
pub fn get_amount_in(amount_out: U128, balance_in: U128, balance_out: U128) -> U128 {
assert(amount_out > U128::zero(), "INSUFFICIENT_OUTPUT_AMOUNT");
assert((balance_in > U128::zero()) & (balance_out > U128::zero()), "INSUFFICIENT_LIQUIDITY");

// The expression below is:
// (balance_in * amount_out * 1000) / (balance_out - amout_out * 997) + 1
// which is equivalent to:
// balance_in * (amount_out / (balance_in + amount_in)) * 1/0.997 + 1
// resulting in an implicit 0.3% fee on the amount in, as the fee tokens are not taken into consideration. The +1
// at the end ensures the rounding error favors the pool.

let numerator = balance_in * amount_out * U128::from_integer(1000);
let denominator = (balance_out - amount_out) * U128::from_integer(997);
(numerator / denominator) + U128::from_integer(1)
}

/// Given the desired amounts and balances of token0 and token1 returns the optimal amount of token0 and token1 to be added to the pool.
pub fn get_amounts_to_add(
amount0_max: U128,
amount1_max: U128,
amount0_min: U128,
amount1_min: U128,
balance0: U128,
balance1: U128,
) -> (U128, U128) {
// When adding tokens, both balances must grow by the same ratio, which means that their spot price is unchanged.
// Since any swaps would affect these ratios, liquidity providers supply a range of minimum and maximum balances
// they are willing to supply for each token (which translates to minimum and maximum relative prices of the
// tokens, preventing loss of value outside of this range due to e.g. front-running).

if (balance0 == U128::zero()) | (balance1 == U128::zero()) {
// The token balances should only be zero when initializing the pool. In this scenario there is no prior ratio
// to follow so we simply transfer the full maximum balance - it is up to the caller to make sure that the ratio
// they've chosen results in a a reasonable spot price.
(amount0_max, amount1_max)
} else {
// There is a huge number of amount combinations that respect the minimum and maximum for each token, but we'll
// only consider the two scenarios in which one of the amounts is the maximum amount.

// First we calculate the token1 amount that'd need to be supplied if we used the maximum amount for token0.
let amount1_equivalent = get_equivalent_amount(amount0_max, balance0, balance1);
if (amount1_equivalent <= amount1_max) {
assert(amount1_equivalent >= amount1_min, "AMOUNT_1_BELOW_MINIMUM");
(amount0_max, amount1_equivalent)
} else {
// If the max amount for token0 results in a token1 amount larger than the maximum, then we try with the
// maximum token1 amount, hoping that it'll result in a token0 amount larger than the minimum.
let amount0_equivalent = get_equivalent_amount(amount1_max, balance1, balance0);
// This should never happen, as it'd imply that the maximum is lower than the minimum.
assert(amount0_equivalent <= amount0_max);

assert(amount0_equivalent >= amount0_min, "AMOUNT_0_BELOW_MINIMUM");
(amount0_equivalent, amount1_max)
}
}
}

/// Returns the amount of tokens to return to a liquidity provider when they remove liquidity from the pool.
pub fn get_amounts_on_remove(
to_burn: U128,
total_supply: U128,
balance0: U128,
balance1: U128,
) -> (U128, U128) {
// Since the liquidity token tracks ownership of the pool, the liquidity provider gets a proportional share of each
// token.
(to_burn * balance0 / total_supply, to_burn * balance1 / total_supply)
}

/// Given some amount of an asset and pair balances, returns an equivalent amount of the other asset. Tokens should be
/// added and removed from the Pool respecting this ratio.
fn get_equivalent_amount(amount0: U128, balance0: U128, balance1: U128) -> U128 {
assert((balance0 > U128::zero()) & (balance1 > U128::zero()), "INSUFFICIENT_LIQUIDITY");

// This is essentially the Rule of Three, since we're computing proportional ratios. Note we divide at the end to
// avoid introducing too much error due to truncation.
(amount0 * balance1) / balance0
}
Loading

0 comments on commit d8c2099

Please sign in to comment.