Skip to main content

ddk_payouts/
options.rs

1use crate::options_builder::OptionBuilder;
2use bitcoin::Amount;
3use ddk_manager::{
4    contract::{
5        contract_input::{ContractInput, ContractInputInfo, OracleInput},
6        numerical_descriptor::NumericalDescriptor,
7        ContractDescriptor,
8    },
9    payout_curve::{PayoutFunction, RoundingInterval, RoundingIntervals},
10};
11use ddk_messages::oracle_msgs::OracleAnnouncement;
12use ddk_trie::OracleNumericInfo;
13
14// Helper enums
15#[derive(Copy, Clone)]
16pub enum OptionType {
17    Call,
18    Put,
19}
20
21#[derive(Copy, Clone)]
22pub enum Direction {
23    Long,
24    Short,
25}
26
27fn build_order_offer(
28    announcement: &OracleAnnouncement,
29    total_collateral: Amount,
30    offer_collateral: Amount,
31    payout_function: PayoutFunction,
32    rounding_intervals: RoundingIntervals,
33    fee_rate: u64,
34) -> ContractInput {
35    let contract_descriptor = ContractDescriptor::Numerical(NumericalDescriptor {
36        payout_function,
37        rounding_intervals,
38        difference_params: None,
39        oracle_numeric_infos: OracleNumericInfo {
40            nb_digits: vec![20],
41            base: 2,
42        },
43    });
44
45    let oracles = OracleInput {
46        public_keys: vec![announcement.oracle_public_key],
47        event_id: announcement.oracle_event.event_id.clone(),
48        threshold: 1,
49    };
50
51    let contract_info = ContractInputInfo {
52        oracles,
53        contract_descriptor,
54    };
55
56    ContractInput {
57        contract_infos: vec![contract_info],
58        offer_collateral,
59        accept_collateral: total_collateral - offer_collateral,
60        fee_rate,
61        contract_flags: 0,
62    }
63}
64
65#[allow(clippy::too_many_arguments)]
66// Main option builder function
67pub fn build_option_order_offer(
68    announcement: &OracleAnnouncement,
69    contract_size: Amount,
70    strike_price: u64,
71    premium: Amount,
72    fee_per_byte: u64,
73    rounding: u64,
74    option_type: OptionType,
75    direction: Direction,
76    total_collateral: Amount,
77    nb_oracle_digits: u32,
78) -> anyhow::Result<ContractInput> {
79    let payout_function = OptionBuilder::build_option_payout(
80        direction,
81        option_type,
82        strike_price,
83        contract_size,
84        total_collateral,
85        // Oracle Base
86        2,
87        // Oracle digits
88        nb_oracle_digits,
89    )?;
90
91    let rounding_mod = compute_rounding_modulus(rounding, contract_size);
92    let rounding_intervals =
93        create_rounding_intervals(strike_price, rounding_mod, option_type, direction);
94
95    let offer_collateral = match direction {
96        Direction::Short => total_collateral - premium,
97        Direction::Long => premium,
98    };
99
100    Ok(build_order_offer(
101        announcement,
102        total_collateral,
103        offer_collateral,
104        payout_function,
105        rounding_intervals,
106        fee_per_byte,
107    ))
108}
109
110// Helper function to create rounding intervals
111fn create_rounding_intervals(
112    strike_price: u64,
113    rounding_mod: u64,
114    option_type: OptionType,
115    direction: Direction,
116) -> RoundingIntervals {
117    let intervals = match (option_type, direction) {
118        (OptionType::Call, Direction::Short) | (OptionType::Call, Direction::Long) => vec![
119            RoundingInterval {
120                begin_interval: 0,
121                rounding_mod: 1,
122            },
123            RoundingInterval {
124                begin_interval: strike_price,
125                rounding_mod,
126            },
127        ],
128        (OptionType::Put, Direction::Short) | (OptionType::Put, Direction::Long) => vec![
129            RoundingInterval {
130                begin_interval: 0,
131                rounding_mod,
132            },
133            RoundingInterval {
134                begin_interval: strike_price,
135                rounding_mod: 1,
136            },
137        ],
138    };
139
140    RoundingIntervals { intervals }
141}
142
143fn compute_rounding_modulus(rounding: u64, total_collateral: Amount) -> u64 {
144    (rounding * total_collateral.to_sat()) / 100_000_000
145}
146
147// Helper function to create rounding intervals
148pub fn create_covered_call_rounding_intervals(
149    strike_price: u64,
150    rounding_mod: u64,
151) -> RoundingIntervals {
152    RoundingIntervals {
153        intervals: vec![
154            // No rounding below strike price
155            RoundingInterval {
156                begin_interval: 0,
157                rounding_mod: 1,
158            },
159            // Apply rounding above strike price
160            RoundingInterval {
161                begin_interval: strike_price,
162                rounding_mod,
163            },
164        ],
165    }
166}