Skip to content

Commit 33a0149

Browse files
authored
Merge pull request #13 from AztecProtocol/testnet
testnet compatibility
2 parents cf9c9f0 + 3db3fa5 commit 33a0149

37 files changed

Lines changed: 3026 additions & 2456 deletions

contracts/Nargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[workspace]
22
members = [
3-
"proof_of_password"
3+
"proof_of_password",
4+
"amm"
45
]

contracts/amm/Nargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "amm_contract"
3+
authors = [""]
4+
type = "contract"
5+
6+
[dependencies]
7+
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-aztecnr-rc.2", directory = "noir-projects/aztec-nr/aztec" }
8+
token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-aztecnr-rc.2", directory = "noir-projects/noir-contracts/contracts/app/token_contract" }
9+
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-aztecnr-rc.2", directory = "noir-projects/aztec-nr/uint-note" }

contracts/amm/src/config.nr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use aztec::protocol::{address::AztecAddress, traits::{Deserialize, Packable, Serialize}};
2+
use std::meta::derive;
3+
4+
/// We store the tokens of the pool in a struct such that to load it from PublicImmutable asserts only a single
5+
/// merkle proof.
6+
#[derive(Deserialize, Eq, Packable, Serialize)]
7+
pub struct Config {
8+
pub token0: AztecAddress,
9+
pub token1: AztecAddress,
10+
pub liquidity_token: AztecAddress,
11+
}

contracts/amm/src/lib.nr

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/// Given an input amount of an asset and pair balances, returns the maximum output amount of the other asset.
2+
pub fn get_amount_out(amount_in: u128, balance_in: u128, balance_out: u128) -> u128 {
3+
assert(amount_in > 0 as u128, "INSUFFICIENT_INPUT_AMOUNT");
4+
assert((balance_in > 0 as u128) & (balance_out > 0 as u128), "INSUFFICIENT_LIQUIDITY");
5+
6+
// The expression below is:
7+
// (amount_in * 997 * balance_out) / (balance_in * 10000 + amount_in * 997)
8+
// which is equivalent to:
9+
// balance_out * ((amount_in * 0.997) / (balance_in + amount_in * 0.997))
10+
// resulting in an implicit 0.3% fee on the amount in, as the fee tokens are not taken into consideration.
11+
12+
let amount_in_with_fee = amount_in * 997 as u128;
13+
let numerator = amount_in_with_fee * balance_out;
14+
let denominator = balance_in * 1000 as u128 + amount_in_with_fee;
15+
numerator / denominator
16+
}
17+
18+
/// Given an output amount of an asset and pair balances, returns a required input amount of the other asset.
19+
pub fn get_amount_in(amount_out: u128, balance_in: u128, balance_out: u128) -> u128 {
20+
assert(amount_out > 0 as u128, "INSUFFICIENT_OUTPUT_AMOUNT");
21+
assert((balance_in > 0 as u128) & (balance_out > 0 as u128), "INSUFFICIENT_LIQUIDITY");
22+
23+
// The expression below is:
24+
// (balance_in * amount_out * 1000) / (balance_out - amount_out * 997) + 1
25+
// which is equivalent to:
26+
// balance_in * (amount_out / (balance_in + amount_in)) * 1/0.997 + 1
27+
// resulting in an implicit 0.3% fee on the amount in, as the fee tokens are not taken into consideration. The +1
28+
// at the end ensures the rounding error favors the pool.
29+
30+
let numerator = balance_in * amount_out * 1000 as u128;
31+
let denominator = (balance_out - amount_out) * 997 as u128;
32+
(numerator / denominator) + 1 as u128
33+
}
34+
35+
/// Given the desired amounts and balances of token0 and token1 returns the optimal amount of token0 and token1 to be added to the pool.
36+
pub fn get_amounts_to_add(
37+
amount0_max: u128,
38+
amount1_max: u128,
39+
amount0_min: u128,
40+
amount1_min: u128,
41+
balance0: u128,
42+
balance1: u128,
43+
) -> (u128, u128) {
44+
// When adding tokens, both balances must grow by the same ratio, which means that their spot price is unchanged.
45+
// Since any swaps would affect these ratios, liquidity providers supply a range of minimum and maximum balances
46+
// they are willing to supply for each token (which translates to minimum and maximum relative prices of the
47+
// tokens, preventing loss of value outside of this range due to e.g. front-running).
48+
49+
if (balance0 == 0 as u128) | (balance1 == 0 as u128) {
50+
// The token balances should only be zero when initializing the pool. In this scenario there is no prior ratio
51+
// to follow so we simply transfer the full maximum balance - it is up to the caller to make sure that the ratio
52+
// they've chosen results in a a reasonable spot price.
53+
(amount0_max, amount1_max)
54+
} else {
55+
// There is a huge number of amount combinations that respect the minimum and maximum for each token, but we'll
56+
// only consider the two scenarios in which one of the amounts is the maximum amount.
57+
58+
// First we calculate the token1 amount that'd need to be supplied if we used the maximum amount for token0.
59+
let amount1_equivalent = get_equivalent_amount(amount0_max, balance0, balance1);
60+
if (amount1_equivalent <= amount1_max) {
61+
assert(amount1_equivalent >= amount1_min, "AMOUNT_1_BELOW_MINIMUM");
62+
(amount0_max, amount1_equivalent)
63+
} else {
64+
// If the max amount for token0 results in a token1 amount larger than the maximum, then we try with the
65+
// maximum token1 amount, hoping that it'll result in a token0 amount larger than the minimum.
66+
let amount0_equivalent = get_equivalent_amount(amount1_max, balance1, balance0);
67+
// This should never happen, as it'd imply that the maximum is lower than the minimum.
68+
assert(amount0_equivalent <= amount0_max);
69+
70+
assert(amount0_equivalent >= amount0_min, "AMOUNT_0_BELOW_MINIMUM");
71+
(amount0_equivalent, amount1_max)
72+
}
73+
}
74+
}
75+
76+
/// Returns the amount of tokens to return to a liquidity provider when they remove liquidity from the pool.
77+
pub fn get_amounts_on_remove(
78+
to_burn: u128,
79+
total_supply: u128,
80+
balance0: u128,
81+
balance1: u128,
82+
) -> (u128, u128) {
83+
// Since the liquidity token tracks ownership of the pool, the liquidity provider gets a proportional share of each
84+
// token.
85+
(to_burn * balance0 / total_supply, to_burn * balance1 / total_supply)
86+
}
87+
88+
/// Given some amount of an asset and pair balances, returns an equivalent amount of the other asset. Tokens should be
89+
/// added and removed from the Pool respecting this ratio.
90+
fn get_equivalent_amount(amount0: u128, balance0: u128, balance1: u128) -> u128 {
91+
assert((balance0 > 0 as u128) & (balance1 > 0 as u128), "INSUFFICIENT_LIQUIDITY");
92+
93+
// This is essentially the Rule of Three, since we're computing proportional ratios. Note we divide at the end to
94+
// avoid introducing too much error due to truncation.
95+
(amount0 * balance1) / balance0
96+
}

0 commit comments

Comments
 (0)