Skip to content

Commit 5851f5a

Browse files
authored
Added simple AMM to defi crate (#261)
* Added simple amm to defi crate * rename to constant_product_market * remove debug import
1 parent 2819ed7 commit 5851f5a

File tree

4 files changed

+169
-0
lines changed

4 files changed

+169
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod components;
2+
mod systems;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use traits::Into;
2+
use traits::TryInto;
3+
use option::OptionTrait;
4+
5+
const SCALING_FACTOR: u128 = 10000_u128;
6+
7+
#[derive(Component)]
8+
struct Cash {
9+
amount: u128,
10+
}
11+
12+
#[derive(Component)]
13+
struct Item {
14+
quantity: usize,
15+
}
16+
17+
#[derive(Component)]
18+
struct Market {
19+
cash_amount: u128,
20+
item_quantity: usize,
21+
}
22+
23+
trait MarketTrait {
24+
fn buy(self: @Market, quantity: usize) -> u128;
25+
fn sell(self: @Market, quantity: usize) -> u128;
26+
}
27+
28+
impl MarketImpl of MarketTrait {
29+
fn buy(self: @Market, quantity: usize) -> u128 {
30+
assert(quantity < *self.item_quantity, 'not enough liquidity');
31+
let (quantity, available, cash) = normalize(quantity, self);
32+
let k = cash * available;
33+
let cost = (k / (available - quantity)) - cash;
34+
cost
35+
}
36+
37+
fn sell(self: @Market, quantity: usize) -> u128 {
38+
let (quantity, available, cash) = normalize(quantity, self);
39+
let k = cash * available;
40+
let payout = cash - (k / (available + quantity));
41+
payout
42+
}
43+
}
44+
45+
fn normalize(quantity: usize, market: @Market) -> (u128, u128, u128) {
46+
let quantity: u128 = quantity.into().try_into().unwrap() * SCALING_FACTOR;
47+
let available: u128 = (*market.item_quantity).into().try_into().unwrap() * SCALING_FACTOR;
48+
(quantity, available, *market.cash_amount)
49+
}
50+
51+
52+
#[test]
53+
#[should_panic(expected: ('not enough liquidity', ))]
54+
fn test_not_enough_quantity() {
55+
let market = Market {
56+
cash_amount: SCALING_FACTOR * 1_u128, item_quantity: 1_usize
57+
}; // pool 1:1
58+
let cost = market.buy(10_usize);
59+
}
60+
61+
#[test]
62+
#[available_gas(100000)]
63+
fn test_market_buy() {
64+
let market = Market {
65+
cash_amount: SCALING_FACTOR * 1_u128, item_quantity: 10_usize
66+
}; // pool 1:10
67+
let cost = market.buy(5_usize);
68+
assert(cost == SCALING_FACTOR * 1_u128, 'wrong cost');
69+
}
70+
71+
#[test]
72+
#[available_gas(100000)]
73+
fn test_market_sell() {
74+
let market = Market {
75+
cash_amount: SCALING_FACTOR * 1_u128, item_quantity: 10_usize
76+
}; // pool 1:10
77+
let payout = market.sell(5_usize);
78+
assert(payout == 3334_u128, 'wrong payout');
79+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#[system]
2+
mod Buy {
3+
use traits::Into;
4+
use array::ArrayTrait;
5+
use dojo_defi::constant_product_market::components::Item;
6+
use dojo_defi::constant_product_market::components::Cash;
7+
use dojo_defi::constant_product_market::components::Market;
8+
use dojo_defi::constant_product_market::components::MarketTrait;
9+
10+
fn execute(game_id: felt252, item_id: felt252, quantity: usize) {
11+
let player: felt252 = starknet::get_caller_address().into();
12+
13+
let cash_sk: Query = (game_id, (player)).into();
14+
let player_cash = commands::<Cash>::entity(cash_sk);
15+
16+
let market_sk: Query = (game_id, (item_id)).into();
17+
let market = commands::<Market>::entity(market_sk);
18+
19+
let cost = market.buy(quantity);
20+
assert(cost < player_cash.amount, 'not enough cash');
21+
22+
// update market
23+
commands::set_entity(
24+
market_sk,
25+
(Market {
26+
cash_amount: market.cash_amount + cost,
27+
item_quantity: market.item_quantity - quantity,
28+
})
29+
);
30+
31+
// update player cash
32+
commands::set_entity(cash_sk, (Cash { amount: player_cash.amount - cost }));
33+
34+
// update player item
35+
let item_sk: Query = (game_id, (player, item_id)).into();
36+
let maybe_item = commands::<Item>::try_entity(item_sk);
37+
let player_quantity = match maybe_item {
38+
Option::Some(item) => item.quantity + quantity,
39+
Option::None(_) => quantity,
40+
};
41+
commands::set_entity(item_sk, (Item { quantity: player_quantity }));
42+
}
43+
}
44+
45+
#[system]
46+
mod Sell {
47+
use traits::Into;
48+
use array::ArrayTrait;
49+
use dojo_defi::constant_product_market::components::Item;
50+
use dojo_defi::constant_product_market::components::Cash;
51+
use dojo_defi::constant_product_market::components::Market;
52+
use dojo_defi::constant_product_market::components::MarketTrait;
53+
54+
fn execute(game_id: felt252, item_id: felt252, quantity: usize) {
55+
let player: felt252 = starknet::get_caller_address().into();
56+
57+
let item_sk: Query = (game_id, (player, item_id)).into();
58+
let maybe_item = commands::<Item>::try_entity(item_sk);
59+
let player_quantity = match maybe_item {
60+
Option::Some(item) => item.quantity,
61+
Option::None(_) => 0_u32,
62+
};
63+
assert(player_quantity >= quantity, 'not enough items');
64+
65+
let cash_sk: Query = (game_id, (player)).into();
66+
let player_cash = commands::<Cash>::entity(cash_sk);
67+
68+
let market_sk: Query = (game_id, (item_id)).into();
69+
let market = commands::<Market>::entity(market_sk);
70+
let payout = market.sell(quantity);
71+
72+
// update market
73+
commands::set_entity(
74+
market_sk,
75+
(Market {
76+
cash_amount: market.cash_amount - payout,
77+
item_quantity: market.item_quantity + quantity
78+
})
79+
);
80+
81+
// update player cash
82+
commands::set_entity(cash_sk, (Cash { amount: player_cash.amount + payout }));
83+
84+
// update player item
85+
commands::set_entity(item_sk, (Item { quantity: player_quantity - quantity }));
86+
}
87+
}

crates/dojo-defi/src/lib.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod constant_product_market;

0 commit comments

Comments
 (0)