From 9ee843664a3109e8b4d93849776bfbf66896d89d Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 18 Sep 2026 19:34:06 +0300 Subject: [PATCH 01/11] Update deepbook to v4 & use main token package Update deepbook package for tests to use v4-tag-fork-test-methods branch. --- packages/deeptrade-core/Move.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/deeptrade-core/Move.toml b/packages/deeptrade-core/Move.toml index b5d5b9d..4fa3d85 100644 --- a/packages/deeptrade-core/Move.toml +++ b/packages/deeptrade-core/Move.toml @@ -4,8 +4,8 @@ version = "2.0.0" edition = "2024.alpha" [dependencies] -deepbook = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/deepbook", rev = "v3.0.0" } -token = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "v3.0.0" } +deepbook = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/deepbook", rev = "v4.0.0" } +token = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "main" } multisig = { git = "https://github.com/DeeptradeProtocol/multisig-move.git", subdir = "move", rev = "v1.0.1" } # Pyth dependency @@ -16,6 +16,5 @@ pyth_upgraded = { git = "https://github.com/pyth-network/pyth-crosschain.git", s # For `sui move test`, comment the production pins above and uncomment these. # Never leave both copies uncommented. -# deepbook = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/deepbook", rev = "v3-tag-fork-test-methods" } -# token = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/token", rev = "v3-tag-fork-test-methods", override = true } +# deepbook = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/deepbook", rev = "v4-tag-fork-test-methods" } # pyth = { git = "https://github.com/DeeptradeProtocol/pyth-crosschain.git", subdir = "target_chains/sui/contracts", rev = "sui-contract-mainnet" } From ab929fc2b6d9d119c126e1c02ec453d61fddff49 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 18 Sep 2026 19:34:38 +0300 Subject: [PATCH 02/11] Upd tests CI to don't touch token package --- .github/workflows/move_test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/move_test.yml b/.github/workflows/move_test.yml index e48799a..2d95910 100644 --- a/.github/workflows/move_test.yml +++ b/.github/workflows/move_test.yml @@ -67,10 +67,8 @@ jobs: TOML=packages/deeptrade-core/Move.toml # Comment production pins, then uncomment the test-fork lines (no duplicate keys). perl -i -pe 's/^(deepbook = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" - perl -i -pe 's/^(token = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" perl -i -pe 's/^(pyth = \{ git = "https:\/\/github.com\/pyth-network\/pyth-crosschain.git".*\})/# $1/' "$TOML" perl -i -pe 's/^#\s*(deepbook = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" - perl -i -pe 's/^#\s*(token = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" perl -i -pe 's/^#\s*(pyth = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/pyth-crosschain.git".*\})/$1/' "$TOML" echo "Move.toml after changes:" cat "$TOML" From 49d56bf538843efb953044482afe521d14fe088a Mon Sep 17 00:00:00 2001 From: bathord Date: Sat, 7 Nov 2026 18:44:12 +0300 Subject: [PATCH 03/11] Handle 0 deep required estimation case when execution > 0 --- packages/deeptrade-core/sources/helper.move | 32 +++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/deeptrade-core/sources/helper.move b/packages/deeptrade-core/sources/helper.move index 57fed4a..18f5c39 100644 --- a/packages/deeptrade-core/sources/helper.move +++ b/packages/deeptrade-core/sources/helper.move @@ -490,14 +490,42 @@ public(package) fun calculate_market_order_params( // Since `get_quantity_out` goes through order book same way as actual order placement, // we can use its `deep_req` value // - For asks: Use order_amount directly as base quantity. Since `get_quantity_out` goes through - // order book same way as actual order placement, we can use its `deep_req` value + // order book same way as actual order placement, we can use its `deep_req` value. + // Exception is the dust-bid case handled below. if (is_bid) { let (base_out, _, deep_req) = pool.get_quantity_out(0, order_amount, clock); let (_, lot_size, _) = pool.pool_book_params(); let floored_base_out = base_out - base_out % lot_size; (floored_base_out, deep_req) } else { - let (_, _, deep_req) = pool.get_quantity_out(order_amount, 0, clock); + let (base_left, _, mut deep_req) = pool.get_quantity_out(order_amount, 0, clock); + let (_, _, min_size) = pool.pool_book_params(); + + // DeepBook v9 `get_quantity_out` can return 0 DEEP required for ask that will require + // DEEP on execution. Example: min_size is 1 SUI, the sell is 10 SUI, and the only + // bid is 0.2 SUI. `get_quantity_out` will say nothing is filled, so no DEEP required, + // but the real order will fill that 0.2 SUI and take DEEP fee for that. If the balance + // manager has no DEEP, transaction aborts. + // How do we handle this? + // First, we check that `get_quantity_out` says nothing is filled (`base_left == order_amount`). + // This can be in 3 cases: empty bids, dust bids (< min_size), or order is too small (< min_size). + // So, at second, we check that the order is at least `min_size` (order_amount >= min_size). + // If both are true, we know that either bids are empty or dust. + // Third, we check that the best bid is live (best_bid.is_some()). + // If all three are true, we know that this is the dust-bid case and the order will require + // DEEP on execution. Since we know that sum of dust bids sizes is less than `min_size` + // (otherwise, `get_quantity_out` returns base_left < order_amount), we can estimate the maximum + // DEEP that can be required to fill these dust bids: `min_size * best_bid_price`. + if (base_left == order_amount && order_amount >= min_size) { + let best_bid = pool.best_bid_price(clock); + if (best_bid.is_some()) { + let (taker_deep, _) = pool.get_order_deep_required( + min_size, + best_bid.destroy_some(), + ); + deep_req = taker_deep; + } + }; (order_amount, deep_req) } } From 16434c9b935554211e84ceb4fbd324f95a983f7a Mon Sep 17 00:00:00 2001 From: bathord Date: Sat, 7 Nov 2026 18:44:26 +0300 Subject: [PATCH 04/11] Add tests for the 0 deep required estimation case --- .../calculate_market_order_params_tests.move | 214 +++++++++++++++++- .../order/create_market_order_tests.move | 111 ++++++++- 2 files changed, 322 insertions(+), 3 deletions(-) diff --git a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move index 7126da9..0c3f2c1 100644 --- a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move +++ b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move @@ -2,9 +2,9 @@ module deeptrade_core::calculate_market_order_params_tests; use deepbook::balance_manager::BalanceManager; -use deepbook::balance_manager_tests::{USDC, create_acct_and_share_with_funds}; +use deepbook::balance_manager_tests::{USDC, USDT, create_acct_and_share_with_funds}; use deepbook::constants; -use deepbook::pool::Pool; +use deepbook::pool::{Self, Pool}; use deepbook::pool_tests::{ setup_test, setup_pool_with_default_fees, @@ -13,6 +13,7 @@ use deepbook::pool_tests::{ add_deep_price_point, set_time, }; +use deepbook::registry::Registry; use deeptrade_core::fee; use deeptrade_core::fee_manager::{Self, FeeManager}; use deeptrade_core::get_sui_per_deep_from_oracle_tests::{ @@ -401,3 +402,212 @@ fun ask_base_quantity_direct() { price_info::destroy(sui_price); end(scenario); } + +/// A sell larger than `min_size` against one bid smaller than `min_size` quotes the +/// taker DEEP for `min_size`, not the 0 DeepBook reports. +/// +/// The DEEP/SUI reference mid is 100 SUI per DEEP, so this SUI pool stores 0.01 DEEP +/// per SUI (`10_000_000` scaled). The pool is volatile, so the taker fee is +/// `1_000_000`. The fee is `base * 10_000_000 / 1e9 * 1_000_000 / 1e9 = base / 100_000`. +/// `min_size` is `1_000_000`, so that stand-in fee is 10. The bid left on the book is +/// `500_000`, which would cost 5, and DeepBook's own quote is 0 because `500_000 < min_size`. +#[test] +fun ask_dust_bid_quotes_min_size_deep_bound() { + let (mut scenario, _pool_id, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = + setup_test_environment(); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + leave_dust_bid(pool_id, balance_manager_id, balance_manager_id, &mut scenario); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 2_000_000; + + let (_, _, quoted_by_deepbook) = pool.get_quantity_out(sell_amount, 0, &clock); + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert_eq!(quoted_by_deepbook, 0); + assert_eq!(base_quantity, sell_amount); + // 1_000_000 / 100_000 = 10 + assert_eq!(deep_required, 10); + + return_shared(pool); + return_shared(clock); + }; + + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + +/// An empty bid side has no live bid, so the sell still quotes 0 DEEP. +#[test] +fun ask_empty_book_quotes_zero_deep() { + let (mut scenario, _pool_id, _, _, reference_pool_id, deep_price, sui_price, _) = + setup_test_environment(); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 2_000_000; + + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert_eq!(base_quantity, sell_amount); + assert_eq!(deep_required, 0); + + return_shared(pool); + return_shared(clock); + }; + + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + +/// A bid of at least `min_size` is a real fill. The quote's own DEEP fee is kept: +/// selling `2_000_000` costs 20, which is more than the `min_size` stand-in of 10. +#[test] +fun ask_bid_covering_min_size_quotes_fill_fee() { + let ( + mut scenario, + _pool_id, + balance_manager_id, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + fund_usdt(balance_manager_id, &mut scenario); + let bid = place_limit_order( + ALICE, + pool_id, + balance_manager_id, + 1, + constants::no_restriction(), + constants::self_matching_allowed(), + 2 * constants::float_scaling(), + 2_000_000, + true, + true, + constants::max_u64(), + &mut scenario, + ); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 2_000_000; + + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert_eq!(base_quantity, sell_amount); + // 2_000_000 / 100_000 = 20 + assert_eq!(deep_required, 20); + + return_shared(pool); + return_shared(clock); + }; + + destroy(bid); + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + +/// SUI/USDT pool whose `min_size` is `1_000_000`, priced from the standard DEEP/SUI reference. +/// USDT is a fresh pair: the setup already registered SUI/USDC. +public(package) fun create_wide_min_pool(reference_pool_id: ID, scenario: &mut Scenario): ID { + scenario.next_tx(ALICE); + let pool_id = { + let mut registry = scenario.take_shared(); + let pool_id = pool::create_permissionless_pool( + &mut registry, + constants::tick_size(), + constants::lot_size(), + 1_000_000, + coin::mint_for_testing(constants::pool_creation_fee(), scenario.ctx()), + scenario.ctx(), + ); + return_shared(registry); + pool_id + }; + add_deep_price_point(ALICE, pool_id, reference_pool_id, scenario); + pool_id +} + +/// Leaves a single live bid of `500_000`, which is below the wide pool's `min_size`. +/// The maker rests `1_500_000` and the taker sells `1_000_000` of it. +public(package) fun leave_dust_bid( + pool_id: ID, + maker_balance_manager_id: ID, + taker_balance_manager_id: ID, + scenario: &mut Scenario, +) { + fund_usdt(maker_balance_manager_id, scenario); + let price = 2 * constants::float_scaling(); + let bid = place_limit_order( + ALICE, + pool_id, + maker_balance_manager_id, + 1, + constants::no_restriction(), + constants::self_matching_allowed(), + price, + 1_500_000, + true, + true, + constants::max_u64(), + scenario, + ); + let taken = place_limit_order( + ALICE, + pool_id, + taker_balance_manager_id, + 2, + constants::immediate_or_cancel(), + constants::self_matching_allowed(), + price, + 1_000_000, + false, + true, + constants::max_u64(), + scenario, + ); + destroy(bid); + destroy(taken); +} + +/// The shared setup funds USDC. These tests trade USDT, so the maker needs a quote balance. +fun fund_usdt(balance_manager_id: ID, scenario: &mut Scenario) { + scenario.next_tx(ALICE); + { + let mut balance_manager = scenario.take_shared_by_id(balance_manager_id); + balance_manager.deposit( + coin::mint_for_testing(1000 * constants::float_scaling(), scenario.ctx()), + scenario.ctx(), + ); + return_shared(balance_manager); + }; +} diff --git a/packages/deeptrade-core/tests/order/create_market_order_tests.move b/packages/deeptrade-core/tests/order/create_market_order_tests.move index 3874c0e..b42e43e 100644 --- a/packages/deeptrade-core/tests/order/create_market_order_tests.move +++ b/packages/deeptrade-core/tests/order/create_market_order_tests.move @@ -2,7 +2,7 @@ module deeptrade_core::create_market_order_tests; use deepbook::balance_manager::BalanceManager; -use deepbook::balance_manager_tests::{USDC, create_acct_and_share_with_funds}; +use deepbook::balance_manager_tests::{USDC, USDT, create_acct_and_share_with_funds}; use deepbook::constants; use deepbook::pool::Pool; use deepbook::pool_tests::{ @@ -13,6 +13,7 @@ use deepbook::pool_tests::{ add_deep_price_point, set_time, }; +use deeptrade_core::calculate_market_order_params_tests::{create_wide_min_pool, leave_dust_bid}; use deeptrade_core::dt_math as math; use deeptrade_core::dt_order::{Self as order, create_market_order}; use deeptrade_core::fee::{Self, TradingFeeConfig}; @@ -300,6 +301,114 @@ fun unsupported_self_matching_option() { end(scenario); } +/// A dust ask fill charges the real DEEP fee when the wallet covers the `min_size` stand-in. +/// +/// The book has one bid of `500_000` under a `min_size` of `1_000_000`. DeepBook quotes 0 DEEP +/// for a sell of `2_000_000`. The stand-in is 10 (`1_000_000 / 100_000`) and the fill itself +/// costs 5 (`500_000 / 100_000`). The balance manager starts with no DEEP and the wallet holds +/// exactly 10, so the order completes and 5 DEEP remains. +#[test] +fun ask_dust_fill_charges_less_deep_than_the_bound() { + let ( + mut scenario, + _pool_id, + balance_manager_id, + fee_manager_id, + reference_pool_id, + deep_price, + sui_price, + ) = setup_test_environment(); + + let maker_balance_manager_id = create_acct_and_share_with_funds( + ALICE, + 1000 * constants::float_scaling(), + &mut scenario, + ); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + leave_dust_bid(pool_id, maker_balance_manager_id, balance_manager_id, &mut scenario); + + scenario.next_tx(ALICE); + { + let mut balance_manager = scenario.take_shared_by_id(balance_manager_id); + let leftover_deep = balance_manager.withdraw_all(scenario.ctx()); + destroy(leftover_deep); + return_shared(balance_manager); + }; + + scenario.next_tx(ALICE); + { + let mut treasury = scenario.take_shared(); + let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); + let trading_fee_config = scenario.take_shared(); + let loyalty_program = scenario.take_shared(); + let mut pool = scenario.take_shared_by_id>(pool_id); + let reference_pool = scenario.take_shared_by_id>(reference_pool_id); + let mut balance_manager = scenario.take_shared_by_id(balance_manager_id); + let clock = scenario.take_shared(); + + let base_coin = coin::mint_for_testing(0, scenario.ctx()); + let quote_coin = coin::mint_for_testing(0, scenario.ctx()); + let deep_coin = coin::mint_for_testing(10, scenario.ctx()); + let sui_coin = coin::mint_for_testing(0, scenario.ctx()); + + assert_eq!(balance_manager.balance(), 0); + + let (order_info, base_coin, quote_coin, deep_coin, sui_coin) = create_market_order< + SUI, + USDT, + DEEP, + SUI, + >( + &mut treasury, + &mut fee_manager, + &trading_fee_config, + &loyalty_program, + &mut pool, + &reference_pool, + &deep_price, + &sui_price, + &mut balance_manager, + base_coin, + quote_coin, + deep_coin, + sui_coin, + 2_000_000, + false, + constants::self_matching_allowed(), + 3, + 10, + 0, + 0, + 0, + &clock, + scenario.ctx(), + ); + + // The 500_000 bid fills. The wallet's 10 DEEP was deposited and 5 was charged. + assert_eq!(order_info.executed_quantity(), 500_000); + assert_eq!(deep_coin.value(), 0); + assert_eq!(balance_manager.balance(), 5); + + destroy(order_info); + destroy(base_coin); + destroy(quote_coin); + destroy(deep_coin); + destroy(sui_coin); + return_shared(treasury); + return_shared(fee_manager); + return_shared(trading_fee_config); + return_shared(loyalty_program); + return_shared(pool); + return_shared(reference_pool); + return_shared(balance_manager); + return_shared(clock); + }; + + destroy(deep_price); + destroy(sui_price); + end(scenario); +} + // === Helper Functions === /// Sets up a complete test environment with treasury, deepbook infrastructure, and fee components. From 141931b0c3b7ce46053c7bbee4fc8fab920dec7b Mon Sep 17 00:00:00 2001 From: bathord Date: Sat, 7 Nov 2026 18:45:15 +0300 Subject: [PATCH 05/11] Bump deepbook & token deps to v9.0.0 + upd test deps --- packages/deeptrade-core/Move.lock | 173 +++++++++++++----------------- packages/deeptrade-core/Move.toml | 7 +- 2 files changed, 79 insertions(+), 101 deletions(-) diff --git a/packages/deeptrade-core/Move.lock b/packages/deeptrade-core/Move.lock index 5a5d908..a3a42cf 100644 --- a/packages/deeptrade-core/Move.lock +++ b/packages/deeptrade-core/Move.lock @@ -1,100 +1,77 @@ -# @generated by Move, please check-in and do not edit manually. +# Generated by move; do not edit +# This file should be checked in. [move] -version = 3 -manifest_digest = "69931A1480BE3DF484F425E055C6C764F7512F1B0D6FF5EBFE31A5313B1021A4" -deps_digest = "CAFAD8A7CF51067FB4358215BECB86BD100DD64E57C2AC8A7AE7D74B688F5965" -dependencies = [ - { id = "Bridge", name = "Bridge" }, - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Pyth", name = "Pyth" }, - { id = "Sui", name = "Sui" }, - { id = "SuiSystem", name = "SuiSystem" }, - { id = "deepbook", name = "deepbook" }, - { id = "multisig", name = "multisig" }, -] - -[[move.package]] -id = "Bridge" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "4e8b6eda7d6411d80c62f39ac8a4f028e8d174c4", subdir = "crates/sui-framework/packages/bridge" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, - { id = "SuiSystem", name = "SuiSystem" }, -] - -[[move.package]] -id = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "4e8b6eda7d6411d80c62f39ac8a4f028e8d174c4", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -id = "Pyth" -source = { git = "https://github.com/pyth-network/pyth-crosschain.git", rev = "sui-contract-mainnet", subdir = "target_chains/sui/contracts" } - -dependencies = [ - { id = "Sui", name = "Sui" }, - { id = "Wormhole", name = "Wormhole" }, -] - -[[move.package]] -id = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "4e8b6eda7d6411d80c62f39ac8a4f028e8d174c4", subdir = "crates/sui-framework/packages/sui-framework" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, -] - -[[move.package]] -id = "SuiSystem" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "4e8b6eda7d6411d80c62f39ac8a4f028e8d174c4", subdir = "crates/sui-framework/packages/sui-system" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, -] - -[[move.package]] -id = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "sui/mainnet", subdir = "sui/wormhole" } - -dependencies = [ - { id = "Sui", name = "Sui" }, -] - -[[move.package]] -id = "deepbook" -source = { git = "https://github.com/MystenLabs/deepbookv3.git", rev = "v3.0.0", subdir = "packages/deepbook" } - -dependencies = [ - { id = "Sui", name = "Sui" }, - { id = "token", name = "token" }, -] - -[[move.package]] -id = "multisig" -source = { git = "https://github.com/DeeptradeProtocol/multisig-move.git", rev = "v1.0.1", subdir = "move" } - -dependencies = [ - { id = "Bridge", name = "Bridge" }, - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, - { id = "SuiSystem", name = "SuiSystem" }, -] - -[[move.package]] -id = "token" -source = { git = "https://github.com/MystenLabs/deepbookv3.git", rev = "v3.0.0", subdir = "packages/token" } - -dependencies = [ - { id = "Sui", name = "Sui" }, -] - -[move.toolchain-version] -compiler-version = "1.62.1" -edition = "2024.beta" -flavor = "sui" - -[env] - -[env.mainnet] +version = 4 + +[pinned.mainnet.MoveStdlib] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\move-stdlib', rev = "ae59d7718668b468ce65702ecb0440aa2330f389" } +use_environment = "mainnet" +manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" +deps = {} + +[pinned.mainnet.MoveStdlib_1] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\move-stdlib', rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } +use_environment = "mainnet" +manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" +deps = {} + +[pinned.mainnet.Pyth] +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\contracts', rev = "3bd1262dcba9518a6901aa6a15f04072799bfb37" } +use_environment = "mainnet" +manifest_digest = "F2C5AF85C4B72C8F6A8132D05DE4F787F4EB80DBFF812331ED65AE599E7DF92A" +deps = { Sui = "Sui_1", Wormhole = "Wormhole" } + +[pinned.mainnet.PythProCompatible] +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\contracts', rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } +use_environment = "mainnet" +manifest_digest = "3D2B5BBF2058F3C9DCA3527CC2486C518124687B60479CA9875E5475652B7BBC" +deps = { Sui = "Sui_1", WormholeSimpleMajority = "WormholeSimpleMajority" } + +[pinned.mainnet.Sui] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\sui-framework', rev = "ae59d7718668b468ce65702ecb0440aa2330f389" } +use_environment = "mainnet" +manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195" +deps = { MoveStdlib = "MoveStdlib" } + +[pinned.mainnet.Sui_1] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\sui-framework', rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } +use_environment = "mainnet" +manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195" +deps = { MoveStdlib = "MoveStdlib_1" } + +[pinned.mainnet.Wormhole] +source = { git = "https://github.com/wormhole-foundation/wormhole.git", subdir = 'sui\wormhole', rev = "b71be5cbb9537c4aac8e23e74371affa3825efcd" } +use_environment = "mainnet" +manifest_digest = "0D766A0380B75080707CFA6099AF469A2E502B0D9DC334A9E9983B391C5555D9" +deps = { Sui = "Sui_1" } + +[pinned.mainnet.WormholeSimpleMajority] +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\vendor\wormhole_simple_majority\wormhole', rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } +use_environment = "mainnet" +manifest_digest = "0D766A0380B75080707CFA6099AF469A2E502B0D9DC334A9E9983B391C5555D9" +deps = { Sui = "Sui_1" } + +[pinned.mainnet.deepbook] +source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = 'packages\deepbook', rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } +use_environment = "mainnet" +manifest_digest = "BBE7F8BFEF088532C5358623C6F5B3153EE14B3265ABA833B0F3CF9D313264A7" +deps = { std = "MoveStdlib", sui = "Sui", token = "token" } + +[pinned.mainnet.deeptrade_core] +source = { root = true } +use_environment = "mainnet" +manifest_digest = "349A4DE56587A83933E2890B98CC6978620033B8002BE40ABC22DBFE28820C32" +deps = { deepbook = "deepbook", multisig = "multisig", pyth = "Pyth", pyth_upgraded = "PythProCompatible", std = "MoveStdlib", sui = "Sui", token = "token" } + +[pinned.mainnet.multisig] +source = { git = "https://github.com/DeeptradeProtocol/multisig-move.git", subdir = "move", rev = "263aeb17cd55115a1b2300a6cf5272e7962e7f7a" } +use_environment = "mainnet" +manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6" +deps = { std = "MoveStdlib", sui = "Sui" } + +[pinned.mainnet.token] +source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = 'packages\token', rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } +use_environment = "mainnet" +manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6" +deps = { std = "MoveStdlib", sui = "Sui" } diff --git a/packages/deeptrade-core/Move.toml b/packages/deeptrade-core/Move.toml index 4fa3d85..87dba9c 100644 --- a/packages/deeptrade-core/Move.toml +++ b/packages/deeptrade-core/Move.toml @@ -4,8 +4,8 @@ version = "2.0.0" edition = "2024.alpha" [dependencies] -deepbook = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/deepbook", rev = "v4.0.0" } -token = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "main" } +deepbook = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/deepbook", rev = "v9.0.0" } +token = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "v9.0.0" } multisig = { git = "https://github.com/DeeptradeProtocol/multisig-move.git", subdir = "move", rev = "v1.0.1" } # Pyth dependency @@ -16,5 +16,6 @@ pyth_upgraded = { git = "https://github.com/pyth-network/pyth-crosschain.git", s # For `sui move test`, comment the production pins above and uncomment these. # Never leave both copies uncommented. -# deepbook = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/deepbook", rev = "v4-tag-fork-test-methods" } +# deepbook = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/deepbook", rev = "v9-tag-fork-test-methods" } +# token = { git = "https://github.com/DeeptradeProtocol/deepbookv3.git", subdir = "packages/token", rev = "v9-tag-fork-test-methods" } # pyth = { git = "https://github.com/DeeptradeProtocol/pyth-crosschain.git", subdir = "target_chains/sui/contracts", rev = "sui-contract-mainnet" } From d2dff8994eeebbcad39f10e4c362993f5048549b Mon Sep 17 00:00:00 2001 From: bathord Date: Sat, 7 Nov 2026 19:18:13 +0300 Subject: [PATCH 06/11] Add 4 test cases Quote-priced dust bid, Several dust bids, Order below the minimum size, Treasury reserves. --- .../calculate_market_order_params_tests.move | 249 ++++++++++++++++++ .../order/create_market_order_tests.move | 116 ++++++++ 2 files changed, 365 insertions(+) diff --git a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move index 0c3f2c1..a495a72 100644 --- a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move +++ b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move @@ -4,6 +4,7 @@ module deeptrade_core::calculate_market_order_params_tests; use deepbook::balance_manager::BalanceManager; use deepbook::balance_manager_tests::{USDC, USDT, create_acct_and_share_with_funds}; use deepbook::constants; +use deepbook::deep_price::{asset_is_base, deep_per_asset}; use deepbook::pool::{Self, Pool}; use deepbook::pool_tests::{ setup_test, @@ -30,6 +31,7 @@ use pyth::price_info::{Self, PriceInfoObject}; use std::unit_test::assert_eq; use sui::clock; use sui::coin; +use std::option::is_some; use sui::sui::SUI; use sui::test_scenario::{Self, Scenario, end, return_shared}; use sui::test_utils::destroy; @@ -536,6 +538,188 @@ fun ask_bid_covering_min_size_quotes_fill_fee() { end(scenario); } +/// DEEP priced per quote. The fee is `taker_fee * quote_notional * deep_per_sui`, so the +/// `min_size` stand-in depends on the best bid's price. +/// +/// USDT/SUI puts SUI on the quote side of the DEEP/SUI reference, so the stored rate is +/// DEEP per SUI. The reference mid is 100 SUI per DEEP, which is `10_000_000` DEEP per SUI. +/// The pool is volatile, so the taker fee is `1_000_000`. One bid of `500_000` at 2 SUI sits +/// under `min_size` of `1_000_000`, and DeepBook quotes 0. The stand-in sells `min_size` at +/// that price: quote `1_000_000 * 2 = 2_000_000`, DEEP `2_000_000 * 10_000_000 / 1e9 = 20_000`, +/// fee `20_000 * 1_000_000 / 1e9 = 20`. The same bid on a base-priced pool quotes 10. +#[test] +fun ask_quote_priced_dust_bid_quotes_notional_bound() { + let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = + setup_test_environment(); + let pool_id = create_quote_priced_pool(reference_pool_id, &mut scenario); + fund_usdt(balance_manager_id, &mut scenario); + shave_quote_priced_bid( + pool_id, + balance_manager_id, + 1, + 2 * constants::float_scaling(), + 1_500_000, + 500_000, + &mut scenario, + ); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 2_000_000; + + let order_deep_price = pool.get_order_deep_price(); + let (_, _, quoted_by_deepbook) = pool.get_quantity_out(sell_amount, 0, &clock); + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert!(!order_deep_price.asset_is_base()); + assert_eq!(order_deep_price.deep_per_asset(), 10_000_000); + assert_eq!(quoted_by_deepbook, 0); + assert_eq!(base_quantity, sell_amount); + // 20_000 * 1_000_000 / 1e9 = 20 + assert_eq!(deep_required, 20); + + return_shared(pool); + return_shared(clock); + }; + + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + +/// Three dust bids at different prices still sum to less than `min_size`, so DeepBook quotes 0. +/// The stand-in uses the best of those prices. +/// +/// Each bid is shaved to `200_000`. Worst first, and each sell is priced at its own bid, so a +/// sell never reaches the cheaper dust already on the book. `200_000 * 3 = 600_000`, under +/// `min_size` of `1_000_000`. The best price is 3 SUI: quote `3_000_000`, DEEP `30_000`, +/// fee `30_000 * 1_000_000 / 1e9 = 30`. +#[test] +fun ask_several_dust_bids_bound_uses_best_price() { + let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = + setup_test_environment(); + let pool_id = create_quote_priced_pool(reference_pool_id, &mut scenario); + fund_usdt(balance_manager_id, &mut scenario); + + let one_sui = constants::float_scaling(); + shave_quote_priced_bid( + pool_id, + balance_manager_id, + 1, + one_sui, + 1_200_000, + 200_000, + &mut scenario, + ); + shave_quote_priced_bid( + pool_id, + balance_manager_id, + 3, + 2 * one_sui, + 1_200_000, + 200_000, + &mut scenario, + ); + shave_quote_priced_bid( + pool_id, + balance_manager_id, + 5, + 3 * one_sui, + 1_200_000, + 200_000, + &mut scenario, + ); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 2_000_000; + + let (_, _, quoted_by_deepbook) = pool.get_quantity_out(sell_amount, 0, &clock); + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert_eq!(quoted_by_deepbook, 0); + assert_eq!(base_quantity, sell_amount); + // 30_000 * 1_000_000 / 1e9 = 30 + assert_eq!(deep_required, 30); + + return_shared(pool); + return_shared(clock); + }; + + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + +/// An order smaller than `min_size` quotes 0 even with a live bid behind it. +/// DeepBook rejects the order itself, so the dust stand-in must not apply. +#[test] +fun ask_below_min_size_quotes_zero_deep() { + let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = + setup_test_environment(); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + fund_usdt(balance_manager_id, &mut scenario); + let bid = place_limit_order( + ALICE, + pool_id, + balance_manager_id, + 1, + constants::no_restriction(), + constants::self_matching_allowed(), + 2 * constants::float_scaling(), + 2_000_000, + true, + true, + constants::max_u64(), + &mut scenario, + ); + + scenario.next_tx(ALICE); + { + let pool = scenario.take_shared_by_id>(pool_id); + let clock = scenario.take_shared(); + let sell_amount = 500_000; + let (_, _, min_size) = pool.pool_book_params(); + + let (base_left, _, quoted_by_deepbook) = pool.get_quantity_out(sell_amount, 0, &clock); + let (base_quantity, deep_required) = helper::calculate_market_order_params( + &pool, + sell_amount, + false, + &clock, + ); + + assert!(sell_amount < min_size); + assert!(pool.best_bid_price(&clock).is_some()); + assert_eq!(base_left, sell_amount); + assert_eq!(quoted_by_deepbook, 0); + assert_eq!(base_quantity, sell_amount); + assert_eq!(deep_required, 0); + + return_shared(pool); + return_shared(clock); + }; + + destroy(bid); + price_info::destroy(deep_price); + price_info::destroy(sui_price); + end(scenario); +} + /// SUI/USDT pool whose `min_size` is `1_000_000`, priced from the standard DEEP/SUI reference. /// USDT is a fresh pair: the setup already registered SUI/USDC. public(package) fun create_wide_min_pool(reference_pool_id: ID, scenario: &mut Scenario): ID { @@ -599,6 +783,71 @@ public(package) fun leave_dust_bid( destroy(taken); } +/// USDT/SUI pool whose `min_size` is `1_000_000`. SUI is the quote, so the DEEP/SUI reference +/// prices DEEP per quote (`asset_is_base` is false). +fun create_quote_priced_pool(reference_pool_id: ID, scenario: &mut Scenario): ID { + scenario.next_tx(ALICE); + let pool_id = { + let mut registry = scenario.take_shared(); + let pool_id = pool::create_permissionless_pool( + &mut registry, + constants::tick_size(), + constants::lot_size(), + 1_000_000, + coin::mint_for_testing(constants::pool_creation_fee(), scenario.ctx()), + scenario.ctx(), + ); + return_shared(registry); + pool_id + }; + add_deep_price_point(ALICE, pool_id, reference_pool_id, scenario); + pool_id +} + +/// Rests `rest_quantity` at `price` on the USDT/SUI pool, then sells the difference at the same +/// price so `leave_quantity` remains. The ask price stops the sell from matching a worse bid, +/// which is what makes shaving several prices safe when done worst price first. +fun shave_quote_priced_bid( + pool_id: ID, + balance_manager_id: ID, + client_order_id: u64, + price: u64, + rest_quantity: u64, + leave_quantity: u64, + scenario: &mut Scenario, +) { + let bid = place_limit_order( + ALICE, + pool_id, + balance_manager_id, + client_order_id, + constants::no_restriction(), + constants::self_matching_allowed(), + price, + rest_quantity, + true, + true, + constants::max_u64(), + scenario, + ); + let taken = place_limit_order( + ALICE, + pool_id, + balance_manager_id, + client_order_id + 1, + constants::immediate_or_cancel(), + constants::self_matching_allowed(), + price, + rest_quantity - leave_quantity, + false, + true, + constants::max_u64(), + scenario, + ); + destroy(bid); + destroy(taken); +} + /// The shared setup funds USDC. These tests trade USDT, so the maker needs a quote balance. fun fund_usdt(balance_manager_id: ID, scenario: &mut Scenario) { scenario.next_tx(ALICE); diff --git a/packages/deeptrade-core/tests/order/create_market_order_tests.move b/packages/deeptrade-core/tests/order/create_market_order_tests.move index b42e43e..a4be64a 100644 --- a/packages/deeptrade-core/tests/order/create_market_order_tests.move +++ b/packages/deeptrade-core/tests/order/create_market_order_tests.move @@ -409,6 +409,122 @@ fun ask_dust_fill_charges_less_deep_than_the_bound() { end(scenario); } +/// No DEEP in the wallet or the balance manager. Reserves lend the stand-in of 10, the +/// coverage fee is the SUI value of those 10, and the fill spends 5, which stays behind. +/// +/// The book is the same dust bid as `ask_dust_fill_charges_less_deep_than_the_bound`: `500_000` +/// under a `min_size` of `1_000_000`, stand-in 10, fill cost 5. Oracle DEEP/USD is 3 and +/// SUI/USD is 10, both at exponent -8, so SUI per DEEP is `300_000_000_000`. That beats the +/// reference ask of 180 SUI per DEEP. Coverage on the borrowed 10 is +/// `10 * 300_000_000_000 / 1e9 = 3000` SUI, taken from the balance manager. Coverage on the 5 +/// actually charged would be 1500. +#[test] +fun ask_dust_fill_borrows_bound_from_reserves() { + let ( + mut scenario, + _pool_id, + balance_manager_id, + fee_manager_id, + reference_pool_id, + deep_price, + sui_price, + ) = setup_test_environment(); + + let maker_balance_manager_id = create_acct_and_share_with_funds( + ALICE, + 1000 * constants::float_scaling(), + &mut scenario, + ); + let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); + leave_dust_bid(pool_id, maker_balance_manager_id, balance_manager_id, &mut scenario); + + scenario.next_tx(ALICE); + { + let mut balance_manager = scenario.take_shared_by_id(balance_manager_id); + let leftover_deep = balance_manager.withdraw_all(scenario.ctx()); + destroy(leftover_deep); + return_shared(balance_manager); + }; + + scenario.next_tx(ALICE); + { + let mut treasury = scenario.take_shared(); + let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); + let trading_fee_config = scenario.take_shared(); + let loyalty_program = scenario.take_shared(); + let mut pool = scenario.take_shared_by_id>(pool_id); + let reference_pool = scenario.take_shared_by_id>(reference_pool_id); + let mut balance_manager = scenario.take_shared_by_id(balance_manager_id); + let clock = scenario.take_shared(); + + let base_coin = coin::mint_for_testing(0, scenario.ctx()); + let quote_coin = coin::mint_for_testing(0, scenario.ctx()); + let deep_coin = coin::mint_for_testing(0, scenario.ctx()); + let sui_coin = coin::mint_for_testing(0, scenario.ctx()); + let reserves_before = treasury.deep_reserves(); + + assert_eq!(balance_manager.balance(), 0); + assert_eq!(treasury.get_deep_reserves_coverage_fee_balance(), 0); + + let (order_info, base_coin, quote_coin, deep_coin, sui_coin) = create_market_order< + SUI, + USDT, + DEEP, + SUI, + >( + &mut treasury, + &mut fee_manager, + &trading_fee_config, + &loyalty_program, + &mut pool, + &reference_pool, + &deep_price, + &sui_price, + &mut balance_manager, + base_coin, + quote_coin, + deep_coin, + sui_coin, + 2_000_000, + false, + constants::self_matching_allowed(), + 3, + 10, + 0, + 3000, + 0, + &clock, + scenario.ctx(), + ); + + // Reserves lent 10 and the fill charged 5, so 5 remains. Coverage was charged on 10. + assert_eq!(order_info.executed_quantity(), 500_000); + assert_eq!(deep_coin.value(), 0); + assert_eq!(sui_coin.value(), 0); + assert_eq!(balance_manager.balance(), 5); + assert_eq!(treasury.deep_reserves(), reserves_before - 10); + assert_eq!(treasury.get_deep_reserves_coverage_fee_balance(), 3000); + + destroy(order_info); + destroy(base_coin); + destroy(quote_coin); + destroy(deep_coin); + destroy(sui_coin); + return_shared(treasury); + return_shared(fee_manager); + return_shared(trading_fee_config); + return_shared(loyalty_program); + return_shared(pool); + return_shared(reference_pool); + return_shared(balance_manager); + return_shared(clock); + }; + + destroy(deep_price); + destroy(sui_price); + end(scenario); +} + // === Helper Functions === /// Sets up a complete test environment with treasury, deepbook infrastructure, and fee components. From b11e595ccd88f80bf83b3a9a7fa0652a0d77baad Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 9 Nov 2026 10:00:28 +0300 Subject: [PATCH 07/11] Prettify tests --- .../calculate_market_order_params_tests.move | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move index a495a72..8858229 100644 --- a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move +++ b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move @@ -28,10 +28,10 @@ use deeptrade_core::treasury; use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use multisig::multisig_test_utils::get_test_multisig_address; use pyth::price_info::{Self, PriceInfoObject}; +use std::option::is_some; use std::unit_test::assert_eq; use sui::clock; use sui::coin; -use std::option::is_some; use sui::sui::SUI; use sui::test_scenario::{Self, Scenario, end, return_shared}; use sui::test_utils::destroy; @@ -415,8 +415,16 @@ fun ask_base_quantity_direct() { /// `500_000`, which would cost 5, and DeepBook's own quote is 0 because `500_000 < min_size`. #[test] fun ask_dust_bid_quotes_min_size_deep_bound() { - let (mut scenario, _pool_id, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = - setup_test_environment(); + let ( + mut scenario, + _pool_id, + balance_manager_id, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); leave_dust_bid(pool_id, balance_manager_id, balance_manager_id, &mut scenario); @@ -451,8 +459,16 @@ fun ask_dust_bid_quotes_min_size_deep_bound() { /// An empty bid side has no live bid, so the sell still quotes 0 DEEP. #[test] fun ask_empty_book_quotes_zero_deep() { - let (mut scenario, _pool_id, _, _, reference_pool_id, deep_price, sui_price, _) = - setup_test_environment(); + let ( + mut scenario, + _pool_id, + _, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); scenario.next_tx(ALICE); @@ -549,8 +565,16 @@ fun ask_bid_covering_min_size_quotes_fill_fee() { /// fee `20_000 * 1_000_000 / 1e9 = 20`. The same bid on a base-priced pool quotes 10. #[test] fun ask_quote_priced_dust_bid_quotes_notional_bound() { - let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = - setup_test_environment(); + let ( + mut scenario, + _, + balance_manager_id, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); let pool_id = create_quote_priced_pool(reference_pool_id, &mut scenario); fund_usdt(balance_manager_id, &mut scenario); shave_quote_priced_bid( @@ -603,8 +627,16 @@ fun ask_quote_priced_dust_bid_quotes_notional_bound() { /// fee `30_000 * 1_000_000 / 1e9 = 30`. #[test] fun ask_several_dust_bids_bound_uses_best_price() { - let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = - setup_test_environment(); + let ( + mut scenario, + _, + balance_manager_id, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); let pool_id = create_quote_priced_pool(reference_pool_id, &mut scenario); fund_usdt(balance_manager_id, &mut scenario); @@ -669,8 +701,16 @@ fun ask_several_dust_bids_bound_uses_best_price() { /// DeepBook rejects the order itself, so the dust stand-in must not apply. #[test] fun ask_below_min_size_quotes_zero_deep() { - let (mut scenario, _, balance_manager_id, _, reference_pool_id, deep_price, sui_price, _) = - setup_test_environment(); + let ( + mut scenario, + _, + balance_manager_id, + _, + reference_pool_id, + deep_price, + sui_price, + _, + ) = setup_test_environment(); let pool_id = create_wide_min_pool(reference_pool_id, &mut scenario); fund_usdt(balance_manager_id, &mut scenario); let bid = place_limit_order( From 80021d85a06a51a62a19607894efe3ffaa05dd23 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 9 Nov 2026 10:04:12 +0300 Subject: [PATCH 08/11] Fix Move test CI: comment/uncomment token dependency --- .github/workflows/move_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/move_test.yml b/.github/workflows/move_test.yml index 2d95910..e48799a 100644 --- a/.github/workflows/move_test.yml +++ b/.github/workflows/move_test.yml @@ -67,8 +67,10 @@ jobs: TOML=packages/deeptrade-core/Move.toml # Comment production pins, then uncomment the test-fork lines (no duplicate keys). perl -i -pe 's/^(deepbook = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" + perl -i -pe 's/^(token = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" perl -i -pe 's/^(pyth = \{ git = "https:\/\/github.com\/pyth-network\/pyth-crosschain.git".*\})/# $1/' "$TOML" perl -i -pe 's/^#\s*(deepbook = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" + perl -i -pe 's/^#\s*(token = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" perl -i -pe 's/^#\s*(pyth = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/pyth-crosschain.git".*\})/$1/' "$TOML" echo "Move.toml after changes:" cat "$TOML" From e742938de857937f3ccc007c64efedf366d0a37c Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 9 Nov 2026 11:18:26 +0300 Subject: [PATCH 09/11] Fix Move test CI after resolving conflicts --- .github/workflows/move_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/move_test.yml b/.github/workflows/move_test.yml index 03ca6e7..eed32bd 100644 --- a/.github/workflows/move_test.yml +++ b/.github/workflows/move_test.yml @@ -67,8 +67,10 @@ jobs: TOML=packages/deeptrade-core/Move.toml # Comment production pins, then uncomment the test-fork lines (no duplicate keys). perl -i -pe 's/^(deepbook = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" + perl -i -pe 's/^(token = \{ git = "https:\/\/github.com\/MystenLabs\/deepbookv3.git".*\})/# $1/' "$TOML" perl -i -pe 's/^(pyth = \{ git = "https:\/\/github.com\/pyth-network\/pyth-crosschain.git".*\})/# $1/' "$TOML" perl -i -pe 's/^#\s*(deepbook = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" + perl -i -pe 's/^#\s*(token = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/deepbookv3.git".*\})/$1/' "$TOML" perl -i -pe 's/^#\s*(pyth = \{ git = "https:\/\/github.com\/DeeptradeProtocol\/pyth-crosschain.git".*\})/$1/' "$TOML" echo "Move.toml after changes:" cat "$TOML" From c1ffc74de47621a5af2bcd1d08728d51fe8c457b Mon Sep 17 00:00:00 2001 From: avernikoz Date: Mon, 9 Nov 2026 11:31:05 +0300 Subject: [PATCH 10/11] regenerate move.lock (fix backslashes) --- packages/deeptrade-core/Move.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/deeptrade-core/Move.lock b/packages/deeptrade-core/Move.lock index a3a42cf..da3f877 100644 --- a/packages/deeptrade-core/Move.lock +++ b/packages/deeptrade-core/Move.lock @@ -5,55 +5,55 @@ version = 4 [pinned.mainnet.MoveStdlib] -source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\move-stdlib', rev = "ae59d7718668b468ce65702ecb0440aa2330f389" } +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "b0535f1f3a3310e71790e90d8ae4e8ca840c897e" } use_environment = "mainnet" manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" deps = {} [pinned.mainnet.MoveStdlib_1] -source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\move-stdlib', rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } use_environment = "mainnet" manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" deps = {} [pinned.mainnet.Pyth] -source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\contracts', rev = "3bd1262dcba9518a6901aa6a15f04072799bfb37" } +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = "target_chains/sui/contracts", rev = "3bd1262dcba9518a6901aa6a15f04072799bfb37" } use_environment = "mainnet" manifest_digest = "F2C5AF85C4B72C8F6A8132D05DE4F787F4EB80DBFF812331ED65AE599E7DF92A" deps = { Sui = "Sui_1", Wormhole = "Wormhole" } [pinned.mainnet.PythProCompatible] -source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\contracts', rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = "target_chains/sui/contracts", rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } use_environment = "mainnet" manifest_digest = "3D2B5BBF2058F3C9DCA3527CC2486C518124687B60479CA9875E5475652B7BBC" deps = { Sui = "Sui_1", WormholeSimpleMajority = "WormholeSimpleMajority" } [pinned.mainnet.Sui] -source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\sui-framework', rev = "ae59d7718668b468ce65702ecb0440aa2330f389" } +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "b0535f1f3a3310e71790e90d8ae4e8ca840c897e" } use_environment = "mainnet" manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195" deps = { MoveStdlib = "MoveStdlib" } [pinned.mainnet.Sui_1] -source = { git = "https://github.com/MystenLabs/sui.git", subdir = 'crates\sui-framework\packages\sui-framework', rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" } use_environment = "mainnet" manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195" deps = { MoveStdlib = "MoveStdlib_1" } [pinned.mainnet.Wormhole] -source = { git = "https://github.com/wormhole-foundation/wormhole.git", subdir = 'sui\wormhole', rev = "b71be5cbb9537c4aac8e23e74371affa3825efcd" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", subdir = "sui/wormhole", rev = "b71be5cbb9537c4aac8e23e74371affa3825efcd" } use_environment = "mainnet" manifest_digest = "0D766A0380B75080707CFA6099AF469A2E502B0D9DC334A9E9983B391C5555D9" deps = { Sui = "Sui_1" } [pinned.mainnet.WormholeSimpleMajority] -source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = 'target_chains\sui\vendor\wormhole_simple_majority\wormhole', rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = "target_chains/sui/vendor/wormhole_simple_majority/wormhole", rev = "aa291266173173480efef77b9454f2dbc2b4fd62" } use_environment = "mainnet" manifest_digest = "0D766A0380B75080707CFA6099AF469A2E502B0D9DC334A9E9983B391C5555D9" deps = { Sui = "Sui_1" } [pinned.mainnet.deepbook] -source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = 'packages\deepbook', rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } +source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/deepbook", rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } use_environment = "mainnet" manifest_digest = "BBE7F8BFEF088532C5358623C6F5B3153EE14B3265ABA833B0F3CF9D313264A7" deps = { std = "MoveStdlib", sui = "Sui", token = "token" } @@ -71,7 +71,7 @@ manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5E deps = { std = "MoveStdlib", sui = "Sui" } [pinned.mainnet.token] -source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = 'packages\token', rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } +source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "8a46552dfac02f2225965dda243bdea1ddcbec89" } use_environment = "mainnet" manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6" deps = { std = "MoveStdlib", sui = "Sui" } From 67e850b635c70e40f0809e9cb482cbec9a9a7d58 Mon Sep 17 00:00:00 2001 From: avernikoz Date: Mon, 9 Nov 2026 11:48:48 +0300 Subject: [PATCH 11/11] Bump the upgrade toolchain to Sui 1.83.1. Mainnet is on protocol 141, and the 1.80.1 CLI refuses to serialize the unsigned upgrade because it only supports protocol 137. The test workflow and Published.toml toolchain stay on the same pin. --- .github/workflows/move_test.yml | 2 +- .github/workflows/upgrade_package.yml | 2 +- docs/upgrade-provenance.md | 2 +- packages/deeptrade-core/Published.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/move_test.yml b/.github/workflows/move_test.yml index eed32bd..b03af3c 100644 --- a/.github/workflows/move_test.yml +++ b/.github/workflows/move_test.yml @@ -20,7 +20,7 @@ jobs: test: runs-on: ubuntu-latest env: - SUI_VERSION: "1.80.1" # Parameterized Sui version + SUI_VERSION: "1.83.1" # Parameterized Sui version steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/upgrade_package.yml b/.github/workflows/upgrade_package.yml index 68be7a5..bc9b2b9 100644 --- a/.github/workflows/upgrade_package.yml +++ b/.github/workflows/upgrade_package.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest env: # Keep in sync with .github/workflows/move_test.yml (SUI_VERSION). - SUI_VERSION: "1.80.1" + SUI_VERSION: "1.83.1" PACKAGE_PATH: "packages/deeptrade-core" # Gas budget for the unsigned upgrade PTB (MIST). CLI auto-selects coins from sender. GAS_BUDGET: "1000000000" diff --git a/docs/upgrade-provenance.md b/docs/upgrade-provenance.md index 08a9091..31f3c1c 100644 --- a/docs/upgrade-provenance.md +++ b/docs/upgrade-provenance.md @@ -86,7 +86,7 @@ This rebuilds with `toolchain-version` from `Published.toml` and compares root p If metadata is incomplete, override explicitly: ```bash -sui client verify-source --toolchain-version 1.80.1 +sui client verify-source --toolchain-version 1.83.1 ``` ## What each check proves diff --git a/packages/deeptrade-core/Published.toml b/packages/deeptrade-core/Published.toml index a373afc..05a8182 100644 --- a/packages/deeptrade-core/Published.toml +++ b/packages/deeptrade-core/Published.toml @@ -13,5 +13,5 @@ published-at = "0xda5b27f23ac02c1f9acec07a32baf6523a381d7df9b00d99c99e3f9b41b128 original-id = "0xc10d536b6580d809711b9bb8eee3945d5e96f92a346c84d74ff7a0697e664695" version = 2 upgrade-capability = "0x331c41b3587619223c8ccf44b2aa9ad683fae7b536d6b5ed96fc94fe9a8d4278" -toolchain-version = "1.80.1" +toolchain-version = "1.83.1" build-config = { flavor = "sui", edition = "2024.alpha" }