forked from SO4-Markets/contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
406 lines (366 loc) · 14.5 KB
/
Copy pathlib.rs
File metadata and controls
406 lines (366 loc) · 14.5 KB
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#![no_std]
#![allow(dependency_on_unit_never_type_fallback)]
use soroban_sdk::{contracttype, Address, Vec};
// ─── Price ───────────────────────────────────────────────────────────────────
/// USD price with min/max spread; 30-decimal FLOAT_PRECISION. Mirrors GMX's Price.Props.
#[contracttype]
pub struct PriceProps {
pub min: i128,
pub max: i128,
}
impl PriceProps {
pub fn is_empty(&self) -> bool {
self.min == 0 || self.max == 0
}
pub fn mid_price(&self) -> i128 {
(self.max + self.min) / 2
}
pub fn pick_price(&self, maximize: bool) -> i128 {
if maximize {
self.max
} else {
self.min
}
}
/// Longs profit from higher prices, shorts from lower.
/// maximize=true → worst-case PnL for the LP / best-case for the trader.
pub fn pick_price_for_pnl(&self, is_long: bool, maximize: bool) -> i128 {
match (is_long, maximize) {
(true, true) => self.max,
(true, false) => self.min,
(false, true) => self.min,
(false, false) => self.max,
}
}
}
// ─── Market ──────────────────────────────────────────────────────────────────
/// Mirrors GMX's Market.Props.
#[contracttype]
pub struct MarketProps {
pub market_token: Address,
pub index_token: Address,
pub long_token: Address,
pub short_token: Address,
}
impl MarketProps {
/// Construct a `MarketProps` from borrowed addresses.
///
/// Issue #248: `MarketProps` carries only `Address` fields, every one of which
/// is required and has no meaningful zero value (Soroban `Address` cannot be
/// constructed without an `Env`, so a `Default` impl is not possible). The
/// per-field struct literal — repeated verbatim across handlers, libs, and
/// every test — is the actual boilerplate. This constructor collapses that
/// six-line literal into a single call and clones internally so callers keep
/// ownership of their addresses, which is the common case when the same
/// addresses are reused to build other structs in the same scope.
pub fn new(
market_token: &Address,
index_token: &Address,
long_token: &Address,
short_token: &Address,
) -> Self {
Self {
market_token: market_token.clone(),
index_token: index_token.clone(),
long_token: long_token.clone(),
short_token: short_token.clone(),
}
}
}
// ─── Position ────────────────────────────────────────────────────────────────
/// Mirrors GMX's Position.Props.
/// Field name abbreviations (30-char limit in #[contracttype]):
/// long_claim_fnd_per_size = longTokenClaimableFundingAmountPerSize
/// short_claim_fnd_per_size = shortTokenClaimableFundingAmountPerSize
#[contracttype]
pub struct PositionProps {
pub account: Address,
pub market: Address,
pub collateral_token: Address,
pub size_in_usd: i128,
pub size_in_tokens: i128,
pub collateral_amount: i128,
pub pending_impact_amount: i128,
pub borrowing_factor: i128,
pub funding_fee_amount_per_size: i128,
pub long_claim_fnd_per_size: i128,
pub short_claim_fnd_per_size: i128,
pub increased_at_time: u64,
pub decreased_at_time: u64,
pub is_long: bool,
}
// ─── Orders ──────────────────────────────────────────────────────────────────
/// Mirrors GMX's Order.OrderType.
#[contracttype]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OrderType {
MarketSwap,
LimitSwap,
MarketIncrease,
LimitIncrease,
MarketDecrease,
LimitDecrease,
StopLossDecrease,
Liquidation,
StopIncrease,
}
/// Mirrors GMX's Order.Props.
#[contracttype]
pub struct OrderProps {
pub account: Address,
pub receiver: Address,
pub market: Address,
pub initial_collateral_token: Address,
pub swap_path: Vec<Address>,
pub size_delta_usd: i128,
pub collateral_delta_amount: i128,
pub trigger_price: i128,
pub acceptable_price: i128,
pub execution_fee: i128,
pub min_output_amount: i128,
pub order_type: OrderType,
pub is_long: bool,
pub updated_at_time: u64,
}
// ─── Handler create-params (shared so router doesn't depend on handler crates) ─
/// User-supplied parameters for creating a deposit.
#[contracttype]
pub struct CreateDepositParams {
pub receiver: Address,
pub market: Address,
pub initial_long_token: Address,
pub initial_short_token: Address,
pub long_token_amount: i128,
pub short_token_amount: i128,
pub min_market_tokens: i128,
pub execution_fee: i128,
}
/// User-supplied parameters for creating a withdrawal.
#[contracttype]
pub struct CreateWithdrawalParams {
pub receiver: Address,
pub market: Address,
pub market_token_amount: i128,
pub min_long_token_amount: i128,
pub min_short_token_amount: i128,
pub execution_fee: i128,
}
/// User-supplied parameters for creating an order. Mirrors GMX BaseOrderUtils.CreateOrderParams.
#[contracttype]
#[derive(Clone)]
pub struct CreateOrderParams {
pub receiver: Address,
pub market: Address,
pub initial_collateral_token: Address,
pub swap_path: Vec<Address>,
pub size_delta_usd: i128,
pub collateral_delta_amount: i128,
pub trigger_price: i128,
pub acceptable_price: i128,
pub execution_fee: i128,
pub min_output_amount: i128,
pub order_type: OrderType,
pub is_long: bool,
/// Optional ledger-sequence deadline. If `Some(n)` the order auto-cancels
/// (with full refund) when the keeper calls `execute_order` after sequence `n`.
/// `None` means the order never expires via this mechanism.
pub expiry_ledger: Option<u64>,
}
// ─── Deposits / Withdrawals ───────────────────────────────────────────────────
/// Mirrors GMX's Deposit.Props.
#[contracttype]
pub struct DepositProps {
pub account: Address,
pub receiver: Address,
pub market: Address,
pub initial_long_token: Address,
pub initial_short_token: Address,
pub long_token_amount: i128,
pub short_token_amount: i128,
pub min_market_tokens: i128,
pub execution_fee: i128,
pub updated_at_time: u64,
}
/// Mirrors GMX's Withdrawal.Props.
#[contracttype]
pub struct WithdrawalProps {
pub account: Address,
pub receiver: Address,
pub market: Address,
pub market_token_amount: i128,
pub min_long_token_amount: i128,
pub min_short_token_amount: i128,
pub execution_fee: i128,
pub updated_at_time: u64,
}
// ─── Oracle ───────────────────────────────────────────────────────────────────
/// Used by keepers to submit prices to the oracle contract.
#[contracttype]
pub struct TokenPrice {
pub token: Address,
pub min: i128,
pub max: i128,
}
// ─── Market utils output types ────────────────────────────────────────────────
/// Full pool value breakdown returned by market_utils::get_pool_value.
#[contracttype]
pub struct PoolValueInfo {
pub pool_value: i128,
pub long_pnl: i128,
pub short_pnl: i128,
pub net_pnl: i128,
pub long_token_amount: i128,
pub short_token_amount: i128,
pub long_token_usd: i128,
pub short_token_usd: i128,
pub total_borrowing_fees: i128,
pub impact_pool_amount: i128,
}
/// Pending (not-yet-settled) funding for a single position — issue #275.
/// Positive = amount the holder would receive; negative = amount the holder
/// would pay, in each of the position's long_token / short_token units.
#[contracttype]
pub struct FundingAmountResult {
pub long_token_amount: i128,
pub short_token_amount: i128,
pub at_ledger: u64,
}
/// Aggregate funding information for a market (used by Reader).
#[contracttype]
pub struct FundingInfo {
pub funding_factor_per_second: i128,
pub long_funding_amount_per_size: i128,
pub short_funding_amount_per_size: i128,
}
/// Detailed per-hour funding rate view for frontend display (issue #207).
#[contracttype]
pub struct FundingRateInfo {
pub long_funding_rate_per_hour: i128,
pub short_funding_rate_per_hour: i128,
pub long_funding_amount_per_size: i128,
pub short_funding_amount_per_size: i128,
pub funding_updated_at_ledger: u64,
pub long_open_interest_usd: u128,
pub short_open_interest_usd: u128,
}
/// Position fee breakdown.
#[contracttype]
pub struct PositionFees {
pub borrowing_fee_amount: i128,
pub funding_fee_amount: i128,
pub position_fee_amount: i128,
pub total_cost_amount: i128,
}
/// Result of executing a position decrease (partial or full close).
#[contracttype]
pub struct DecreasePositionResult {
pub execution_price: i128, // FLOAT_PRECISION per whole token
pub pnl_usd: i128, // realised PnL (positive = profit, negative = loss)
pub output_amount: i128, // collateral token amount sent to receiver
pub secondary_output_amount: i128, // optional second token (e.g. from swap-on-close)
pub remaining_collateral: i128, // collateral left in position after fees & pnl
pub is_fully_closed: bool,
}
/// Aggregated protocol-wide statistics across a set of markets (returned by Reader).
///
/// Issue #251: lets the frontend fetch headline numbers (TVL, OI, accumulated
/// fees) in a single call instead of N per-market round-trips. All USD figures
/// use 30-decimal FLOAT_PRECISION and reflect the oracle prices at the ledger
/// in which the call executes — the result is a snapshot, valid only for
/// `computed_at_ledger`.
#[contracttype]
pub struct ProtocolStats {
pub total_pool_value_usd: i128, // sum of get_pool_value across all markets
pub total_long_open_interest_usd: i128,
pub total_short_open_interest_usd: i128,
pub total_accumulated_fees_usd: i128, // sum of unclaimed fee balances, in USD
pub market_count: u32, // number of markets aggregated
pub computed_at_ledger: u64, // ledger sequence the snapshot was taken at
}
/// Liveness status for a keeper role (issue #249), returned by Reader.
///
/// A keeper that has gone permanently offline leaves orders unexecuted. This
/// gives the admin an on-chain signal: when `is_stale` is true the gap since the
/// last successful execution has exceeded the configured heartbeat timeout, and
/// the keeper's role can be revoked.
#[contracttype]
pub struct KeeperHeartbeatStatus {
pub last_active_ledger: u64,
pub ledgers_since_last_activity: u64,
pub is_stale: bool, // gap > keeper_heartbeat_timeout(role)
}
/// Rich position info including computed PnL and fees (returned by Reader).
#[contracttype]
pub struct PositionInfo {
pub position: PositionProps,
pub pnl_usd: i128,
pub uncapped_pnl_usd: i128,
pub borrowing_fee_usd: i128,
pub funding_fee_usd: i128,
pub position_fee_usd: i128,
pub liquidation_price: i128,
/// Issue #260: weighted average entry price across all position increases.
/// Computed as size_in_usd / size_in_tokens × TOKEN_PRECISION (FLOAT_PRECISION units).
/// Zero when size_in_tokens is zero (no open position).
pub avg_entry_price: i128,
}
/// ADL-eligible position candidate for auto-deleveraging.
/// Returned by reader::get_adl_eligible_positions.
#[contracttype]
#[derive(Clone)]
pub struct AdlCandidate {
pub key: BytesN<32>, // position key for order_handler::get_position
pub owner: Address, // position owner
pub size_usd: u128, // position size in USD (absolute value)
pub unrealised_pnl_usd: u128, // positive PnL only (loss positions are filtered)
pub pnl_to_size_ratio_bps: u32, // unrealised_pnl / size in basis points (sort key)
}
/// Estimated swap output for dry-run queries.
/// Returned by reader::estimate_swap_output.
#[contracttype]
pub struct SwapEstimate {
pub token_out: Address, // output token after full swap path
pub amount_out: u128, // estimated output amount
pub price_impact_usd: i128, // cumulative impact (negative = cost, positive = rebate)
pub execution_price: u128, // effective rate for the full swap path
pub reverts_if_executed: bool, // true if any market is paused or insufficient liquidity
}
/// Position leverage breakdown returned by reader::get_position_leverage.
/// leverage_bps = size_usd * 100 / net_collateral_usd (e.g. 2000 = 20×).
/// If net_collateral_usd == 0, effective_leverage_bps == u32::MAX.
#[contracttype]
pub struct PositionLeverage {
pub effective_leverage_bps: u32,
pub net_collateral_usd: u128,
pub position_size_usd: u128,
pub is_liquidatable: bool,
}
/// Lightweight pending-order summary returned by reader::get_pending_orders (issue #202).
///
/// Carries only the fields a keeper bot needs to decide execution order:
/// owner, type, size, fee, last-update time, and direction.
/// Full details can always be fetched via reader::get_order if needed.
#[contracttype]
pub struct PendingOrder {
pub owner: Address,
pub market: Address,
pub order_type: OrderType,
pub size_delta_usd: i128,
pub execution_fee: i128,
pub updated_at_time: u64,
pub is_long: bool,
}
/// Liquidatable position entry returned by `reader::get_liquidatable_positions` (issue #283).
///
/// `health_factor_bps < 10000` means the position is below the min-collateral threshold
/// (i.e., currently eligible for liquidation). Sorted ascending so the most
/// under-collateralised positions appear first.
#[contracttype]
pub struct LiquidatablePosition {
pub key: BytesN<32>, // canonical position key for order_handler::get_position
pub owner: Address,
pub size_usd: u128,
pub collateral_usd: u128,
pub health_factor_bps: u32, // collateral_usd * 10000 / size_usd; < 10000 = liquidatable
}
use soroban_sdk::BytesN;