@drift-labs/sdk 2.149.1 → 2.150.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +4 -0
- package/VERSION +1 -1
- package/lib/browser/constants/perpMarkets.js +11 -0
- package/lib/browser/constants/spotMarkets.js +13 -0
- package/lib/browser/decode/user.js +2 -2
- package/lib/browser/driftClient.d.ts +20 -8
- package/lib/browser/driftClient.js +216 -17
- package/lib/browser/idl/drift.json +225 -21
- package/lib/browser/math/margin.js +2 -1
- package/lib/browser/math/position.d.ts +1 -0
- package/lib/browser/math/position.js +10 -2
- package/lib/browser/math/superStake.d.ts +3 -2
- package/lib/browser/types.d.ts +12 -6
- package/lib/browser/types.js +11 -6
- package/lib/browser/user.d.ts +3 -2
- package/lib/browser/user.js +24 -8
- package/lib/node/constants/perpMarkets.d.ts.map +1 -1
- package/lib/node/constants/perpMarkets.js +11 -0
- package/lib/node/constants/spotMarkets.d.ts.map +1 -1
- package/lib/node/constants/spotMarkets.js +13 -0
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +2 -2
- package/lib/node/driftClient.d.ts +20 -8
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +216 -17
- package/lib/node/idl/drift.json +225 -21
- package/lib/node/math/margin.d.ts.map +1 -1
- package/lib/node/math/margin.js +2 -1
- package/lib/node/math/position.d.ts +1 -0
- package/lib/node/math/position.d.ts.map +1 -1
- package/lib/node/math/position.js +10 -2
- package/lib/node/math/spotBalance.d.ts.map +1 -1
- package/lib/node/math/superStake.d.ts +3 -2
- package/lib/node/math/superStake.d.ts.map +1 -1
- package/lib/node/types.d.ts +12 -6
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/types.js +11 -6
- package/lib/node/user.d.ts +3 -2
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +24 -8
- package/package.json +1 -1
- package/scripts/deposit-isolated-positions.ts +110 -0
- package/scripts/single-grpc-client-test.ts +71 -21
- package/scripts/withdraw-isolated-positions.ts +174 -0
- package/src/constants/perpMarkets.ts +11 -0
- package/src/constants/spotMarkets.ts +14 -0
- package/src/decode/user.ts +2 -3
- package/src/driftClient.ts +464 -41
- package/src/idl/drift.json +226 -22
- package/src/margin/README.md +143 -0
- package/src/math/margin.ts +3 -4
- package/src/math/position.ts +12 -2
- package/src/math/spotBalance.ts +0 -1
- package/src/types.ts +15 -7
- package/src/user.ts +49 -15
- package/tests/amm/test.ts +1 -1
- package/tests/dlob/helpers.ts +1 -1
- package/tests/user/test.ts +0 -7
package/src/idl/drift.json
CHANGED
|
@@ -652,6 +652,163 @@
|
|
|
652
652
|
}
|
|
653
653
|
]
|
|
654
654
|
},
|
|
655
|
+
{
|
|
656
|
+
"name": "depositIntoIsolatedPerpPosition",
|
|
657
|
+
"accounts": [
|
|
658
|
+
{
|
|
659
|
+
"name": "state",
|
|
660
|
+
"isMut": false,
|
|
661
|
+
"isSigner": false
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
"name": "user",
|
|
665
|
+
"isMut": true,
|
|
666
|
+
"isSigner": false
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
"name": "userStats",
|
|
670
|
+
"isMut": true,
|
|
671
|
+
"isSigner": false
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
"name": "authority",
|
|
675
|
+
"isMut": false,
|
|
676
|
+
"isSigner": true
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
"name": "spotMarketVault",
|
|
680
|
+
"isMut": true,
|
|
681
|
+
"isSigner": false
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
"name": "userTokenAccount",
|
|
685
|
+
"isMut": true,
|
|
686
|
+
"isSigner": false
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
"name": "tokenProgram",
|
|
690
|
+
"isMut": false,
|
|
691
|
+
"isSigner": false
|
|
692
|
+
}
|
|
693
|
+
],
|
|
694
|
+
"args": [
|
|
695
|
+
{
|
|
696
|
+
"name": "spotMarketIndex",
|
|
697
|
+
"type": "u16"
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
"name": "perpMarketIndex",
|
|
701
|
+
"type": "u16"
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
"name": "amount",
|
|
705
|
+
"type": "u64"
|
|
706
|
+
}
|
|
707
|
+
]
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"name": "transferIsolatedPerpPositionDeposit",
|
|
711
|
+
"accounts": [
|
|
712
|
+
{
|
|
713
|
+
"name": "user",
|
|
714
|
+
"isMut": true,
|
|
715
|
+
"isSigner": false
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
"name": "userStats",
|
|
719
|
+
"isMut": true,
|
|
720
|
+
"isSigner": false
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
"name": "authority",
|
|
724
|
+
"isMut": false,
|
|
725
|
+
"isSigner": true
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
"name": "state",
|
|
729
|
+
"isMut": false,
|
|
730
|
+
"isSigner": false
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
"name": "spotMarketVault",
|
|
734
|
+
"isMut": false,
|
|
735
|
+
"isSigner": false
|
|
736
|
+
}
|
|
737
|
+
],
|
|
738
|
+
"args": [
|
|
739
|
+
{
|
|
740
|
+
"name": "spotMarketIndex",
|
|
741
|
+
"type": "u16"
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
"name": "perpMarketIndex",
|
|
745
|
+
"type": "u16"
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
"name": "amount",
|
|
749
|
+
"type": "i64"
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
"name": "withdrawFromIsolatedPerpPosition",
|
|
755
|
+
"accounts": [
|
|
756
|
+
{
|
|
757
|
+
"name": "state",
|
|
758
|
+
"isMut": false,
|
|
759
|
+
"isSigner": false
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
"name": "user",
|
|
763
|
+
"isMut": true,
|
|
764
|
+
"isSigner": false
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
"name": "userStats",
|
|
768
|
+
"isMut": true,
|
|
769
|
+
"isSigner": false
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
"name": "authority",
|
|
773
|
+
"isMut": false,
|
|
774
|
+
"isSigner": true
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
"name": "spotMarketVault",
|
|
778
|
+
"isMut": true,
|
|
779
|
+
"isSigner": false
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
"name": "driftSigner",
|
|
783
|
+
"isMut": false,
|
|
784
|
+
"isSigner": false
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
"name": "userTokenAccount",
|
|
788
|
+
"isMut": true,
|
|
789
|
+
"isSigner": false
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
"name": "tokenProgram",
|
|
793
|
+
"isMut": false,
|
|
794
|
+
"isSigner": false
|
|
795
|
+
}
|
|
796
|
+
],
|
|
797
|
+
"args": [
|
|
798
|
+
{
|
|
799
|
+
"name": "spotMarketIndex",
|
|
800
|
+
"type": "u16"
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
"name": "perpMarketIndex",
|
|
804
|
+
"type": "u16"
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
"name": "amount",
|
|
808
|
+
"type": "u64"
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
},
|
|
655
812
|
{
|
|
656
813
|
"name": "placePerpOrder",
|
|
657
814
|
"accounts": [
|
|
@@ -14412,13 +14569,13 @@
|
|
|
14412
14569
|
"type": "u64"
|
|
14413
14570
|
},
|
|
14414
14571
|
{
|
|
14415
|
-
"name": "
|
|
14572
|
+
"name": "isolatedPositionScaledBalance",
|
|
14416
14573
|
"docs": [
|
|
14417
14574
|
"The last base asset amount per lp the amm had",
|
|
14418
14575
|
"Used to settle the users lp position",
|
|
14419
|
-
"precision:
|
|
14576
|
+
"precision: SPOT_BALANCE_PRECISION"
|
|
14420
14577
|
],
|
|
14421
|
-
"type": "
|
|
14578
|
+
"type": "u64"
|
|
14422
14579
|
},
|
|
14423
14580
|
{
|
|
14424
14581
|
"name": "lastQuoteAssetAmountPerLp",
|
|
@@ -14457,8 +14614,8 @@
|
|
|
14457
14614
|
"type": "u8"
|
|
14458
14615
|
},
|
|
14459
14616
|
{
|
|
14460
|
-
"name": "
|
|
14461
|
-
"type": "
|
|
14617
|
+
"name": "positionFlag",
|
|
14618
|
+
"type": "u8"
|
|
14462
14619
|
}
|
|
14463
14620
|
]
|
|
14464
14621
|
}
|
|
@@ -15138,6 +15295,17 @@
|
|
|
15138
15295
|
]
|
|
15139
15296
|
}
|
|
15140
15297
|
},
|
|
15298
|
+
{
|
|
15299
|
+
"name": "LiquidationBitFlag",
|
|
15300
|
+
"type": {
|
|
15301
|
+
"kind": "enum",
|
|
15302
|
+
"variants": [
|
|
15303
|
+
{
|
|
15304
|
+
"name": "IsolatedPosition"
|
|
15305
|
+
}
|
|
15306
|
+
]
|
|
15307
|
+
}
|
|
15308
|
+
},
|
|
15141
15309
|
{
|
|
15142
15310
|
"name": "SettlePnlExplanation",
|
|
15143
15311
|
"type": {
|
|
@@ -15267,13 +15435,7 @@
|
|
|
15267
15435
|
"kind": "enum",
|
|
15268
15436
|
"variants": [
|
|
15269
15437
|
{
|
|
15270
|
-
"name": "Standard"
|
|
15271
|
-
"fields": [
|
|
15272
|
-
{
|
|
15273
|
-
"name": "trackOpenOrdersFraction",
|
|
15274
|
-
"type": "bool"
|
|
15275
|
-
}
|
|
15276
|
-
]
|
|
15438
|
+
"name": "Standard"
|
|
15277
15439
|
},
|
|
15278
15440
|
{
|
|
15279
15441
|
"name": "Liquidation",
|
|
@@ -15434,6 +15596,9 @@
|
|
|
15434
15596
|
},
|
|
15435
15597
|
{
|
|
15436
15598
|
"name": "AmmImmediateFill"
|
|
15599
|
+
},
|
|
15600
|
+
{
|
|
15601
|
+
"name": "SettleRevPool"
|
|
15437
15602
|
}
|
|
15438
15603
|
]
|
|
15439
15604
|
}
|
|
@@ -15912,6 +16077,23 @@
|
|
|
15912
16077
|
]
|
|
15913
16078
|
}
|
|
15914
16079
|
},
|
|
16080
|
+
{
|
|
16081
|
+
"name": "PositionFlag",
|
|
16082
|
+
"type": {
|
|
16083
|
+
"kind": "enum",
|
|
16084
|
+
"variants": [
|
|
16085
|
+
{
|
|
16086
|
+
"name": "IsolatedPosition"
|
|
16087
|
+
},
|
|
16088
|
+
{
|
|
16089
|
+
"name": "BeingLiquidated"
|
|
16090
|
+
},
|
|
16091
|
+
{
|
|
16092
|
+
"name": "Bankrupt"
|
|
16093
|
+
}
|
|
16094
|
+
]
|
|
16095
|
+
}
|
|
16096
|
+
},
|
|
15915
16097
|
{
|
|
15916
16098
|
"name": "ReferrerStatus",
|
|
15917
16099
|
"type": {
|
|
@@ -16131,6 +16313,18 @@
|
|
|
16131
16313
|
"option": "publicKey"
|
|
16132
16314
|
},
|
|
16133
16315
|
"index": false
|
|
16316
|
+
},
|
|
16317
|
+
{
|
|
16318
|
+
"name": "signer",
|
|
16319
|
+
"type": {
|
|
16320
|
+
"option": "publicKey"
|
|
16321
|
+
},
|
|
16322
|
+
"index": false
|
|
16323
|
+
},
|
|
16324
|
+
{
|
|
16325
|
+
"name": "userTokenAmountAfter",
|
|
16326
|
+
"type": "i128",
|
|
16327
|
+
"index": false
|
|
16134
16328
|
}
|
|
16135
16329
|
]
|
|
16136
16330
|
},
|
|
@@ -16876,6 +17070,11 @@
|
|
|
16876
17070
|
"defined": "SpotBankruptcyRecord"
|
|
16877
17071
|
},
|
|
16878
17072
|
"index": false
|
|
17073
|
+
},
|
|
17074
|
+
{
|
|
17075
|
+
"name": "bitFlags",
|
|
17076
|
+
"type": "u8",
|
|
17077
|
+
"index": false
|
|
16879
17078
|
}
|
|
16880
17079
|
]
|
|
16881
17080
|
},
|
|
@@ -18312,8 +18511,8 @@
|
|
|
18312
18511
|
},
|
|
18313
18512
|
{
|
|
18314
18513
|
"code": 6094,
|
|
18315
|
-
"name": "
|
|
18316
|
-
"msg": "
|
|
18514
|
+
"name": "CantUpdateSpotBalanceType",
|
|
18515
|
+
"msg": "CantUpdateSpotBalanceType"
|
|
18317
18516
|
},
|
|
18318
18517
|
{
|
|
18319
18518
|
"code": 6095,
|
|
@@ -19427,41 +19626,46 @@
|
|
|
19427
19626
|
},
|
|
19428
19627
|
{
|
|
19429
19628
|
"code": 6317,
|
|
19629
|
+
"name": "InvalidIsolatedPerpMarket",
|
|
19630
|
+
"msg": "Invalid Isolated Perp Market"
|
|
19631
|
+
},
|
|
19632
|
+
{
|
|
19633
|
+
"code": 6318,
|
|
19430
19634
|
"name": "InvalidRevenueShareResize",
|
|
19431
19635
|
"msg": "Invalid RevenueShare resize"
|
|
19432
19636
|
},
|
|
19433
19637
|
{
|
|
19434
|
-
"code":
|
|
19638
|
+
"code": 6319,
|
|
19435
19639
|
"name": "BuilderRevoked",
|
|
19436
19640
|
"msg": "Builder has been revoked"
|
|
19437
19641
|
},
|
|
19438
19642
|
{
|
|
19439
|
-
"code":
|
|
19643
|
+
"code": 6320,
|
|
19440
19644
|
"name": "InvalidBuilderFee",
|
|
19441
19645
|
"msg": "Builder fee is greater than max fee bps"
|
|
19442
19646
|
},
|
|
19443
19647
|
{
|
|
19444
|
-
"code":
|
|
19648
|
+
"code": 6321,
|
|
19445
19649
|
"name": "RevenueShareEscrowAuthorityMismatch",
|
|
19446
19650
|
"msg": "RevenueShareEscrow authority mismatch"
|
|
19447
19651
|
},
|
|
19448
19652
|
{
|
|
19449
|
-
"code":
|
|
19653
|
+
"code": 6322,
|
|
19450
19654
|
"name": "RevenueShareEscrowOrdersAccountFull",
|
|
19451
19655
|
"msg": "RevenueShareEscrow has too many active orders"
|
|
19452
19656
|
},
|
|
19453
19657
|
{
|
|
19454
|
-
"code":
|
|
19658
|
+
"code": 6323,
|
|
19455
19659
|
"name": "InvalidRevenueShareAccount",
|
|
19456
19660
|
"msg": "Invalid RevenueShareAccount"
|
|
19457
19661
|
},
|
|
19458
19662
|
{
|
|
19459
|
-
"code":
|
|
19663
|
+
"code": 6324,
|
|
19460
19664
|
"name": "CannotRevokeBuilderWithOpenOrders",
|
|
19461
19665
|
"msg": "Cannot revoke builder with open orders"
|
|
19462
19666
|
},
|
|
19463
19667
|
{
|
|
19464
|
-
"code":
|
|
19668
|
+
"code": 6325,
|
|
19465
19669
|
"name": "UnableToLoadRevenueShareAccount",
|
|
19466
19670
|
"msg": "Unable to load builder account"
|
|
19467
19671
|
},
|
|
@@ -19566,4 +19770,4 @@
|
|
|
19566
19770
|
"msg": "MarketIndexNotFoundAmmCache"
|
|
19567
19771
|
}
|
|
19568
19772
|
]
|
|
19569
|
-
}
|
|
19773
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
## Margin Calculation Snapshot (SDK)
|
|
2
|
+
|
|
3
|
+
This document describes the single-source-of-truth margin engine in the SDK that mirrors the on-chain `MarginCalculation` and related semantics. The goal is to compute an immutable snapshot in one pass and have existing `User` getters delegate to it, eliminating duplicative work across getters and UI hooks while maintaining parity with the program.
|
|
4
|
+
|
|
5
|
+
### Alignment with on-chain
|
|
6
|
+
|
|
7
|
+
- The SDK snapshot shape mirrors `programs/drift/src/state/margin_calculation.rs` field-for-field.
|
|
8
|
+
- The inputs and ordering mirror `calculate_margin_requirement_and_total_collateral_and_liability_info` in `programs/drift/src/math/margin.rs`.
|
|
9
|
+
- Isolated positions are represented as `isolated_margin_calculations` keyed by perp `market_index`, matching program logic.
|
|
10
|
+
|
|
11
|
+
### Core SDK types (shape parity)
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// Types reflect on-chain names and numeric signs
|
|
15
|
+
export type MarginRequirementType = 'Initial' | 'Fill' | 'Maintenance';
|
|
16
|
+
export type MarketType = 'Spot' | 'Perp';
|
|
17
|
+
|
|
18
|
+
export type MarketIdentifier = {
|
|
19
|
+
marketType: MarketType;
|
|
20
|
+
marketIndex: number; // u16
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type MarginCalculationMode =
|
|
24
|
+
| { kind: 'Standard' }
|
|
25
|
+
| { kind: 'Liquidation'; marketToTrackMarginRequirement?: MarketIdentifier };
|
|
26
|
+
|
|
27
|
+
export type MarginContext = {
|
|
28
|
+
marginType: MarginRequirementType;
|
|
29
|
+
mode: MarginCalculationMode;
|
|
30
|
+
strict: boolean;
|
|
31
|
+
ignoreInvalidDepositOracles: boolean;
|
|
32
|
+
marginBuffer: BN; // u128
|
|
33
|
+
fuelBonusNumerator: number; // i64
|
|
34
|
+
fuelBonus: number; // u64
|
|
35
|
+
fuelPerpDelta?: { marketIndex: number; delta: BN }; // (u16, i64)
|
|
36
|
+
fuelSpotDeltas: Array<{ marketIndex: number; delta: BN }>; // up to 2 entries
|
|
37
|
+
marginRatioOverride?: number; // u32
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type IsolatedMarginCalculation = {
|
|
41
|
+
marginRequirement: BN; // u128
|
|
42
|
+
totalCollateral: BN; // i128
|
|
43
|
+
totalCollateralBuffer: BN; // i128
|
|
44
|
+
marginRequirementPlusBuffer: BN; // u128
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type MarginCalculation = {
|
|
48
|
+
context: MarginContext;
|
|
49
|
+
|
|
50
|
+
totalCollateral: BN; // i128
|
|
51
|
+
totalCollateralBuffer: BN; // i128
|
|
52
|
+
marginRequirement: BN; // u128
|
|
53
|
+
marginRequirementPlusBuffer: BN; // u128
|
|
54
|
+
|
|
55
|
+
isolatedMarginCalculations: Map<number, IsolatedMarginCalculation>; // BTreeMap<u16,_>
|
|
56
|
+
|
|
57
|
+
numSpotLiabilities: number; // u8
|
|
58
|
+
numPerpLiabilities: number; // u8
|
|
59
|
+
allDepositOraclesValid: boolean;
|
|
60
|
+
allLiabilityOraclesValid: boolean;
|
|
61
|
+
withPerpIsolatedLiability: boolean;
|
|
62
|
+
withSpotIsolatedLiability: boolean;
|
|
63
|
+
|
|
64
|
+
totalSpotAssetValue: BN; // i128
|
|
65
|
+
totalSpotLiabilityValue: BN; // u128
|
|
66
|
+
totalPerpLiabilityValue: BN; // u128
|
|
67
|
+
totalPerpPnl: BN; // i128
|
|
68
|
+
|
|
69
|
+
trackedMarketMarginRequirement: BN; // u128
|
|
70
|
+
fuelDeposits: number; // u32
|
|
71
|
+
fuelBorrows: number; // u32
|
|
72
|
+
fuelPositions: number; // u32
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Engine API
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// Pure computation, no I/O; uses data already cached in the client/subscribers
|
|
80
|
+
export function computeMarginCalculation(user: User, context: MarginContext): MarginCalculation;
|
|
81
|
+
|
|
82
|
+
// Helpers that mirror on-chain semantics
|
|
83
|
+
export function meets_margin_requirement(calc: MarginCalculation): boolean;
|
|
84
|
+
export function meets_margin_requirement_with_buffer(calc: MarginCalculation): boolean;
|
|
85
|
+
export function get_cross_free_collateral(calc: MarginCalculation): BN;
|
|
86
|
+
export function get_isolated_free_collateral(calc: MarginCalculation, marketIndex: number): BN;
|
|
87
|
+
export function cross_margin_shortage(calc: MarginCalculation): BN; // requires buffer mode
|
|
88
|
+
export function isolated_margin_shortage(calc: MarginCalculation, marketIndex: number): BN; // requires buffer mode
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Computation model (on-demand)
|
|
92
|
+
|
|
93
|
+
- The SDK computes the snapshot on-demand when `getMarginCalculation(...)` is called.
|
|
94
|
+
- No event-driven recomputation by default (oracle prices can change every slot; recomputing every update would be wasteful).
|
|
95
|
+
- Callers (UI/bots) decide polling frequency (e.g., UI can refresh every ~1s on active trade forms).
|
|
96
|
+
|
|
97
|
+
### User integration
|
|
98
|
+
|
|
99
|
+
- Add `user.getMarginCalculation(margin_type = 'Initial', overrides?: Partial<MarginContext>)`.
|
|
100
|
+
- Existing getters delegate to the snapshot to avoid duplicate work:
|
|
101
|
+
- `getTotalCollateral()` → `snapshot.total_collateral`
|
|
102
|
+
- `getMarginRequirement(mode)` → `snapshot.margin_requirement`
|
|
103
|
+
- `getFreeCollateral()` → `get_cross_free_collateral(snapshot)`
|
|
104
|
+
- Per-market isolated FC → `get_isolated_free_collateral(snapshot, marketIndex)`
|
|
105
|
+
|
|
106
|
+
Suggested `User` API surface (non-breaking):
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// Primary entrypoint
|
|
110
|
+
getMarginCalculation(
|
|
111
|
+
marginType: 'Initial' | 'Maintenance' | 'Fill' = 'Initial',
|
|
112
|
+
contextOverrides?: Partial<MarginContext>
|
|
113
|
+
): MarginCalculation;
|
|
114
|
+
|
|
115
|
+
// Optional conveniences for consumers
|
|
116
|
+
getIsolatedMarginCalculation(
|
|
117
|
+
marketIndex: number,
|
|
118
|
+
marginType: 'Initial' | 'Maintenance' | 'Fill' = 'Initial',
|
|
119
|
+
contextOverrides?: Partial<MarginContext>
|
|
120
|
+
): IsolatedMarginCalculation | undefined;
|
|
121
|
+
|
|
122
|
+
// Cross views can continue to use helpers on the snapshot:
|
|
123
|
+
// get_cross_free_collateral(snapshot), meets_margin_requirement(snapshot), etc.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### UI compatibility
|
|
127
|
+
|
|
128
|
+
- All existing `User` getters remain and delegate to the snapshot, so current UI keeps working without call-site changes.
|
|
129
|
+
- New consumers can call `user.getMarginCalculation()` to access isolated breakdowns.
|
|
130
|
+
|
|
131
|
+
### Testing and parity
|
|
132
|
+
|
|
133
|
+
- Golden tests comparing SDK snapshot against program outputs (cross and isolated, edge cases).
|
|
134
|
+
- Keep math/rounding identical to program (ordering, buffers, funding, open-order IM, oracle strictness).
|
|
135
|
+
|
|
136
|
+
### Migration plan (brief)
|
|
137
|
+
|
|
138
|
+
1. Implement `types` and `engine` with strict parity; land behind a feature flag.
|
|
139
|
+
2. Add `user.getMarginCalculation()` and delegate legacy getters.
|
|
140
|
+
3. Optionally update UI hooks to read richer fields; not required for compatibility.
|
|
141
|
+
4. Expand parity tests; enable by default after validation.
|
|
142
|
+
|
|
143
|
+
|
package/src/math/margin.ts
CHANGED
|
@@ -166,21 +166,20 @@ export function calculateWorstCasePerpLiabilityValue(
|
|
|
166
166
|
perpPosition: PerpPosition,
|
|
167
167
|
perpMarket: PerpMarketAccount,
|
|
168
168
|
oraclePrice: BN,
|
|
169
|
-
includeOpenOrders = true
|
|
169
|
+
includeOpenOrders: boolean = true
|
|
170
170
|
): { worstCaseBaseAssetAmount: BN; worstCaseLiabilityValue: BN } {
|
|
171
171
|
const isPredictionMarket = isVariant(perpMarket.contractType, 'prediction');
|
|
172
|
-
|
|
172
|
+
// return early if no open orders required
|
|
173
173
|
if (!includeOpenOrders) {
|
|
174
174
|
return {
|
|
175
175
|
worstCaseBaseAssetAmount: perpPosition.baseAssetAmount,
|
|
176
176
|
worstCaseLiabilityValue: calculatePerpLiabilityValue(
|
|
177
177
|
perpPosition.baseAssetAmount,
|
|
178
178
|
oraclePrice,
|
|
179
|
-
|
|
179
|
+
isVariant(perpMarket.contractType, 'prediction')
|
|
180
180
|
),
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
|
-
|
|
184
183
|
const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
|
|
185
184
|
const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
|
|
186
185
|
|
package/src/math/position.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
PositionDirection,
|
|
15
15
|
PerpPosition,
|
|
16
16
|
SpotMarketAccount,
|
|
17
|
+
PositionFlag,
|
|
17
18
|
} from '../types';
|
|
18
19
|
import {
|
|
19
20
|
calculateUpdatedAMM,
|
|
@@ -127,7 +128,6 @@ export function calculatePositionPNL(
|
|
|
127
128
|
|
|
128
129
|
if (withFunding) {
|
|
129
130
|
const fundingRatePnL = calculateUnsettledFundingPnl(market, perpPosition);
|
|
130
|
-
|
|
131
131
|
pnl = pnl.add(fundingRatePnL);
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -244,7 +244,17 @@ export function positionIsAvailable(position: PerpPosition): boolean {
|
|
|
244
244
|
position.baseAssetAmount.eq(ZERO) &&
|
|
245
245
|
position.openOrders === 0 &&
|
|
246
246
|
position.quoteAssetAmount.eq(ZERO) &&
|
|
247
|
-
position.lpShares.eq(ZERO)
|
|
247
|
+
position.lpShares.eq(ZERO) &&
|
|
248
|
+
position.isolatedPositionScaledBalance.eq(ZERO) &&
|
|
249
|
+
!positionIsBeingLiquidated(position)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function positionIsBeingLiquidated(position: PerpPosition): boolean {
|
|
254
|
+
return (
|
|
255
|
+
(position.positionFlag &
|
|
256
|
+
(PositionFlag.BeingLiquidated | PositionFlag.Bankruptcy)) >
|
|
257
|
+
0
|
|
248
258
|
);
|
|
249
259
|
}
|
|
250
260
|
|
package/src/math/spotBalance.ts
CHANGED
|
@@ -68,7 +68,6 @@ export function getTokenAmount(
|
|
|
68
68
|
balanceType: SpotBalanceType
|
|
69
69
|
): BN {
|
|
70
70
|
const precisionDecrease = TEN.pow(new BN(19 - spotMarket.decimals));
|
|
71
|
-
|
|
72
71
|
if (isVariant(balanceType, 'deposit')) {
|
|
73
72
|
return balanceAmount
|
|
74
73
|
.mul(spotMarket.cumulativeDepositInterest)
|
package/src/types.ts
CHANGED
|
@@ -585,6 +585,10 @@ export type SpotBankruptcyRecord = {
|
|
|
585
585
|
ifPayment: BN;
|
|
586
586
|
};
|
|
587
587
|
|
|
588
|
+
export class LiquidationBitFlag {
|
|
589
|
+
static readonly IsolatedPosition = 1;
|
|
590
|
+
}
|
|
591
|
+
|
|
588
592
|
export type SettlePnlRecord = {
|
|
589
593
|
ts: BN;
|
|
590
594
|
user: PublicKey;
|
|
@@ -1137,16 +1141,10 @@ export type PerpPosition = {
|
|
|
1137
1141
|
lastBaseAssetAmountPerLp: BN;
|
|
1138
1142
|
lastQuoteAssetAmountPerLp: BN;
|
|
1139
1143
|
perLpBase: number;
|
|
1140
|
-
isolatedPositionScaledBalance: BN;
|
|
1141
1144
|
positionFlag: number;
|
|
1145
|
+
isolatedPositionScaledBalance: BN;
|
|
1142
1146
|
};
|
|
1143
1147
|
|
|
1144
|
-
export class PositionFlag {
|
|
1145
|
-
static readonly IsolatedPosition = 1;
|
|
1146
|
-
static readonly BeingLiquidated = 2;
|
|
1147
|
-
static readonly Bankruptcy = 4;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
1148
|
export type UserStatsAccount = {
|
|
1151
1149
|
numberOfSubAccounts: number;
|
|
1152
1150
|
numberOfSubAccountsCreated: number;
|
|
@@ -1297,6 +1295,12 @@ export class OrderParamsBitFlag {
|
|
|
1297
1295
|
static readonly UpdateHighLeverageMode = 2;
|
|
1298
1296
|
}
|
|
1299
1297
|
|
|
1298
|
+
export class PositionFlag {
|
|
1299
|
+
static readonly IsolatedPosition = 1;
|
|
1300
|
+
static readonly BeingLiquidated = 2;
|
|
1301
|
+
static readonly Bankruptcy = 4;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1300
1304
|
export type NecessaryOrderParams = {
|
|
1301
1305
|
orderType: OrderType;
|
|
1302
1306
|
marketIndex: number;
|
|
@@ -1308,6 +1312,10 @@ export type OptionalOrderParams = {
|
|
|
1308
1312
|
[Property in keyof OrderParams]?: OrderParams[Property];
|
|
1309
1313
|
} & NecessaryOrderParams;
|
|
1310
1314
|
|
|
1315
|
+
export type PerpOrderIsolatedExtras = {
|
|
1316
|
+
isolatedPositionDepositAmount?: BN;
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1311
1319
|
export type ModifyOrderParams = {
|
|
1312
1320
|
[Property in keyof OrderParams]?: OrderParams[Property] | null;
|
|
1313
1321
|
} & { policy?: ModifyOrderPolicy };
|