Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions packages/predict/Move.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@
version = 4

[pinned.mainnet.MoveStdlib]
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "73dd2c2ba6f9fdb21d7ffde2b50a3f2f0ac39bc1" }
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "367fd808279bed26f7c64fc63160062a2ee29ab7" }
use_environment = "mainnet"
manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363"
deps = {}

[pinned.mainnet.Sui]
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "73dd2c2ba6f9fdb21d7ffde2b50a3f2f0ac39bc1" }
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "367fd808279bed26f7c64fc63160062a2ee29ab7" }
use_environment = "mainnet"
manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195"
deps = { MoveStdlib = "MoveStdlib" }

[pinned.mainnet.block_scholes_oracle]
source = { local = "../block_scholes_oracle" }
use_environment = "mainnet"
manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6"
deps = { std = "MoveStdlib", sui = "Sui" }

[pinned.mainnet.deepbook]
source = { local = "../deepbook" }
use_environment = "mainnet"
Expand All @@ -25,23 +31,35 @@ deps = { std = "MoveStdlib", sui = "Sui", token = "token" }
[pinned.mainnet.deepbook_predict]
source = { root = true }
use_environment = "mainnet"
manifest_digest = "AD4D86775C165BDA3A3AB3B85CF056D155B53B6141BBEA1CE253567D05E6E3C5"
deps = { deepbook = "deepbook", dusdc = "dusdc", pyth_lazer = "pyth_lazer", std = "MoveStdlib", sui = "Sui", token = "token" }
manifest_digest = "8953286E5B8A7E6DFAB7A21CA3B92825926EB63E91CE2C00FBB7B27485C1B4ED"
deps = { block_scholes_oracle = "block_scholes_oracle", deepbook = "deepbook", dusdc = "dusdc", fixed_math = "fixed_math", propbook = "propbook", pyth_lazer = "pyth_lazer", std = "MoveStdlib", sui = "Sui", token = "token" }

[pinned.mainnet.dusdc]
source = { local = "../dusdc" }
use_environment = "mainnet"
manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6"
deps = { std = "MoveStdlib", sui = "Sui" }

[pinned.mainnet.fixed_math]
source = { local = "../fixed_math" }
use_environment = "mainnet"
manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6"
deps = { std = "MoveStdlib", sui = "Sui" }

[pinned.mainnet.propbook]
source = { local = "../propbook" }
use_environment = "mainnet"
manifest_digest = "534E62FC51C4D17C0F3E18F810632C473FC966D6998F781494674293CCABA367"
deps = { block_scholes_oracle = "block_scholes_oracle", fixed_math = "fixed_math", pyth_lazer = "pyth_lazer", std = "MoveStdlib", sui = "Sui" }

[pinned.mainnet.pyth_lazer]
source = { git = "https://github.com/pyth-network/pyth-crosschain.git", subdir = "lazer/contracts/sui", rev = "f80ff8b1aa2aa9ca17530a9b6294d254f938a5bf" }
use_environment = "mainnet"
manifest_digest = "6DC4B589535C23A9330A984EA7BC41000D18E222DF48EC556493CD219989C241"
deps = { std = "MoveStdlib", sui = "Sui", wormhole = "wormhole" }

[pinned.mainnet.token]
source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "834493b2f3a93ffb0b86fba29414ac6707116744" }
source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "5411ef3aa93f7722409b2a85047baa3d4d830c07" }
use_environment = "mainnet"
manifest_digest = "E41BBD67BE8940D26C79D78B028477EF5B33BA217A1282C78ACB344CF8A5ECF6"
deps = { std = "MoveStdlib", sui = "Sui" }
Expand Down
2 changes: 2 additions & 0 deletions packages/predict/simulations/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const BS_UNDERLYING_ID = PYTH_FEED_ID;
const TICK_BITS = 24n;
const POS_INF_TICK = (1n << TICK_BITS) - 1n;
const ORACLE_TICK_SIZE = 1_000_000_000n;
const U64_MAX = (1n << 64n) - 1n;
// Genesis minimum-liquidity lock (constants::min_bootstrap_liquidity). `lock_capital`
// permanently locks this much DUSDC so `total_supply > 0` for the life of the pool,
// making the supply==0 re-bootstrap branch unreachable. request_supply/withdraw abort
Expand Down Expand Up @@ -421,6 +422,7 @@ function addMint(tx: Transaction, params: MintParams): void {
tx.pure.u64(higherTick),
tx.pure.u64(params.quantity),
tx.pure.u64(params.leverage),
tx.pure.u64(U64_MAX),
tx.object(CLOCK_ID),
],
});
Expand Down
14 changes: 13 additions & 1 deletion packages/predict/sources/expiry_market.move
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const EMintPaused: u64 = 4;
const EFullCloseRequired: u64 = 5;
const EProofRequiredForLiveRedeem: u64 = 6;
const EWrongPythFeed: u64 = 7;
const EMintCostAboveMax: u64 = 8;

/// Per-expiry market state.
public struct ExpiryMarket has key {
Expand Down Expand Up @@ -190,7 +191,9 @@ public fun allowed_versions(market: &ExpiryMarket): VecSet<u64> {
/// required even for owner-initiated mints. The position's strike range is the
/// tick pair `(lower_tick, higher_tick]` (`lower_tick = 0` is `-inf`,
/// `higher_tick = pos_inf_tick` is `+inf`); the SDK converts raw strikes to ticks.
/// Returns the minted order ID for future order-scoped flows.
/// `max_cost` caps the all-in DUSDC withdrawal; callers that accept any cost can
/// pass `std::u64::max_value!()`. Returns the minted order ID for future
/// order-scoped flows.
public fun mint(
market: &mut ExpiryMarket,
manager: &mut PredictManager,
Expand All @@ -204,6 +207,7 @@ public fun mint(
higher_tick: u64,
quantity: u64,
leverage: u64,
max_cost: u64,
clock: &Clock,
ctx: &mut TxContext,
): u256 {
Expand All @@ -219,6 +223,7 @@ public fun mint(
higher_tick,
quantity,
leverage,
max_cost,
clock,
ctx,
)
Expand Down Expand Up @@ -670,6 +675,7 @@ public(package) fun mint_internal(
higher_tick: u64,
quantity: u64,
leverage: u64,
max_cost: u64,
clock: &Clock,
ctx: &mut TxContext,
): u256 {
Expand Down Expand Up @@ -702,6 +708,12 @@ public(package) fun mint_internal(
.stake_config()
.fee_amount_after_discount(raw_fee_amount, manager.active_stake());
let penalty_amount = market.ewma_penalty(config.ewma_config(), quantity, clock, ctx);
let builder_code_id = manager.builder_code_id();
let quoted_builder_fee_amount = builder_fee_amount(&builder_code_id, fee_amount, quantity);
assert!(
net_premium + fee_amount + quoted_builder_fee_amount + penalty_amount <= max_cost,
EMintCostAboveMax,
);

let builder_fee_amount = market.settle_mint_payment(
manager,
Expand Down
80 changes: 80 additions & 0 deletions packages/predict/tests/flows/mint_slippage_flow_tests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// Slippage guards for mint flows.
#[test_only]
module deepbook_predict::mint_slippage_flow_tests;

use deepbook_predict::{constants, expiry_market, flow_test_helpers as helpers, test_constants};
use std::unit_test::destroy;

/// ATM 1x UP over the default short-expiry fixture:
/// net_premium = floor(0.5 * 1_000_000_000) = 500_000_000
/// fee = min_fee * quantity = 5_000_000
/// no builder fee and EWMA penalty disabled, so all-in cost is 505_000_000.
const ONE_X_MINT_COST: u64 = 505_000_000;
const POST_FULL_MINT_BALANCE: u64 = 495_000_000;

#[test]
fun mint_accepts_exact_max_cost() {
let (mut fx, expiry_id, mut manager) = helpers::setup_live_market(
test_constants::short_expiry_ms(),
test_constants::default_live_price(),
);
fx.scenario_mut().next_tx(test_constants::alice());
let (pyth, bs, oracle_registry, vault, mut market, config) = fx.take_market(expiry_id);

let order_id = fx.mint_capped(
&config,
&oracle_registry,
&mut manager,
&mut market,
&pyth,
&bs,
helpers::strike_tick(),
constants::pos_inf_tick!(),
test_constants::mint_quantity(),
test_constants::leverage_one_x(),
ONE_X_MINT_COST,
);

assert!(manager.has_position(expiry_id, order_id));
helpers::check_manager(
&manager,
expiry_id,
helpers::expected_manager_state(POST_FULL_MINT_BALANCE, 5_000_000, 1, 0, 0),
);

helpers::return_market(pyth, bs, oracle_registry, vault, market, config);
destroy(manager);
fx.finish();
}

#[test, expected_failure(abort_code = expiry_market::EMintCostAboveMax)]
fun mint_aborts_above_max_cost() {
let (mut fx, expiry_id, mut manager) = helpers::setup_live_market(
test_constants::short_expiry_ms(),
test_constants::default_live_price(),
);
fx.scenario_mut().next_tx(test_constants::alice());
let (pyth, bs, oracle_registry, vault, mut market, config) = fx.take_market(expiry_id);

fx.mint_capped(
&config,
&oracle_registry,
&mut manager,
&mut market,
&pyth,
&bs,
helpers::strike_tick(),
constants::pos_inf_tick!(),
test_constants::mint_quantity(),
test_constants::leverage_one_x(),
ONE_X_MINT_COST - 1,
);

helpers::return_market(pyth, bs, oracle_registry, vault, market, config);
destroy(manager);
fx.finish();
abort 999
}
35 changes: 35 additions & 0 deletions packages/predict/tests/helper/flow_test_helpers.move
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,41 @@ public fun mint(
higher_tick,
quantity,
leverage,
std::u64::max_value!(),
&self.clock,
self.scenario.ctx(),
);
order_id
}

/// Mint one fixed-quantity order with an all-in cost cap.
public fun mint_capped(
self: &mut Fixture,
config: &ProtocolConfig,
oracle_registry: &OracleRegistry,
manager: &mut PredictManager,
market: &mut ExpiryMarket,
pyth: &PythFeed,
bs: &BlockScholesFeed,
lower_tick: u64,
higher_tick: u64,
quantity: u64,
leverage: u64,
max_cost: u64,
): u256 {
let proof = manager.generate_proof_as_owner(self.scenario.ctx());
let order_id = market.mint_internal(
manager,
&proof,
config,
oracle_registry,
pyth,
bs,
lower_tick,
higher_tick,
quantity,
leverage,
max_cost,
&self.clock,
self.scenario.ctx(),
);
Expand Down
Loading