@drift-labs/sdk 2.145.0 → 2.146.0-alpha.13

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.
Files changed (99) hide show
  1. package/.env +4 -0
  2. package/VERSION +1 -1
  3. package/lib/browser/accounts/grpcMultiUserAccountSubscriber.js +8 -1
  4. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
  5. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
  6. package/lib/browser/adminClient.d.ts +5 -1
  7. package/lib/browser/adminClient.js +57 -23
  8. package/lib/browser/constants/numericConstants.d.ts +2 -0
  9. package/lib/browser/constants/numericConstants.js +5 -1
  10. package/lib/browser/constants/perpMarkets.js +0 -2
  11. package/lib/browser/decode/user.js +4 -0
  12. package/lib/browser/driftClient.d.ts +25 -10
  13. package/lib/browser/driftClient.js +238 -41
  14. package/lib/browser/driftClientConfig.d.ts +7 -2
  15. package/lib/browser/idl/drift.json +245 -22
  16. package/lib/browser/index.d.ts +4 -0
  17. package/lib/browser/index.js +9 -1
  18. package/lib/browser/marginCalculation.d.ts +86 -0
  19. package/lib/browser/marginCalculation.js +209 -0
  20. package/lib/browser/math/margin.d.ts +1 -1
  21. package/lib/browser/math/margin.js +8 -1
  22. package/lib/browser/math/position.d.ts +1 -0
  23. package/lib/browser/math/position.js +10 -2
  24. package/lib/browser/math/spotPosition.d.ts +1 -1
  25. package/lib/browser/math/spotPosition.js +3 -2
  26. package/lib/browser/math/superStake.d.ts +3 -2
  27. package/lib/browser/types.d.ts +13 -0
  28. package/lib/browser/types.js +12 -1
  29. package/lib/browser/user.d.ts +59 -11
  30. package/lib/browser/user.js +348 -43
  31. package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts.map +1 -1
  32. package/lib/node/accounts/grpcMultiUserAccountSubscriber.js +8 -1
  33. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
  34. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +1 -1
  35. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
  36. package/lib/node/adminClient.d.ts +5 -1
  37. package/lib/node/adminClient.d.ts.map +1 -1
  38. package/lib/node/adminClient.js +57 -23
  39. package/lib/node/constants/numericConstants.d.ts +2 -0
  40. package/lib/node/constants/numericConstants.d.ts.map +1 -1
  41. package/lib/node/constants/numericConstants.js +5 -1
  42. package/lib/node/constants/perpMarkets.d.ts.map +1 -1
  43. package/lib/node/constants/perpMarkets.js +0 -2
  44. package/lib/node/decode/user.d.ts.map +1 -1
  45. package/lib/node/decode/user.js +4 -0
  46. package/lib/node/driftClient.d.ts +25 -10
  47. package/lib/node/driftClient.d.ts.map +1 -1
  48. package/lib/node/driftClient.js +238 -41
  49. package/lib/node/driftClientConfig.d.ts +7 -2
  50. package/lib/node/driftClientConfig.d.ts.map +1 -1
  51. package/lib/node/idl/drift.json +245 -22
  52. package/lib/node/index.d.ts +4 -0
  53. package/lib/node/index.d.ts.map +1 -1
  54. package/lib/node/index.js +9 -1
  55. package/lib/node/marginCalculation.d.ts +87 -0
  56. package/lib/node/marginCalculation.d.ts.map +1 -0
  57. package/lib/node/marginCalculation.js +209 -0
  58. package/lib/node/math/margin.d.ts +1 -1
  59. package/lib/node/math/margin.d.ts.map +1 -1
  60. package/lib/node/math/margin.js +8 -1
  61. package/lib/node/math/position.d.ts +1 -0
  62. package/lib/node/math/position.d.ts.map +1 -1
  63. package/lib/node/math/position.js +10 -2
  64. package/lib/node/math/spotPosition.d.ts +1 -1
  65. package/lib/node/math/spotPosition.d.ts.map +1 -1
  66. package/lib/node/math/spotPosition.js +3 -2
  67. package/lib/node/math/superStake.d.ts +3 -2
  68. package/lib/node/math/superStake.d.ts.map +1 -1
  69. package/lib/node/types.d.ts +13 -0
  70. package/lib/node/types.d.ts.map +1 -1
  71. package/lib/node/types.js +12 -1
  72. package/lib/node/user.d.ts +59 -11
  73. package/lib/node/user.d.ts.map +1 -1
  74. package/lib/node/user.js +348 -43
  75. package/package.json +1 -1
  76. package/scripts/deposit-isolated-positions.ts +110 -0
  77. package/scripts/single-grpc-client-test.ts +71 -21
  78. package/scripts/withdraw-isolated-positions.ts +174 -0
  79. package/src/accounts/grpcMultiUserAccountSubscriber.ts +8 -1
  80. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +566 -167
  81. package/src/adminClient.ts +74 -25
  82. package/src/constants/numericConstants.ts +5 -0
  83. package/src/constants/perpMarkets.ts +0 -3
  84. package/src/decode/user.ts +7 -1
  85. package/src/driftClient.ts +465 -52
  86. package/src/driftClientConfig.ts +15 -8
  87. package/src/idl/drift.json +246 -23
  88. package/src/index.ts +4 -0
  89. package/src/margin/README.md +143 -0
  90. package/src/marginCalculation.ts +306 -0
  91. package/src/math/margin.ts +13 -1
  92. package/src/math/position.ts +12 -2
  93. package/src/math/spotPosition.ts +6 -2
  94. package/src/types.ts +16 -0
  95. package/src/user.ts +623 -81
  96. package/tests/amm/test.ts +1 -1
  97. package/tests/dlob/helpers.ts +6 -3
  98. package/tests/user/getMarginCalculation.ts +405 -0
  99. package/tests/user/test.ts +0 -7
@@ -5,7 +5,7 @@ import {
5
5
  PublicKey,
6
6
  TransactionVersion,
7
7
  } from '@solana/web3.js';
8
- import { IWallet, TxParams } from './types';
8
+ import { IWallet, TxParams, UserAccount } from './types';
9
9
  import { OracleInfo } from './oracles/types';
10
10
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
11
11
  import { DriftEnv } from './config';
@@ -22,6 +22,9 @@ import { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscri
22
22
  import { grpcDriftClientAccountSubscriberV2 } from './accounts/grpcDriftClientAccountSubscriberV2';
23
23
  import { grpcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
24
24
  import { grpcMultiUserAccountSubscriber } from './accounts/grpcMultiUserAccountSubscriber';
25
+ import { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber';
26
+ import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber';
27
+ import { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDriftClientAccountSubscriberV2';
25
28
 
26
29
  export type DriftClientConfig = {
27
30
  connection: Connection;
@@ -78,6 +81,7 @@ export type DriftClientSubscriptionConfig =
78
81
  resubTimeoutMs?: number;
79
82
  logResubMessages?: boolean;
80
83
  commitment?: Commitment;
84
+ programUserAccountSubscriber?: WebSocketProgramAccountSubscriber<UserAccount>;
81
85
  perpMarketAccountSubscriber?: new (
82
86
  accountName: string,
83
87
  program: Program,
@@ -86,14 +90,17 @@ export type DriftClientSubscriptionConfig =
86
90
  resubOpts?: ResubOpts,
87
91
  commitment?: Commitment
88
92
  ) => WebSocketAccountSubscriberV2<any> | WebSocketAccountSubscriber<any>;
89
- oracleAccountSubscriber?: new (
90
- accountName: string,
93
+ /** If you use V2 here, whatever you pass for perpMarketAccountSubscriber will be ignored and it will use v2 under the hood regardless */
94
+ driftClientAccountSubscriber?: new (
91
95
  program: Program,
92
- accountPublicKey: PublicKey,
93
- decodeBuffer?: (buffer: Buffer) => any,
94
- resubOpts?: ResubOpts,
95
- commitment?: Commitment
96
- ) => WebSocketAccountSubscriberV2<any> | WebSocketAccountSubscriber<any>;
96
+ perpMarketIndexes: number[],
97
+ spotMarketIndexes: number[],
98
+ oracleInfos: OracleInfo[],
99
+ shouldFindAllMarketsAndOracles: boolean,
100
+ delistedMarketSetting: DelistedMarketSetting
101
+ ) =>
102
+ | WebSocketDriftClientAccountSubscriber
103
+ | WebSocketDriftClientAccountSubscriberV2;
97
104
  }
98
105
  | {
99
106
  type: 'polling';
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.145.0",
2
+ "version": "2.145.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -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": [
@@ -4453,6 +4610,37 @@
4453
4610
  ],
4454
4611
  "args": []
4455
4612
  },
4613
+ {
4614
+ "name": "resizeAmmCache",
4615
+ "accounts": [
4616
+ {
4617
+ "name": "admin",
4618
+ "isMut": true,
4619
+ "isSigner": true
4620
+ },
4621
+ {
4622
+ "name": "state",
4623
+ "isMut": false,
4624
+ "isSigner": false
4625
+ },
4626
+ {
4627
+ "name": "ammCache",
4628
+ "isMut": true,
4629
+ "isSigner": false
4630
+ },
4631
+ {
4632
+ "name": "rent",
4633
+ "isMut": false,
4634
+ "isSigner": false
4635
+ },
4636
+ {
4637
+ "name": "systemProgram",
4638
+ "isMut": false,
4639
+ "isSigner": false
4640
+ }
4641
+ ],
4642
+ "args": []
4643
+ },
4456
4644
  {
4457
4645
  "name": "updateInitialAmmCacheInfo",
4458
4646
  "accounts": [
@@ -14599,13 +14787,13 @@
14599
14787
  "type": "u64"
14600
14788
  },
14601
14789
  {
14602
- "name": "lastBaseAssetAmountPerLp",
14790
+ "name": "isolatedPositionScaledBalance",
14603
14791
  "docs": [
14604
14792
  "The last base asset amount per lp the amm had",
14605
14793
  "Used to settle the users lp position",
14606
- "precision: BASE_PRECISION"
14794
+ "precision: SPOT_BALANCE_PRECISION"
14607
14795
  ],
14608
- "type": "i64"
14796
+ "type": "u64"
14609
14797
  },
14610
14798
  {
14611
14799
  "name": "lastQuoteAssetAmountPerLp",
@@ -14644,8 +14832,8 @@
14644
14832
  "type": "u8"
14645
14833
  },
14646
14834
  {
14647
- "name": "perLpBase",
14648
- "type": "i8"
14835
+ "name": "positionFlag",
14836
+ "type": "u8"
14649
14837
  }
14650
14838
  ]
14651
14839
  }
@@ -15118,6 +15306,9 @@
15118
15306
  },
15119
15307
  {
15120
15308
  "name": "SafeMMOracle"
15309
+ },
15310
+ {
15311
+ "name": "Margin"
15121
15312
  }
15122
15313
  ]
15123
15314
  }
@@ -15322,6 +15513,17 @@
15322
15513
  ]
15323
15514
  }
15324
15515
  },
15516
+ {
15517
+ "name": "LiquidationBitFlag",
15518
+ "type": {
15519
+ "kind": "enum",
15520
+ "variants": [
15521
+ {
15522
+ "name": "IsolatedPosition"
15523
+ }
15524
+ ]
15525
+ }
15526
+ },
15325
15527
  {
15326
15528
  "name": "SettlePnlExplanation",
15327
15529
  "type": {
@@ -15451,13 +15653,7 @@
15451
15653
  "kind": "enum",
15452
15654
  "variants": [
15453
15655
  {
15454
- "name": "Standard",
15455
- "fields": [
15456
- {
15457
- "name": "trackOpenOrdersFraction",
15458
- "type": "bool"
15459
- }
15460
- ]
15656
+ "name": "Standard"
15461
15657
  },
15462
15658
  {
15463
15659
  "name": "Liquidation",
@@ -16096,6 +16292,23 @@
16096
16292
  ]
16097
16293
  }
16098
16294
  },
16295
+ {
16296
+ "name": "PositionFlag",
16297
+ "type": {
16298
+ "kind": "enum",
16299
+ "variants": [
16300
+ {
16301
+ "name": "IsolatedPosition"
16302
+ },
16303
+ {
16304
+ "name": "BeingLiquidated"
16305
+ },
16306
+ {
16307
+ "name": "Bankrupt"
16308
+ }
16309
+ ]
16310
+ }
16311
+ },
16099
16312
  {
16100
16313
  "name": "ReferrerStatus",
16101
16314
  "type": {
@@ -17060,6 +17273,11 @@
17060
17273
  "defined": "SpotBankruptcyRecord"
17061
17274
  },
17062
17275
  "index": false
17276
+ },
17277
+ {
17278
+ "name": "bitFlags",
17279
+ "type": "u8",
17280
+ "index": false
17063
17281
  }
17064
17282
  ]
17065
17283
  },
@@ -18496,8 +18714,8 @@
18496
18714
  },
18497
18715
  {
18498
18716
  "code": 6094,
18499
- "name": "CantUpdatePoolBalanceType",
18500
- "msg": "CantUpdatePoolBalanceType"
18717
+ "name": "CantUpdateSpotBalanceType",
18718
+ "msg": "CantUpdateSpotBalanceType"
18501
18719
  },
18502
18720
  {
18503
18721
  "code": 6095,
@@ -19611,41 +19829,46 @@
19611
19829
  },
19612
19830
  {
19613
19831
  "code": 6317,
19832
+ "name": "InvalidIsolatedPerpMarket",
19833
+ "msg": "Invalid Isolated Perp Market"
19834
+ },
19835
+ {
19836
+ "code": 6318,
19614
19837
  "name": "InvalidRevenueShareResize",
19615
19838
  "msg": "Invalid RevenueShare resize"
19616
19839
  },
19617
19840
  {
19618
- "code": 6318,
19841
+ "code": 6319,
19619
19842
  "name": "BuilderRevoked",
19620
19843
  "msg": "Builder has been revoked"
19621
19844
  },
19622
19845
  {
19623
- "code": 6319,
19846
+ "code": 6320,
19624
19847
  "name": "InvalidBuilderFee",
19625
19848
  "msg": "Builder fee is greater than max fee bps"
19626
19849
  },
19627
19850
  {
19628
- "code": 6320,
19851
+ "code": 6321,
19629
19852
  "name": "RevenueShareEscrowAuthorityMismatch",
19630
19853
  "msg": "RevenueShareEscrow authority mismatch"
19631
19854
  },
19632
19855
  {
19633
- "code": 6321,
19856
+ "code": 6322,
19634
19857
  "name": "RevenueShareEscrowOrdersAccountFull",
19635
19858
  "msg": "RevenueShareEscrow has too many active orders"
19636
19859
  },
19637
19860
  {
19638
- "code": 6322,
19861
+ "code": 6323,
19639
19862
  "name": "InvalidRevenueShareAccount",
19640
19863
  "msg": "Invalid RevenueShareAccount"
19641
19864
  },
19642
19865
  {
19643
- "code": 6323,
19866
+ "code": 6324,
19644
19867
  "name": "CannotRevokeBuilderWithOpenOrders",
19645
19868
  "msg": "Cannot revoke builder with open orders"
19646
19869
  },
19647
19870
  {
19648
- "code": 6324,
19871
+ "code": 6325,
19649
19872
  "name": "UnableToLoadRevenueShareAccount",
19650
19873
  "msg": "Unable to load builder account"
19651
19874
  },
@@ -19745,4 +19968,4 @@
19745
19968
  "msg": "Invalid Lp Pool Id for Operation"
19746
19969
  }
19747
19970
  ]
19748
- }
19971
+ }
package/src/index.ts CHANGED
@@ -12,6 +12,10 @@ export * from './accounts/webSocketDriftClientAccountSubscriber';
12
12
  export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber';
13
13
  export * from './accounts/webSocketHighLeverageModeConfigAccountSubscriber';
14
14
  export { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2';
15
+ export { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber';
16
+ export { WebSocketProgramUserAccountSubscriber } from './accounts/websocketProgramUserAccountSubscriber';
17
+ export { WebSocketProgramAccountsSubscriberV2 } from './accounts/webSocketProgramAccountsSubscriberV2';
18
+ export { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDriftClientAccountSubscriberV2';
15
19
  export * from './accounts/bulkAccountLoader';
16
20
  export * from './accounts/bulkUserSubscription';
17
21
  export * from './accounts/bulkUserStatsSubscription';
@@ -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
+