-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.rs
174 lines (148 loc) · 4.63 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use base64;
use chrono::{TimeZone, LocalResult};
use ethers::{
abi::AbiDecode,
contract::{EthAbiCodec, EthAbiType},
prelude::{abigen, SignerMiddleware},
types::{Address, U256},
};
use futures::TryFutureExt;
use serde::Deserialize;
use std::collections::HashMap;
use switchboard_evm::{sb_error, sb_function, FnCall, sdk::EVMFunctionRunner};
use switchboard_utils::reqwest;
use rust_decimal::prelude::FromPrimitive;
use rust_decimal::Decimal;
abigen!(
Receiver,
r#"[ function callback(address, uint256, uint256[]) ]"#,
);
static CLIENT_URL: &str = "https://goerli-rollup.arbitrum.io/rpc";
static RECEIVER: &str = env!("CALLBACK_ADDRESS");
#[derive(Debug, Deserialize)]
struct Response {
result: DeribitResult,
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct DeribitResult {
underlying_price: f64,
underlying_index: String,
timestamp: u64,
// stats: Stats,
state: String,
open_interest: f64,
min_price: f64,
max_price: f64,
mark_price: f64,
mark_iv: f64,
last_price: f64,
interest_rate: u32,
instrument_name: String,
index_price: f64,
// greeks: Greeks,
estimated_delivery_price: String,
delivery_price: f64,
change_id: u64,
bids: Vec<HashMap<String, f64>>,
bid_iv: f64,
best_bid_price: f64,
best_bid_amount: f64,
best_ask_price: f64,
best_ask_amount: f64,
asks: Vec<HashMap<String, f64>>,
ask_iv: f64,
}
#[derive(EthAbiType, EthAbiCodec, Default, Debug, Clone)]
pub struct Order {
market_id: String, // asset name
exp_date: U256, // expiration date
strike_price: U256, // strike price in integers (this ultimately should depend on the asset / exchange)
option_type: u8, // call or put
}
// -- Business logic - Called once for each Switchboard Function Request (many can be resolved in one run) --
#[sb_function(expiration_seconds = 120, gas_limit = 5_500_000)]
async fn sb_function<M: Middleware, S: Signer>(
client: SignerMiddleware<M, S>,
request_id: Address,
params: Order,
) -> Result<Vec<FnCall<M, S>>, Error> {
// --- Initialize clients ---
let receiver: Address = RECEIVER.parse().map_err(|_| Error::ParseError)?;
let receiver_contract = Receiver::new(receiver, client.into());
// --- Get URL from params ---
// get expiration date in format DDMMYY
let timestamp_u64 = params.exp_date.low_u64();
// Create a DateTime object from the timestamp using timestamp_opt()
let exp_date = match Utc.timestamp_opt(timestamp_u64 as i64, 0) {
LocalResult::Single(datetime) => {
// Format the DateTime object as DDMMMYY and return
Ok(datetime.format("%d%b%y").to_string().to_uppercase())
}
_ => Err(Error::InvalidParameter), // Return error if the timestamp is ambiguous or not valid
}?;
// get strike price in usd (8 decimals)
let strike_price = params.strike_price.to_string();
// get option type
let option_type = match params.option_type {
0 => "C",
1 => "P",
_ => return Err(Error::InvalidParameter),
};
let url = format!(
"https://www.deribit.com/api/v2/public/get_order_book?instrument_name={}-{}-{}-{}",
params.market_id,
exp_date,
strike_price,
option_type
);
println!("Constructed url: {}", url);
let derebit_response: Response = reqwest::get(url)
.and_then(|r| r.json())
.await
.map_err(|_| Error::FetchError)?;
// --- Create Callback(s) ---
let mut mark_iv = Decimal::from_f64(derebit_response.result.mark_iv).unwrap();
mark_iv.rescale(8);
let mut callbacks = Vec::new();
let x = U256::from(mark_iv.mantissa());
for i in 0..3 {
// --- Send the callback to the contract with Switchboard verification ---
let y = U256::from(i);
let callback = receiver_contract.callback(
request_id,
y,
vec![
x + y + 1,
x + y + 2,
x + y + 3,
],
);
callbacks.push(callback);
}
// get callback back to the end-user
Ok(callbacks)
}
#[sb_error]
enum Error {
FetchError,
InvalidParameter,
}
/// Run `cargo test -- --nocapture`
#[cfg(test)]
mod tests {
use crate::*;
use std::str::FromStr;
#[tokio::test]
async fn test() {
let derebit_response: DeribitResponse = reqwest::get(
"https://www.deribit.com/api/v2/public/get_order_book?instrument_name=ETH-29SEP23-2000-C",
)
.await
.unwrap()
.json()
.await
.unwrap();
println!("{:#?}", derebit_response);
}
}