/
core-ststx-earn-v1.clar
707 lines (649 loc) · 30 KB
/
core-ststx-earn-v1.clar
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
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
;; @contract Core
;; @version 1
;;-------------------------------------
;; Traits
;;-------------------------------------
(use-trait pnl-calculator-trait .pnl-calculator-trait.pnl-calculator-trait)
(use-trait pyth-storage-trait 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-traits-v1.storage-trait)
(use-trait pyth-decoder-trait 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-traits-v1.decoder-trait)
(use-trait wormhole-core-trait 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.wormhole-traits-v1.core-trait)
;;-------------------------------------
;; Errors
;;-------------------------------------
(define-constant ERR_NO_ENTRY_FOR_ID (err u2001))
(define-constant ERR_NO_ENTRY_FOR_COUNTERPARTY (err u2002))
(define-constant ERR_SETTLEMENT_RATE_NONE (err u2003))
(define-constant ERR_PNL_USD_SET_NONE (err u2004))
(define-constant ERR_NOT_OPTION_ASSET (err u2005))
(define-constant ERR_EPOCH_NOT_EXPIRED (err u2006))
(define-constant ERR_SETTLEMENT_WINDOW_CLOSED (err u2007))
(define-constant ERR_PAYMENT_OUTSTANDING (err u2008))
(define-constant ERR_EPOCH_ALREADY_SETTLED (err u2009))
(define-constant ERR_TRADING_NOT_ALLOWED (err u2010))
(define-constant ERR_EXPIRY_BEFORE_CURRENT_EXPIRY (err u2011))
(define-constant ERR_EPOCH_ALREADY_STARTED_FUNDS_REGISTERED (err u2012))
(define-constant ERR_EPOCH_ALREADY_STARTED_UNITS_TRANSACTED (err u2013))
(define-constant ERR_CURRENT_EPOCH_NOT_SETTLED (err u2014))
(define-constant ERR_VIOLATES_MIN_EPOCH_DURATION (err u2015))
(define-constant ERR_VIOLATES_MAX_EPOCH_DURATION (err u2016))
(define-constant ERR_EPOCH_ZERO_TRADING_ATTEMPT (err u2017))
(define-constant ERR_NOT_STANDARD (err u2018))
(define-constant ERR_NOT_ENOUGH_FUNDS_AVAILABLE (err u2019))
(define-constant ERR_CURRENT_EPOCH_SETTLED (err u2020))
(define-constant ERR_REGISTRATION_WINDOW_CLOSED (err u2021))
(define-constant ERR_NO_REGISTRATION_FOR_COUNTERPARTY (err u2022))
(define-constant ERR_AMOUNT_MISMATCH (err u2023))
(define-constant ERR_PRICE_MISMATCH (err u2024))
(define-constant ERR_CONFIRMATION_WINDOW_CLOSED (err u2025))
(define-constant ERR_PNL_UNDETERMINED (err u2026))
(define-constant ERR_PAYMENT_WINDOW_CLOSED (err u2027))
(define-constant ERR_PAYMENT_AMOUNTS_DONT_MATCH (err u2028))
(define-constant ERR_ALREADY_INITIALIZED (err u2029))
(define-constant ERR_PAYMENT_WINDOW_STILL_OPEN (err u2030))
(define-constant ERR_PNL_ALREADY_DETERMINED (err u2031))
(define-constant ERR_PNL_CALCULATION_WINDOW_STILL_OPEN (err u2032))
;;-------------------------------------
;; Constants
;;-------------------------------------
(define-constant underlying-base (pow u10 u6))
(define-constant bps-base u10000) ;; 1 bps = 0.01%
(define-constant minute-in-ms u60000)
;;-------------------------------------
;; Variables
;;-------------------------------------
(define-data-var is-initialized bool false)
(define-data-var current-epoch-id uint u0)
(define-data-var trading-funds-available uint u0)
(define-data-var trading-funds-registered uint u0)
(define-data-var total-funds-requested uint u0)
(define-data-var counterparty-addresses (list 5000 principal) (list))
(define-data-var delinquent-counterparty-addresses (list 5000 principal) (list))
(define-data-var delinquent-epoch bool false)
(define-data-var helper-principal principal tx-sender)
;;-------------------------------------
;; Maps
;;-------------------------------------
(define-map epoch-info
{
epoch-id: uint
}
{
epoch-expiry: uint, ;; unix timestamp in ms
strike-call: (optional uint), ;; usd-per-option-asset
strike-put: (optional uint), ;; usd-per-option-asset
barrier-up: (optional uint), ;; usd-per-option-asset
barrier-down: (optional uint), ;; usd-per-option-asset
unit-pnl: (optional uint), ;; in underlying
settlement-rate: (optional uint), ;; usd-per-option-asset
block-height-start: uint,
block-height-pnl: (optional uint),
block-height-settled: (optional uint),
total-units-transacted: uint,
total-premium: uint, ;; in underlying
total-underlying-active: uint,
underlying-per-token-settled: (optional uint),
epoch-risk: uint, ;; in bps
unit-size: uint
}
)
(define-map counterparty-info
{
address: principal
}
{
units-registered: uint,
price-registered: (optional uint),
block-height-registered: (optional uint),
units-transacted: uint,
funds-requested: (optional uint),
}
)
(define-map delinquent-counterparty-info
{
address: principal
}
{
funds-requested: uint,
units-transacted: uint,
epoch-id: uint,
}
)
;;-------------------------------------
;; Getters
;;-------------------------------------
(define-read-only (get-current-epoch-id)
(var-get current-epoch-id))
(define-read-only (get-trading-funds-available)
(var-get trading-funds-available))
(define-read-only (get-trading-funds-registered)
(var-get trading-funds-registered))
(define-read-only (get-total-funds-requested)
(var-get total-funds-requested))
(define-read-only (get-epoch-info (epoch-id uint))
(ok (unwrap! (map-get? epoch-info { epoch-id: epoch-id }) ERR_NO_ENTRY_FOR_ID)))
(define-read-only (get-current-epoch-info)
(map-get? epoch-info { epoch-id: (get-current-epoch-id) }))
(define-read-only (get-counterparty-info (counterparty principal))
(ok (unwrap! (map-get? counterparty-info { address: counterparty }) ERR_NO_ENTRY_FOR_COUNTERPARTY)))
(define-read-only (get-counterparty-addresses)
(var-get counterparty-addresses))
(define-read-only (get-delinquent-counterparty-info (counterparty principal))
(ok (unwrap! (map-get? delinquent-counterparty-info { address: counterparty }) ERR_NO_ENTRY_FOR_COUNTERPARTY)))
(define-read-only (get-delinquent-counterparty-addresses)
(var-get delinquent-counterparty-addresses))
;;-------------------------------------
;; Settlement
;;-------------------------------------
(define-private (settle)
(begin
(try! (charge-fees))
(try! (settle-epoch-info))
(try! (activate-pending-claims))
(try! (as-contract (contract-call? .hq-ststx-earn-v1 update-fees-and-settings)))
(unwrap-panic (reset-data))
(ok true)))
(define-private (charge-fees)
(let (
(current-performance-fee (get current (try! (contract-call? .hq-ststx-earn-v1 get-fees "performance"))))
(current-management-fee (get current (try! (contract-call? .hq-ststx-earn-v1 get-fees "management"))))
(fee-address (contract-call? .hq-ststx-earn-v1 get-fee-address))
(no-profit-management-fee (contract-call? .hq-ststx-earn-v1 get-no-profit-management-fee))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(unit-pnl (unwrap-panic (get unit-pnl current-epoch-info)))
(total-premium (get total-premium current-epoch-info))
(units-transacted (get total-units-transacted current-epoch-info))
(total-underlying (get total-underlying-active current-epoch-info))
(strike-call (get strike-call current-epoch-info))
(strike-put (get strike-put current-epoch-info))
(strike-set (or (is-some strike-call) (is-some strike-put))))
(if (and strike-set (not (var-get delinquent-epoch)))
(if (> (* unit-pnl units-transacted) total-premium)
(begin
(try!
(as-contract (contract-call? .vault-ststx-earn-v1 payout-funds
(/ (* (- (* unit-pnl units-transacted) total-premium) current-performance-fee) bps-base) fee-address))
)
(try!
(as-contract (contract-call? .vault-ststx-earn-v1 payout-funds
(/ (/ (* total-underlying current-management-fee) bps-base) u100) fee-address))
)
)
(if no-profit-management-fee
(try!
(as-contract (contract-call? .vault-ststx-earn-v1 payout-funds
(/ (/ (* total-underlying current-management-fee) bps-base) u100) fee-address))
)
true
)
)
true
)
(ok true)))
(define-private (settle-epoch-info)
(let (
(underlying-per-token (contract-call? .vault-ststx-earn-v1 get-underlying-per-token))
(current-epoch-info (unwrap-panic (get-current-epoch-info))))
(map-set epoch-info { epoch-id: (get-current-epoch-id) }
(merge
current-epoch-info
{
block-height-settled: (some burn-block-height),
underlying-per-token-settled: (some underlying-per-token)
}
)
)
(as-contract (contract-call? .vault-ststx-earn-v1 update-epoch-info-for-claims))))
(define-private (activate-pending-claims)
(begin
(try! (as-contract (contract-call? .vault-ststx-earn-v1 activate-pending-deposit-claims)))
(try! (as-contract (contract-call? .vault-ststx-earn-v1 activate-pending-withdrawal-claims)))
(ok true)))
(define-private (init-trading-funds-available)
(begin
(var-set trading-funds-available
(/
(*
(contract-call? .vault-ststx-earn-v1 get-total-underlying-active)
(contract-call? .hq-ststx-earn-v1 get-epoch-risk))
bps-base))
(ok true)))
(define-private (reset-data)
(begin
(map delete-counterparty (var-get counterparty-addresses))
(var-set counterparty-addresses (list))
(var-set total-funds-requested u0)
(var-set trading-funds-registered u0)
(ok (var-set delinquent-epoch false))))
(define-private (payment-requester
(counterparty-address principal))
(let (
(counterparty-info-entry (unwrap-panic (get-counterparty-info counterparty-address)))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(units-transacted (get units-transacted counterparty-info-entry))
(unit-pnl (unwrap-panic (get unit-pnl current-epoch-info)))
(funds-requested (* units-transacted unit-pnl)))
(map-set counterparty-info { address: counterparty-address }
(merge counterparty-info-entry
{
funds-requested: (some funds-requested)
}
)
)
(ok (var-set total-funds-requested (+ (var-get total-funds-requested) funds-requested)))))
(define-public (determine-pnl
(pnl-calculator-contract <pnl-calculator-trait>)
(use-pnl-calculator bool)
(pnl-usd-set (optional uint))
(use-oracle-price bool)
(settlement-rate (optional uint))
(option-asset-per-underlying (optional uint))
(price-feed-bytes (buff 8192))
(execution-plan {
pyth-storage-contract: <pyth-storage-trait>,
pyth-decoder-contract: <pyth-decoder-trait>,
wormhole-core-contract: <wormhole-core-trait>
}))
(let (
(decoded-prices (try! (contract-call? 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-oracle-v2 decode-price-feeds price-feed-bytes execution-plan)))
(decoded-price (element-at decoded-prices u0))
(timestamp (* (unwrap-panic (get publish-time decoded-price)) u1000))
(usd-per-option-asset (if use-oracle-price (to-uint (unwrap-panic (get ema-price decoded-price))) (unwrap! settlement-rate ERR_SETTLEMENT_RATE_NONE)))
(price-id (unwrap-panic (get price-identifier decoded-price)))
(epoch-id (get-current-epoch-id))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(current-epoch-expiry (get epoch-expiry current-epoch-info))
(strike-call (get strike-call current-epoch-info))
(strike-put (get strike-put current-epoch-info))
(barrier-up (get barrier-up current-epoch-info))
(barrier-down (get barrier-down current-epoch-info))
(option-type (contract-call? .hq-ststx-earn-v1 get-option-type))
(strategy-type (contract-call? .hq-ststx-earn-v1 get-strategy-type))
(unit-size (contract-call? .hq-ststx-earn-v1 get-unit-size))
(pnl-usd (if use-pnl-calculator
(try! (contract-call? pnl-calculator-contract calculate-pnl usd-per-option-asset option-type strategy-type strike-call strike-put barrier-up barrier-down))
(unwrap! pnl-usd-set ERR_PNL_USD_SET_NONE)
))
(pnl-option-asset (usd-to-option-asset pnl-usd usd-per-option-asset))
(unit-pnl (/ (* (/ (* pnl-option-asset unit-size) underlying-base) underlying-base)
(match option-asset-per-underlying value value underlying-base)))
(total-units-transacted (get total-units-transacted current-epoch-info))
(total-premium (get total-premium current-epoch-info)))
(try! (contract-call? .hq-ststx-earn-v1 check-is-updater tx-sender))
(try! (contract-call? .hq-ststx-earn-v1 check-is-pnl-calculator-active (contract-of pnl-calculator-contract)))
(asserts! (is-eq price-id (contract-call? .hq-ststx-earn-v1 get-option-asset-symbol)) ERR_NOT_OPTION_ASSET)
(asserts! (>= timestamp current-epoch-expiry) ERR_EPOCH_NOT_EXPIRED)
(asserts! (<= timestamp (+ current-epoch-expiry (contract-call? .hq-ststx-earn-v1 get-pnl-data-window))) ERR_SETTLEMENT_WINDOW_CLOSED)
(asserts! (is-eq (var-get total-funds-requested) u0) ERR_PAYMENT_OUTSTANDING)
(asserts! (is-none (get block-height-settled current-epoch-info)) ERR_EPOCH_ALREADY_SETTLED)
(map-set epoch-info { epoch-id: epoch-id }
(merge
current-epoch-info
{
unit-pnl: (some unit-pnl),
settlement-rate: (some usd-per-option-asset),
block-height-pnl: (some burn-block-height)
}
)
)
(print {
epoch-id: epoch-id,
expiry: current-epoch-expiry,
settlement-rate: usd-per-option-asset,
option-pnl: pnl-option-asset,
unit-pnl: unit-pnl,
return-pre-fees: (- (to-int (* unit-pnl total-units-transacted)) (to-int total-premium))
})
(if (or (is-eq pnl-usd u0) (is-eq total-units-transacted u0))
(try! (settle))
(begin
(map payment-requester (var-get counterparty-addresses))
true
)
)
(ok true)))
;;-------------------------------------
;; Trading
;;-------------------------------------
(define-public (start-new-epoch
(pnl-calculator-contract <pnl-calculator-trait>)
(expiry uint)
(strike-call (optional uint))
(strike-put (optional uint))
(barrier-up (optional uint))
(barrier-down (optional uint)))
(let (
(new-epoch-id (+ (get-current-epoch-id) u1))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(current-epoch-expiry (get epoch-expiry current-epoch-info))
(option-type (contract-call? .hq-ststx-earn-v1 get-option-type))
(strategy-type (contract-call? .hq-ststx-earn-v1 get-strategy-type))
(current-epoch-settled (is-some (get block-height-settled current-epoch-info))))
(try! (contract-call? .hq-ststx-earn-v1 check-is-trader tx-sender))
(try! (contract-call? .hq-ststx-earn-v1 check-is-pnl-calculator-active (contract-of pnl-calculator-contract)))
(asserts! (contract-call? .hq-ststx-earn-v1 get-trading-allowed) ERR_TRADING_NOT_ALLOWED)
(try! (contract-call? pnl-calculator-contract check-strike-order option-type strategy-type strike-call strike-put barrier-up barrier-down))
(asserts! (>= expiry current-epoch-expiry) ERR_EXPIRY_BEFORE_CURRENT_EXPIRY)
(asserts! (is-eq u0 (var-get trading-funds-registered)) ERR_EPOCH_ALREADY_STARTED_FUNDS_REGISTERED)
(try! (activate-pending-claims))
(unwrap-panic (init-trading-funds-available))
(if (and (is-eq current-epoch-expiry expiry) (not current-epoch-settled))
(begin
(asserts! (is-eq u0 (get total-units-transacted current-epoch-info)) ERR_EPOCH_ALREADY_STARTED_UNITS_TRANSACTED)
(map-set epoch-info { epoch-id: (get-current-epoch-id) }
(merge
current-epoch-info
{
epoch-expiry: expiry,
strike-call: strike-call,
strike-put: strike-put,
barrier-up: barrier-up,
barrier-down: barrier-down,
}
)
)
)
(begin
(asserts! current-epoch-settled ERR_CURRENT_EPOCH_NOT_SETTLED)
(asserts! (>= expiry (+ current-epoch-expiry (contract-call? .hq-ststx-earn-v1 get-min-epoch-duration))) ERR_VIOLATES_MIN_EPOCH_DURATION)
(asserts! (<= expiry (+ current-epoch-expiry (contract-call? .hq-ststx-earn-v1 get-max-epoch-duration))) ERR_VIOLATES_MAX_EPOCH_DURATION)
(map-set epoch-info { epoch-id: new-epoch-id }
{
epoch-expiry: expiry,
strike-call: strike-call,
strike-put: strike-put,
barrier-up: barrier-up,
barrier-down: barrier-down,
unit-pnl: none,
settlement-rate: none,
block-height-start: burn-block-height,
block-height-pnl: none,
block-height-settled: none,
total-units-transacted: u0,
total-premium: u0,
total-underlying-active: (contract-call? .vault-ststx-earn-v1 get-total-underlying-active),
underlying-per-token-settled: none,
epoch-risk: (contract-call? .hq-ststx-earn-v1 get-epoch-risk),
unit-size: (contract-call? .hq-ststx-earn-v1 get-next-unit-size)
}
)
(try! (as-contract (contract-call? .vault-ststx-earn-v1 create-epoch-info-for-claims)))
(var-set current-epoch-id new-epoch-id)
)
)
(ok true)))
(define-public (register-trade
(amount uint)
(option-price-per-underlying uint)
(counterparty-address principal))
(let (
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(funds-outgoing (* amount (/ (* option-price-per-underlying (contract-call? .hq-ststx-earn-v1 get-unit-size)) underlying-base))))
(try! (contract-call? .hq-ststx-earn-v1 check-is-trader tx-sender))
(asserts! (> (var-get current-epoch-id) u0) ERR_EPOCH_ZERO_TRADING_ATTEMPT)
(asserts! (is-standard counterparty-address) ERR_NOT_STANDARD)
(asserts! (contract-call? .hq-ststx-earn-v1 get-trading-allowed) ERR_TRADING_NOT_ALLOWED)
(asserts! (>= (var-get trading-funds-available) funds-outgoing) ERR_NOT_ENOUGH_FUNDS_AVAILABLE)
(asserts! (is-none (get block-height-settled current-epoch-info)) ERR_CURRENT_EPOCH_SETTLED)
(asserts! (<= burn-block-height (+
(get block-height-start current-epoch-info)
(contract-call? .hq-ststx-earn-v1 get-registration-window)))
ERR_REGISTRATION_WINDOW_CLOSED)
(match (map-get? counterparty-info { address: counterparty-address })
counterparty-data
(map-set counterparty-info { address: counterparty-address }
(merge
counterparty-data
{
units-registered: amount,
price-registered: (some option-price-per-underlying),
block-height-registered: (some burn-block-height),
}
)
)
(map-set counterparty-info { address: counterparty-address }
{
units-registered: amount,
price-registered: (some option-price-per-underlying),
block-height-registered: (some burn-block-height),
units-transacted: u0,
funds-requested: none,
}
)
)
(var-set trading-funds-available (- (var-get trading-funds-available) funds-outgoing))
(ok (var-set trading-funds-registered (+ (var-get trading-funds-registered) funds-outgoing)))))
(define-public (update-registration
(new-amount uint)
(new-option-price-per-underlying uint)
(counterparty-address principal))
(let (
(counterparty-info-entry (try! (get-counterparty-info counterparty-address)))
(current-units-registered (get units-registered counterparty-info-entry))
(current-price-registered (unwrap! (get price-registered counterparty-info-entry) ERR_NO_REGISTRATION_FOR_COUNTERPARTY))
(unit-size (contract-call? .hq-ststx-earn-v1 get-unit-size))
(current-funds-outgoing (* current-units-registered (/ (* current-price-registered unit-size) underlying-base)))
(units-transacted (get units-transacted counterparty-info-entry))
(new-funds-outgoing (* new-amount (/ (* new-option-price-per-underlying unit-size) underlying-base))))
(try! (contract-call? .hq-ststx-earn-v1 check-is-trader tx-sender))
(if (> new-funds-outgoing current-funds-outgoing)
(asserts! (>= (var-get trading-funds-available) (- new-funds-outgoing current-funds-outgoing)) ERR_NOT_ENOUGH_FUNDS_AVAILABLE)
true
)
(if (is-eq new-amount u0)
(if (is-eq units-transacted u0)
(map-delete counterparty-info { address: counterparty-address })
(map-set counterparty-info { address: counterparty-address }
(merge
counterparty-info-entry
{
units-registered: new-amount,
price-registered: none,
block-height-registered: none,
}
)
)
)
(map-set counterparty-info { address: counterparty-address }
(merge
counterparty-info-entry
{
units-registered: new-amount,
price-registered: (some new-option-price-per-underlying),
block-height-registered: (some burn-block-height),
}
)
)
)
(var-set trading-funds-registered (+ (- (var-get trading-funds-registered) current-funds-outgoing) new-funds-outgoing))
(ok (var-set trading-funds-available (- (+ (var-get trading-funds-available) current-funds-outgoing) new-funds-outgoing)))))
(define-public (confirm-trade
(amount uint)
(option-price-per-underlying uint))
(let (
(counterparty tx-sender)
(counterparty-info-entry (try! (get-counterparty-info tx-sender)))
(price-registered (unwrap! (get price-registered counterparty-info-entry) ERR_NO_REGISTRATION_FOR_COUNTERPARTY))
(block-height-registered (unwrap-panic (get block-height-registered counterparty-info-entry)))
(funds-outgoing (/ (* (* option-price-per-underlying amount) (contract-call? .hq-ststx-earn-v1 get-unit-size)) underlying-base))
(current-epoch-info (unwrap-panic (get-current-epoch-info))))
(asserts! (is-eq amount (get units-registered counterparty-info-entry)) ERR_AMOUNT_MISMATCH)
(asserts! (is-eq option-price-per-underlying price-registered) ERR_PRICE_MISMATCH)
(asserts! (<= burn-block-height (+ block-height-registered (contract-call? .hq-ststx-earn-v1 get-confirmation-window))) ERR_CONFIRMATION_WINDOW_CLOSED)
(try! (as-contract (contract-call? .vault-ststx-earn-v1 payout-funds funds-outgoing counterparty)))
(map-set counterparty-info { address: tx-sender }
{
units-registered: u0,
price-registered: none,
block-height-registered: none,
units-transacted: (+ amount (get units-transacted counterparty-info-entry)),
funds-requested: none,
}
)
(var-set trading-funds-registered (- (var-get trading-funds-registered) funds-outgoing))
(if (is-none (index-of? (var-get counterparty-addresses) tx-sender))
(var-set counterparty-addresses (unwrap-panic (as-max-len? (append (var-get counterparty-addresses) tx-sender) u5000)))
true
)
(map-set epoch-info { epoch-id: (get-current-epoch-id) }
(merge
current-epoch-info
{
total-units-transacted: (+ (get total-units-transacted current-epoch-info) amount),
total-premium: (+ (get total-premium current-epoch-info) funds-outgoing)
}
)
)
(ok true)))
(define-public (make-payment
(amount uint))
(let (
(funds-requested (unwrap! (get funds-requested (try! (get-counterparty-info tx-sender))) ERR_PNL_UNDETERMINED))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(block-height-pnl (unwrap-panic (get block-height-pnl current-epoch-info)))
(payment-window (contract-call? .hq-ststx-earn-v1 get-payment-window)))
(asserts! (<= burn-block-height (+ block-height-pnl payment-window)) ERR_PAYMENT_WINDOW_CLOSED)
(asserts! (is-eq amount funds-requested) ERR_PAYMENT_AMOUNTS_DONT_MATCH)
(try! (contract-call? .vault-ststx-earn-v1 deposit-funds amount tx-sender))
(map-delete counterparty-info { address: tx-sender })
(var-set helper-principal tx-sender)
(var-set counterparty-addresses (filter remove-principal (var-get counterparty-addresses)))
(var-set total-funds-requested (- (var-get total-funds-requested) amount))
(if (is-eq (var-get total-funds-requested) u0)
(try! (settle))
true
)
(ok true)))
(define-public (make-delayed-payment
(amount uint))
(let (
(funds-requested (get funds-requested (try! (get-delinquent-counterparty-info tx-sender)))))
(asserts! (is-eq amount funds-requested) ERR_PAYMENT_AMOUNTS_DONT_MATCH)
(try! (contract-call? .vault-ststx-earn-v1 deposit-funds amount tx-sender))
(map-delete delinquent-counterparty-info { address: tx-sender })
(var-set helper-principal tx-sender)
(ok (var-set delinquent-counterparty-addresses (filter remove-principal (var-get delinquent-counterparty-addresses))))))
;;-------------------------------------
;; Helper
;;-------------------------------------
(define-private (delete-counterparty
(counterparty-address principal))
(ok (map-delete counterparty-info { address: counterparty-address })))
(define-private (add-delinquent-counterparty
(counterparty-address principal))
(let (
(counterparty-info-entry (unwrap-panic (get-counterparty-info counterparty-address)))
(funds-requested (unwrap-panic (get funds-requested counterparty-info-entry)))
(units-transacted (get units-transacted counterparty-info-entry)))
(map-set delinquent-counterparty-info { address: counterparty-address }
{
funds-requested: funds-requested,
units-transacted: units-transacted,
epoch-id: (get-current-epoch-id)
}
)
(ok (var-set delinquent-counterparty-addresses (unwrap-panic (as-max-len? (append (var-get delinquent-counterparty-addresses) counterparty-address) u5000))))))
(define-private (remove-principal
(list-item principal))
(not (is-eq (var-get helper-principal) list-item)))
(define-read-only (usd-to-option-asset (usd uint) (usd-per-option-asset uint))
(/ (* usd underlying-base) usd-per-option-asset))
;;-------------------------------------
;; Initialize
;;-------------------------------------
(define-public (initialize
(first-expiry uint))
(begin
(try! (contract-call? .hq-ststx-earn-v1 check-is-admin tx-sender))
(asserts! (not (var-get is-initialized)) ERR_ALREADY_INITIALIZED)
(var-set is-initialized true) ;; Set to true so that this can't be called again
(map-set epoch-info { epoch-id: u0 }
{
epoch-expiry: first-expiry,
strike-call: none,
strike-put: none,
barrier-up: none,
barrier-down: none,
unit-pnl: none,
settlement-rate: none,
block-height-start: burn-block-height,
block-height-pnl: none,
block-height-settled: none,
total-units-transacted: u0,
total-premium: u0,
total-underlying-active: u0,
underlying-per-token-settled: none,
epoch-risk: (contract-call? .hq-ststx-earn-v1 get-epoch-risk),
unit-size: (contract-call? .hq-ststx-earn-v1 get-unit-size)
}
)
(ok true)))
;;-------------------------------------
;; Emergency API
;;-------------------------------------
;; In case of a deliquent counterparty this function ensures that the current epoch can be settled and deposits and withdrawals are procssed
(define-public (force-settle)
(let (
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(block-height-pnl (unwrap! (get block-height-pnl current-epoch-info) ERR_PNL_UNDETERMINED)))
(asserts! (> burn-block-height (+ block-height-pnl (contract-call? .hq-ststx-earn-v1 get-payment-window))) ERR_PAYMENT_WINDOW_STILL_OPEN)
(if (is-none (get block-height-settled current-epoch-info))
(begin ;; block-height-settled == none
(var-set delinquent-epoch true)
(map add-delinquent-counterparty (var-get counterparty-addresses))
(try! (settle))
)
(try! (activate-pending-claims))
)
(ok true)))
;; In case of malfuntion or compromise of determine-pnl this function ensures that a pnl is set so that settle can be called
(define-public (force-determine-pnl
(use-block-height bool)
(price-feed-bytes (buff 8192))
(execution-plan {
pyth-storage-contract: <pyth-storage-trait>,
pyth-decoder-contract: <pyth-decoder-trait>,
wormhole-core-contract: <wormhole-core-trait>
}))
(let (
(epoch-id (get-current-epoch-id))
(current-epoch-info (unwrap-panic (get-current-epoch-info)))
(current-epoch-expiry (get epoch-expiry current-epoch-info))
(pnl-calculation-window (contract-call? .hq-ststx-earn-v1 get-pnl-calculation-window)))
(asserts! (is-none (get block-height-pnl current-epoch-info)) ERR_PNL_ALREADY_DETERMINED)
(if use-block-height
(let (
(previous-epoch-expiry (get epoch-expiry (try! (get-epoch-info (- epoch-id u1)))))
(expected-epoch-duration-in-blocks (/ (/ (- current-epoch-expiry previous-epoch-expiry) minute-in-ms) u10))
(current-block-height-start (get block-height-start current-epoch-info))
(pnl-calculation-window-in-blocks (/ (/ pnl-calculation-window minute-in-ms) u10))
(safety-margin (* u5 u144))) ;; 5 days worth of blocks
(asserts! (>= burn-block-height (+ (+ (+ current-block-height-start expected-epoch-duration-in-blocks) pnl-calculation-window-in-blocks) safety-margin)) ERR_PNL_CALCULATION_WINDOW_STILL_OPEN))
(let (
(decoded-prices (try! (contract-call? 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-oracle-v2 decode-price-feeds price-feed-bytes execution-plan)))
(decoded-price (element-at decoded-prices u0))
(timestamp (* (unwrap-panic (get publish-time decoded-price)) u1000)))
(asserts! (>= timestamp (+ current-epoch-expiry pnl-calculation-window)) ERR_PNL_CALCULATION_WINDOW_STILL_OPEN)
)
)
(map-set epoch-info { epoch-id: epoch-id }
(merge
current-epoch-info
{
unit-pnl: (some u0),
settlement-rate: (some u0),
block-height-pnl: (some burn-block-height)
}
)
)
(var-set delinquent-epoch true) ;; set true so no fees are charged
(try! (settle))
(ok true)))
;; In case of contract malfunction these function ensure all funds in the contract can be recovered
(define-public (force-activate-pending-deposit-claims)
(begin
(try! (contract-call? .hq-ststx-earn-v1 check-is-admin tx-sender))
(as-contract (contract-call? .vault-ststx-earn-v1 activate-pending-deposit-claims))))
(define-public (force-activate-pending-withdrawal-claims)
(begin
(try! (contract-call? .hq-ststx-earn-v1 check-is-admin tx-sender))
(as-contract (contract-call? .vault-ststx-earn-v1 activate-pending-withdrawal-claims))))