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#[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)]
66pub 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 2,
87 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
110fn 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
147pub fn create_covered_call_rounding_intervals(
149 strike_price: u64,
150 rounding_mod: u64,
151) -> RoundingIntervals {
152 RoundingIntervals {
153 intervals: vec![
154 RoundingInterval {
156 begin_interval: 0,
157 rounding_mod: 1,
158 },
159 RoundingInterval {
161 begin_interval: strike_price,
162 rounding_mod,
163 },
164 ],
165 }
166}