From 1fb1cc5eba1b20a447b5367a3d2d175c600ae531 Mon Sep 17 00:00:00 2001 From: dd Date: Thu, 22 Apr 2021 10:57:52 -0400 Subject: [PATCH 1/7] Updated interest rates to account for added risk at the 100% bound. --- cli/Cargo.lock | 2 +- program/src/state.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index fc4656b..c01bc60 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -1854,7 +1854,7 @@ dependencies = [ [[package]] name = "mango" -version = "0.2.3" +version = "0.2.4" dependencies = [ "arrayref", "bincode", diff --git a/program/src/state.rs b/program/src/state.rs index 4a49f23..ae56f2a 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -24,8 +24,9 @@ pub const HOUR: u64 = 3600; pub const DAY: u64 = 86400; pub const YEAR: U64F64 = U64F64!(31536000); const OPTIMAL_UTIL: U64F64 = U64F64!(0.7); -const OPTIMAL_R: U64F64 = U64F64!(3.17097919837645865e-09); // 10% APY -> 0.1 / YEAR -const MAX_R: U64F64 = U64F64!(3.17097919837645865e-08); // max 100% APY -> 1 / YEAR +const OPTIMAL_R: U64F64 = U64F64!(6.3419583967529173008625e-09); // 20% APY -> 0.1 / YEAR +const MAX_R: U64F64 = U64F64!(9.5129375951293759512937e-08); // max 300% APY -> 1 / YEAR + pub const ONE_U64F64: U64F64 = U64F64!(1); pub const ZERO_U64F64: U64F64 = U64F64!(0); pub const PARTIAL_LIQ_INCENTIVE: U64F64 = U64F64!(1.05); From 884fe87660fbb6c7513e11765fad5934d2af9962 Mon Sep 17 00:00:00 2001 From: dd Date: Thu, 22 Apr 2021 12:51:21 -0400 Subject: [PATCH 2/7] Fixed settle borrow rounding error --- program/Cargo.lock | 2 +- program/Cargo.toml | 2 +- program/src/processor.rs | 26 ++++++++++++++------------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/program/Cargo.lock b/program/Cargo.lock index 4bfef93..ea6ad87 100644 --- a/program/Cargo.lock +++ b/program/Cargo.lock @@ -458,7 +458,7 @@ dependencies = [ [[package]] name = "mango" -version = "0.2.4" +version = "0.2.5" dependencies = [ "arrayref", "bincode", diff --git a/program/Cargo.toml b/program/Cargo.toml index 0c13a81..b6cdeb3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mango" -version = "0.2.4" +version = "0.2.5" authors = ["blockworks"] edition = "2018" diff --git a/program/src/processor.rs b/program/src/processor.rs index 0bb6a39..9b50a8e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1548,21 +1548,23 @@ fn settle_borrow_unchecked( token_index: usize, quantity: u64 ) -> MangoResult<()> { - let index: &MangoIndex = &mango_group.indexes[token_index]; + let deposit_index = mango_group.indexes[token_index].deposit; + let borrow_index = mango_group.indexes[token_index].borrow; + let native_borrow: U64F64 = margin_account.borrows[token_index] * borrow_index; + let native_deposit: U64F64 = margin_account.deposits[token_index] * deposit_index; + let quantity = U64F64::from_num(quantity); - let native_borrow = margin_account.get_native_borrow(index, token_index); - let native_deposit = margin_account.get_native_deposit(index, token_index); - - let quantity = cmp::min(cmp::min(quantity, native_borrow), native_deposit); - - let borr_settle = U64F64::from_num(quantity) / index.borrow; - let dep_settle = U64F64::from_num(quantity) / index.deposit; - - checked_sub_deposit(mango_group, margin_account, token_index, dep_settle)?; - checked_sub_borrow(mango_group, margin_account, token_index, borr_settle)?; + let quantity = min(quantity, native_deposit); + if quantity >= native_borrow { // Reduce borrows to 0 to prevent rounding related dust + // NOTE: native_borrow / index.borrow is same as margin_account.borrows[token_index] + checked_sub_deposit(mango_group, margin_account, token_index, native_borrow / deposit_index)?; + checked_sub_borrow(mango_group, margin_account, token_index, margin_account.borrows[token_index])?; + } else { + checked_sub_deposit(mango_group, margin_account, token_index, quantity / deposit_index)?; + checked_sub_borrow(mango_group, margin_account, token_index, quantity / borrow_index)?; + } // No need to check collateralization ratio or deposits/borrows validity - Ok(()) } From 48095b30bdbd7e0af28fb93f790d4f80b1a5531d Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 26 Apr 2021 00:59:55 -0400 Subject: [PATCH 3/7] Added more help to devnet_deploy.sh --- cli/devnet_deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/devnet_deploy.sh b/cli/devnet_deploy.sh index 80f9a2d..20d9adc 100644 --- a/cli/devnet_deploy.sh +++ b/cli/devnet_deploy.sh @@ -29,6 +29,8 @@ TOKENS="BTC ETH USDT" MANGO_GROUP_NAME=BTC_ETH_USDT BORROW_LIMITS="1.0 20.0 50000.0" +# This will deploy the BTC_ETH_USDT mango group and automatically update the ids.json in mango client +# Make sure IDS_PATH is set correctly in mango/cli/devnet.env, or set it again before running this cargo run -- $CLUSTER init-mango-group \ --payer $KEYPAIR \ --ids-path $IDS_PATH \ From 565b43c3beaf96057682dd2447fa911ea4284518 Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 26 Apr 2021 10:02:00 -0400 Subject: [PATCH 4/7] Added help on how to deploy a new group --- cli/new_group.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 cli/new_group.md diff --git a/cli/new_group.md b/cli/new_group.md new file mode 100644 index 0000000..c156395 --- /dev/null +++ b/cli/new_group.md @@ -0,0 +1,39 @@ +1. First build and deploy serum dex to devnet (if you just want to use already deployed then skip this step) +2. Go to blockworks-foundation/solana-flux-aggregator, Add the token pairs you want into config/setup.dev.json +3. Run `yarn solink setup config/setup.dev.json` +4. Make sure the feeds in solana flux aggregator can feed new tokens +5. Add the oracle pubkeys found in deploy.dev.json into ids.json devnet.oracles +6. Add the token mints to ids.json devnet.symbols +7. Amend devnet.env and add new symbols +8. List the new markets. For example: + +``` +source ~/mango/cli/devnet.env + +cargo run -- $CLUSTER list-market $KEYPAIR $DEX_PROGRAM_ID --coin-mint $BTC --pc-mint $USDT +cargo run -- $CLUSTER list-market $KEYPAIR $DEX_PROGRAM_ID --coin-mint $ETH --pc-mint $USDT +``` + +9. Add the MarketState pubkeys to ids.json devnet.spot_markets +10. go to blockworks-foundation/liquidator/crank.sh and add support for your new markets +11. run crank.sh to run the cranks, for example +``` +source crank.sh btc usdt +source crank.sh eth usdt +``` +12. Deploy new mango group for example: +``` +CLUSTER=devnet +KEYPAIR=~/.config/solana/id.json +IDS_PATH=~/mango-client-ts/src/ids.json +TOKENS="BTC ETH USDT" +MANGO_GROUP_NAME=BTC_ETH_USDT +BORROW_LIMITS="1.0 20.0 50000.0" + +cargo run -- $CLUSTER init-mango-group \ +--payer $KEYPAIR \ +--ids-path $IDS_PATH \ +--tokens $TOKENS \ +--mango-program-id $MANGO_PROGRAM_ID \ +--borrow-limits $BORROW_LIMITS +``` \ No newline at end of file From 55883ef8733d82218a7a53863f66b5a70d23b52c Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 26 Apr 2021 10:05:52 -0400 Subject: [PATCH 5/7] Added help on how to deploy a new group --- cli/new_group.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/new_group.md b/cli/new_group.md index c156395..bed9c0f 100644 --- a/cli/new_group.md +++ b/cli/new_group.md @@ -9,7 +9,7 @@ ``` source ~/mango/cli/devnet.env - +cd ~/blockworks-foundation/serum-dex/ cargo run -- $CLUSTER list-market $KEYPAIR $DEX_PROGRAM_ID --coin-mint $BTC --pc-mint $USDT cargo run -- $CLUSTER list-market $KEYPAIR $DEX_PROGRAM_ID --coin-mint $ETH --pc-mint $USDT ``` From 2b7e619f48be821871d9c547be0acfff084fb208 Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 26 Apr 2021 00:59:08 -0400 Subject: [PATCH 6/7] added more checked math --- program/src/processor.rs | 7 ++++--- program/src/state.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 9b50a8e..9f572db 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1889,7 +1889,7 @@ fn get_in_out_quantities( liqor_max_in: u64 ) -> MangoResult<(u64, u64)> { let deficit_val = margin_account.get_partial_liq_deficit(&mango_group, &prices, open_orders_accs)? + ONE_U64F64; - let out_avail: U64F64 = margin_account.deposits[out_token_index] * mango_group.indexes[out_token_index].deposit; + let out_avail: U64F64 = margin_account.deposits[out_token_index].checked_mul(mango_group.indexes[out_token_index].deposit).unwrap(); let out_avail_val = out_avail * prices[out_token_index]; // liq incentive is max of 1/2 the dist between @@ -1900,7 +1900,8 @@ fn get_in_out_quantities( // we know prices are not 0; if they are this will error; let max_in: U64F64 = max_in_val / prices[in_token_index]; - let native_borrow = margin_account.borrows[in_token_index] * mango_group.indexes[in_token_index].borrow; + let native_borrow = margin_account.borrows[in_token_index].checked_mul( + mango_group.indexes[in_token_index].borrow).unwrap(); // Can only deposit as much there is borrows to offset in in_token let in_quantity = min(min(max_in, native_borrow), U64F64::from_num(liqor_max_in)); @@ -1910,7 +1911,7 @@ fn get_in_out_quantities( checked_sub_borrow(mango_group, margin_account, in_token_index, deposit)?; // Withdraw incentive funds to liqor - let in_val: U64F64 = in_quantity * prices[in_token_index]; + let in_val: U64F64 = in_quantity.checked_mul(prices[in_token_index]).unwrap(); let out_val: U64F64 = in_val * PARTIAL_LIQ_INCENTIVE; let out_quantity: U64F64 = out_val / prices[out_token_index]; diff --git a/program/src/state.rs b/program/src/state.rs index ae56f2a..340b2f1 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -338,7 +338,7 @@ impl MarginAccount { if liabs == ZERO_U64F64 { Ok(U64F64::MAX) } else { - Ok(assets / liabs) + Ok(assets.checked_div( liabs).unwrap()) } } pub fn get_total_assets( From d5f8e96efdba03fc10324735656839b498a754fa Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 26 Apr 2021 13:52:18 -0400 Subject: [PATCH 7/7] Added more checking --- cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- cli/new_group.md | 4 ++-- program/Cargo.lock | 2 +- program/Cargo.toml | 2 +- program/src/state.rs | 15 ++++++++------- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index c01bc60..8e959ae 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -1854,7 +1854,7 @@ dependencies = [ [[package]] name = "mango" -version = "0.2.4" +version = "0.2.5" dependencies = [ "arrayref", "bincode", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index bbaec1b..899e916 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,4 +26,4 @@ serum_dex = { version = "^0.2", git = "https://github.com/project-serum/serum-de flux-aggregator = { version = "^0.1", git = "https://github.com/blockworks-foundation/solana-flux-aggregator.git", features=["program", "no-entrypoint"] } arrayref = "^0.3.6" -fixed = { version = "^1.7.0" } \ No newline at end of file +fixed = { version = "^1.7.0" } diff --git a/cli/new_group.md b/cli/new_group.md index bed9c0f..c31e537 100644 --- a/cli/new_group.md +++ b/cli/new_group.md @@ -18,8 +18,8 @@ cargo run -- $CLUSTER list-market $KEYPAIR $DEX_PROGRAM_ID --coin-mint $ETH --pc 10. go to blockworks-foundation/liquidator/crank.sh and add support for your new markets 11. run crank.sh to run the cranks, for example ``` -source crank.sh btc usdt -source crank.sh eth usdt +source crank.sh $KEYPAIR btc usdt +source crank.sh $KEYPAIR eth usdt ``` 12. Deploy new mango group for example: ``` diff --git a/program/Cargo.lock b/program/Cargo.lock index ea6ad87..01e1385 100644 --- a/program/Cargo.lock +++ b/program/Cargo.lock @@ -458,7 +458,7 @@ dependencies = [ [[package]] name = "mango" -version = "0.2.5" +version = "0.2.6" dependencies = [ "arrayref", "bincode", diff --git a/program/Cargo.toml b/program/Cargo.toml index b6cdeb3..0db49e7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mango" -version = "0.2.5" +version = "0.2.6" authors = ["blockworks"] edition = "2018" diff --git a/program/src/state.rs b/program/src/state.rs index 340b2f1..abe7e7c 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -31,6 +31,7 @@ pub const ONE_U64F64: U64F64 = U64F64!(1); pub const ZERO_U64F64: U64F64 = U64F64!(0); pub const PARTIAL_LIQ_INCENTIVE: U64F64 = U64F64!(1.05); pub const DUST_THRESHOLD: U64F64 = U64F64!(0.01); // TODO make this part of MangoGroup state +pub const EPSILON: U64F64 = U64F64!(1.0e-17); macro_rules! check_default { ($cond:expr) => { @@ -173,13 +174,13 @@ impl MangoGroup { /// interest is in units per second (e.g. 0.01 => 1% interest per second) pub fn get_interest_rate(&self, token_index: usize) -> U64F64 { let index: &MangoIndex = &self.indexes[token_index]; - let native_deposits = index.deposit * self.total_deposits[token_index]; - let native_borrows = index.borrow * self.total_borrows[token_index]; + let native_deposits = index.deposit.checked_mul(self.total_deposits[token_index]).unwrap(); + let native_borrows = index.borrow.checked_mul(self.total_borrows[token_index]).unwrap(); if native_deposits <= native_borrows { // if deps == 0, this is always true return MAX_R; // kind of an error state } - let utilization = native_borrows / native_deposits; + let utilization = native_borrows.checked_div(native_deposits).unwrap(); if utilization > OPTIMAL_UTIL { let extra_util = utilization - OPTIMAL_UTIL; let slope = (MAX_R - OPTIMAL_R) / (ONE_U64F64 - OPTIMAL_UTIL); @@ -205,10 +206,10 @@ impl MangoGroup { continue; } - let native_deposits: U64F64 = self.total_deposits[i] * index.deposit; - let native_borrows: U64F64 = self.total_borrows[i] * index.borrow; - let epsilon = U64F64::from_bits(1u128) * 100; - check_default!(native_borrows <= native_deposits + epsilon)?; // to account for rounding errors + // don't need to check here because this check already happens in get interest rate + let native_deposits: U64F64 = self.total_deposits[i].checked_mul(index.deposit).unwrap(); + let native_borrows: U64F64 = self.total_borrows[i].checked_mul(index.borrow).unwrap(); + check_default!(native_borrows <= native_deposits + EPSILON)?; // to account for rounding errors let utilization = native_borrows.checked_div(native_deposits).unwrap(); let borrow_interest = interest_rate