@velocity-exchange/sdk 0.4.0 → 0.6.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.
Files changed (323) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/adminClient.d.ts +35 -0
  3. package/lib/browser/adminClient.js +65 -0
  4. package/lib/browser/config.js +6 -7
  5. package/lib/browser/constants/numericConstants.d.ts +1 -1
  6. package/lib/browser/constants/numericConstants.js +1 -1
  7. package/lib/browser/decode/user.js +5 -1
  8. package/lib/browser/idl/velocity.d.ts +146 -3
  9. package/lib/browser/idl/velocity.json +147 -4
  10. package/lib/browser/orderSubscriber/OrderSubscriber.js +3 -0
  11. package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
  12. package/lib/browser/types.d.ts +4 -0
  13. package/lib/browser/user.d.ts +15 -0
  14. package/lib/browser/user.js +37 -1
  15. package/lib/browser/velocityClient.d.ts +46 -10
  16. package/lib/browser/velocityClient.js +107 -35
  17. package/lib/node/adminClient.d.ts +35 -0
  18. package/lib/node/adminClient.d.ts.map +1 -1
  19. package/lib/node/adminClient.js +65 -0
  20. package/lib/node/config.d.ts.map +1 -1
  21. package/lib/node/config.js +6 -7
  22. package/lib/node/constants/numericConstants.d.ts +1 -1
  23. package/lib/node/constants/numericConstants.d.ts.map +1 -1
  24. package/lib/node/constants/numericConstants.js +1 -1
  25. package/lib/node/decode/user.d.ts.map +1 -1
  26. package/lib/node/decode/user.js +5 -1
  27. package/lib/node/idl/velocity.d.ts +146 -3
  28. package/lib/node/idl/velocity.d.ts.map +1 -1
  29. package/lib/node/idl/velocity.json +147 -4
  30. package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
  31. package/lib/node/orderSubscriber/OrderSubscriber.js +3 -0
  32. package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
  33. package/lib/node/pyth/pythLazerSubscriber.js +37 -27
  34. package/lib/node/types.d.ts +4 -0
  35. package/lib/node/types.d.ts.map +1 -1
  36. package/lib/node/user.d.ts +15 -0
  37. package/lib/node/user.d.ts.map +1 -1
  38. package/lib/node/user.js +37 -1
  39. package/lib/node/velocityClient.d.ts +46 -10
  40. package/lib/node/velocityClient.d.ts.map +1 -1
  41. package/lib/node/velocityClient.js +107 -35
  42. package/package.json +7 -3
  43. package/.prettierignore +0 -1
  44. package/CHANGELOG.md +0 -132
  45. package/build-browser.js +0 -58
  46. package/bunfig.toml +0 -3
  47. package/esbuild-shims.js +0 -12
  48. package/scripts/deposit-isolated-positions.ts +0 -110
  49. package/scripts/find-flagged-users.ts +0 -213
  50. package/scripts/grpc-client-test-comparison.ts +0 -372
  51. package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
  52. package/scripts/postbuild.js +0 -95
  53. package/scripts/single-grpc-client-test.ts +0 -284
  54. package/scripts/withdraw-isolated-positions.ts +0 -174
  55. package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
  56. package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
  57. package/src/accounts/basicUserAccountSubscriber.ts +0 -79
  58. package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
  59. package/src/accounts/bulkAccountLoader.ts +0 -358
  60. package/src/accounts/bulkUserStatsSubscription.ts +0 -40
  61. package/src/accounts/bulkUserSubscription.ts +0 -39
  62. package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
  63. package/src/accounts/fetch.ts +0 -200
  64. package/src/accounts/grpcAccountSubscriber.ts +0 -225
  65. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
  66. package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
  67. package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
  68. package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
  69. package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
  70. package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
  71. package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
  72. package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
  73. package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
  74. package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
  75. package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
  76. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
  77. package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
  78. package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
  79. package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
  80. package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
  81. package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
  82. package/src/accounts/testBulkAccountLoader.ts +0 -59
  83. package/src/accounts/types.ts +0 -428
  84. package/src/accounts/utils.ts +0 -90
  85. package/src/accounts/webSocketAccountSubscriber.ts +0 -304
  86. package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
  87. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
  88. package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
  89. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
  90. package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
  91. package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
  92. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
  93. package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
  94. package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
  95. package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
  96. package/src/addresses/marketAddresses.ts +0 -28
  97. package/src/addresses/pda.ts +0 -620
  98. package/src/adminClient.ts +0 -8136
  99. package/src/assert/assert.ts +0 -5
  100. package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
  101. package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
  102. package/src/auctionSubscriber/index.ts +0 -9
  103. package/src/auctionSubscriber/types.ts +0 -30
  104. package/src/bankrun/bankrunConnection.ts +0 -630
  105. package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
  106. package/src/blockhashSubscriber/index.ts +0 -6
  107. package/src/blockhashSubscriber/types.ts +0 -13
  108. package/src/clock/clockSubscriber.ts +0 -146
  109. package/src/config.ts +0 -312
  110. package/src/constants/index.ts +0 -9
  111. package/src/constants/numericConstants.ts +0 -194
  112. package/src/constants/perpMarkets.ts +0 -127
  113. package/src/constants/spotMarkets.ts +0 -104
  114. package/src/constants/txConstants.ts +0 -2
  115. package/src/constituentMap/constituentMap.ts +0 -305
  116. package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
  117. package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
  118. package/src/core/VelocityCore.ts +0 -606
  119. package/src/core/index.ts +0 -12
  120. package/src/core/instructions/deposit.ts +0 -64
  121. package/src/core/instructions/fill.ts +0 -51
  122. package/src/core/instructions/funding.ts +0 -33
  123. package/src/core/instructions/liquidation.ts +0 -55
  124. package/src/core/instructions/orders.ts +0 -84
  125. package/src/core/instructions/perpOrders.ts +0 -312
  126. package/src/core/instructions/settlement.ts +0 -40
  127. package/src/core/instructions/trigger.ts +0 -41
  128. package/src/core/instructions/withdraw.ts +0 -65
  129. package/src/core/remainingAccounts.ts +0 -320
  130. package/src/core/signedMsg.ts +0 -109
  131. package/src/decode/customCoder.ts +0 -148
  132. package/src/decode/user.ts +0 -401
  133. package/src/dlob/DLOB.ts +0 -2793
  134. package/src/dlob/DLOBNode.ts +0 -305
  135. package/src/dlob/DLOBSubscriber.ts +0 -269
  136. package/src/dlob/NodeList.ts +0 -244
  137. package/src/dlob/orderBookLevels.ts +0 -695
  138. package/src/dlob/types.ts +0 -37
  139. package/src/events/eventList.ts +0 -114
  140. package/src/events/eventSubscriber.ts +0 -521
  141. package/src/events/eventsServerLogProvider.ts +0 -189
  142. package/src/events/fetchLogs.ts +0 -219
  143. package/src/events/parse.ts +0 -271
  144. package/src/events/pollingLogProvider.ts +0 -113
  145. package/src/events/sort.ts +0 -51
  146. package/src/events/txEventCache.ts +0 -92
  147. package/src/events/types.ts +0 -337
  148. package/src/events/webSocketLogProvider.ts +0 -152
  149. package/src/factory/bigNum.ts +0 -709
  150. package/src/factory/oracleClient.ts +0 -73
  151. package/src/idl/pyth.json +0 -142
  152. package/src/idl/pyth.ts +0 -97
  153. package/src/idl/pyth_solana_receiver.json +0 -628
  154. package/src/idl/token_faucet.json +0 -229
  155. package/src/idl/token_faucet.ts +0 -197
  156. package/src/idl/velocity.json +0 -23974
  157. package/src/idl/velocity.ts +0 -23980
  158. package/src/index.ts +0 -150
  159. package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
  160. package/src/isomorphic/README.md +0 -19
  161. package/src/isomorphic/anchor.browser.ts +0 -44
  162. package/src/isomorphic/anchor.node.ts +0 -1
  163. package/src/isomorphic/anchor.ts +0 -1
  164. package/src/isomorphic/anchor29.browser.ts +0 -24
  165. package/src/isomorphic/anchor29.node.ts +0 -1
  166. package/src/isomorphic/anchor29.ts +0 -1
  167. package/src/isomorphic/grpc.browser.ts +0 -4
  168. package/src/isomorphic/grpc.node.ts +0 -130
  169. package/src/isomorphic/grpc.ts +0 -1
  170. package/src/jupiter/jupiterClient.ts +0 -502
  171. package/src/keypair.ts +0 -32
  172. package/src/margin/README.md +0 -138
  173. package/src/marginCalculation.ts +0 -407
  174. package/src/marinade/idl/idl.json +0 -1962
  175. package/src/marinade/index.ts +0 -73
  176. package/src/marinade/types.ts +0 -3925
  177. package/src/math/amm.ts +0 -1758
  178. package/src/math/auction.ts +0 -480
  179. package/src/math/bankruptcy.ts +0 -138
  180. package/src/math/builder.ts +0 -130
  181. package/src/math/conversion.ts +0 -45
  182. package/src/math/exchangeStatus.ts +0 -261
  183. package/src/math/funding.ts +0 -498
  184. package/src/math/insurance.ts +0 -171
  185. package/src/math/liquidation.ts +0 -433
  186. package/src/math/margin.ts +0 -327
  187. package/src/math/market.ts +0 -487
  188. package/src/math/oracles.ts +0 -477
  189. package/src/math/orders.ts +0 -500
  190. package/src/math/position.ts +0 -381
  191. package/src/math/repeg.ts +0 -223
  192. package/src/math/spotBalance.ts +0 -996
  193. package/src/math/spotMarket.ts +0 -115
  194. package/src/math/spotPosition.ts +0 -263
  195. package/src/math/state.ts +0 -71
  196. package/src/math/superStake.ts +0 -602
  197. package/src/math/tiers.ts +0 -73
  198. package/src/math/trade.ts +0 -879
  199. package/src/math/utils.ts +0 -183
  200. package/src/memcmp.ts +0 -279
  201. package/src/oracles/oracleClientCache.ts +0 -41
  202. package/src/oracles/oracleId.ts +0 -99
  203. package/src/oracles/prelaunchOracleClient.ts +0 -63
  204. package/src/oracles/pythClient.ts +0 -125
  205. package/src/oracles/pythLazerClient.ts +0 -141
  206. package/src/oracles/quoteAssetOracleClient.ts +0 -43
  207. package/src/oracles/strictOraclePrice.ts +0 -40
  208. package/src/oracles/types.ts +0 -82
  209. package/src/oracles/utils.ts +0 -45
  210. package/src/orderParams.ts +0 -117
  211. package/src/orderSubscriber/OrderSubscriber.ts +0 -378
  212. package/src/orderSubscriber/PollingSubscription.ts +0 -50
  213. package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
  214. package/src/orderSubscriber/grpcSubscription.ts +0 -155
  215. package/src/orderSubscriber/index.ts +0 -7
  216. package/src/orderSubscriber/types.ts +0 -64
  217. package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
  218. package/src/priorityFee/averageStrategy.ts +0 -13
  219. package/src/priorityFee/ewmaStrategy.ts +0 -51
  220. package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
  221. package/src/priorityFee/index.ts +0 -16
  222. package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
  223. package/src/priorityFee/maxStrategy.ts +0 -8
  224. package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
  225. package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
  226. package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
  227. package/src/priorityFee/types.ts +0 -82
  228. package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
  229. package/src/pyth/constants.ts +0 -9
  230. package/src/pyth/index.ts +0 -15
  231. package/src/pyth/pythLazerSubscriber.ts +0 -365
  232. package/src/pyth/types.ts +0 -4451
  233. package/src/pyth/utils.ts +0 -13
  234. package/src/slot/SlotSubscriber.ts +0 -126
  235. package/src/slot/SlothashSubscriber.ts +0 -163
  236. package/src/swap/UnifiedSwapClient.ts +0 -315
  237. package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
  238. package/src/swift/index.ts +0 -10
  239. package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
  240. package/src/swift/swiftOrderSubscriber.ts +0 -351
  241. package/src/testClient.ts +0 -42
  242. package/src/titan/titanClient.ts +0 -438
  243. package/src/token/index.ts +0 -13
  244. package/src/tokenFaucet.ts +0 -296
  245. package/src/tx/baseTxSender.ts +0 -651
  246. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
  247. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
  248. package/src/tx/blockhashFetcher/types.ts +0 -9
  249. package/src/tx/fastSingleTxSender.ts +0 -200
  250. package/src/tx/forwardOnlyTxSender.ts +0 -193
  251. package/src/tx/priorityFeeCalculator.ts +0 -117
  252. package/src/tx/reportTransactionError.ts +0 -176
  253. package/src/tx/retryTxSender.ts +0 -177
  254. package/src/tx/txHandler.ts +0 -1009
  255. package/src/tx/txParamProcessor.ts +0 -225
  256. package/src/tx/types.ts +0 -117
  257. package/src/tx/utils.ts +0 -133
  258. package/src/tx/whileValidTxSender.ts +0 -377
  259. package/src/types.ts +0 -2575
  260. package/src/user.ts +0 -5316
  261. package/src/userConfig.ts +0 -38
  262. package/src/userMap/PollingSubscription.ts +0 -61
  263. package/src/userMap/WebsocketSubscription.ts +0 -93
  264. package/src/userMap/grpcSubscription.ts +0 -93
  265. package/src/userMap/referrerMap.ts +0 -360
  266. package/src/userMap/revenueShareEscrowMap.ts +0 -342
  267. package/src/userMap/userMap.ts +0 -784
  268. package/src/userMap/userMapConfig.ts +0 -82
  269. package/src/userMap/userStatsMap.ts +0 -393
  270. package/src/userName.ts +0 -37
  271. package/src/userStats.ts +0 -159
  272. package/src/userStatsConfig.ts +0 -35
  273. package/src/util/TransactionConfirmationManager.ts +0 -333
  274. package/src/util/chainClock.ts +0 -65
  275. package/src/util/computeUnits.ts +0 -101
  276. package/src/util/digest.ts +0 -33
  277. package/src/util/ed25519Utils.ts +0 -95
  278. package/src/util/promiseTimeout.ts +0 -23
  279. package/src/velocityClient.ts +0 -14802
  280. package/src/velocityClientConfig.ts +0 -119
  281. package/src/wallet.ts +0 -91
  282. package/tests/VelocityCore/builder_escrow.test.ts +0 -121
  283. package/tests/VelocityCore/decode.test.ts +0 -16
  284. package/tests/VelocityCore/fill_trigger.test.ts +0 -73
  285. package/tests/VelocityCore/instructions.test.ts +0 -46
  286. package/tests/VelocityCore/pdas.test.ts +0 -30
  287. package/tests/VelocityCore/perp_orders.test.ts +0 -205
  288. package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
  289. package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
  290. package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
  291. package/tests/amm/test.ts +0 -2402
  292. package/tests/auctions/test.ts +0 -81
  293. package/tests/bn/test.ts +0 -355
  294. package/tests/builder/builderFee.test.ts +0 -42
  295. package/tests/ci/verifyConstants.ts +0 -393
  296. package/tests/decode/test.ts +0 -262
  297. package/tests/decode/userAccountBufferStrings.ts +0 -102
  298. package/tests/dlob/helpers.ts +0 -748
  299. package/tests/dlob/test.ts +0 -6969
  300. package/tests/dlob/tickSizeStandardization.ts +0 -545
  301. package/tests/events/parseLogsForCuUsage.ts +0 -139
  302. package/tests/exchangeStatus/test.ts +0 -45
  303. package/tests/insurance/test.ts +0 -40
  304. package/tests/liquidation/test.ts +0 -125
  305. package/tests/oracles/mmOracleGate.test.ts +0 -379
  306. package/tests/oracles/pythPegSnap.test.ts +0 -76
  307. package/tests/sdkParity/enumParity.test.ts +0 -84
  308. package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
  309. package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
  310. package/tests/spot/test.ts +0 -281
  311. package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
  312. package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
  313. package/tests/tx/priorityFeeCalculator.ts +0 -77
  314. package/tests/tx/priorityFeeStrategy.ts +0 -95
  315. package/tests/user/bankruptcy.ts +0 -165
  316. package/tests/user/feeAndWithdrawLimits.ts +0 -284
  317. package/tests/user/getMarginCalculation.ts +0 -586
  318. package/tests/user/helpers.ts +0 -184
  319. package/tests/user/liquidations.ts +0 -129
  320. package/tests/user/marginCalculations.test.ts +0 -321
  321. package/tests/user/test.ts +0 -833
  322. package/tsconfig.browser.json +0 -18
  323. package/tsconfig.json +0 -36
package/src/math/trade.ts DELETED
@@ -1,879 +0,0 @@
1
- import {
2
- MarketType,
3
- PerpMarketAccount,
4
- PositionDirection,
5
- UserStatsAccount,
6
- } from '../types';
7
- import { BN } from '../isomorphic/anchor';
8
- import { assert } from '../assert/assert';
9
- import {
10
- PRICE_PRECISION,
11
- PEG_PRECISION,
12
- AMM_TO_QUOTE_PRECISION_RATIO,
13
- ZERO,
14
- BASE_PRECISION,
15
- BN_MAX,
16
- } from '../constants/numericConstants';
17
- import {
18
- calculateBidPrice,
19
- calculateAskPrice,
20
- calculateReservePrice,
21
- } from './market';
22
- import {
23
- calculateAmmReservesAfterSwap,
24
- calculatePrice,
25
- getSwapDirection,
26
- AssetType,
27
- calculateUpdatedAMMSpreadReserves,
28
- calculateQuoteAssetAmountSwapped,
29
- calculateMarketOpenBidAsk,
30
- } from './amm';
31
- import { squareRootBN } from './utils';
32
- import { isVariant } from '../types';
33
- import { MMOraclePriceData } from '../oracles/types';
34
- import { DLOB } from '../dlob/DLOB';
35
- import { PublicKey } from '@solana/web3.js';
36
- import { L2OrderBook } from '../dlob/orderBookLevels';
37
-
38
- const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
39
-
40
- /**
41
- * Enumerates the price-impact-related fields historically produced by trade-slippage helpers.
42
- * Not currently consumed as a parameter/return type by any function in this file — kept for
43
- * backward compatibility with callers that reference it as a key type.
44
- */
45
- export type PriceImpactUnit =
46
- | 'entryPrice'
47
- | 'maxPrice'
48
- | 'priceDelta'
49
- | 'priceDeltaAsNumber'
50
- | 'pctAvg'
51
- | 'pctMax'
52
- | 'quoteAssetAmount'
53
- | 'quoteAssetAmountPeg'
54
- | 'acquiredBaseAssetAmount'
55
- | 'acquiredQuoteAssetAmount'
56
- | 'all';
57
-
58
- /**
59
- * Calculates avg/max slippage (price impact) for a hypothetical AMM-only trade.
60
- *
61
- * @deprecated Use `calculateEstimatedPerpEntryPrice` instead (this ignores DLOB liquidity and
62
- * only swaps against the vAMM).
63
- *
64
- * @param {PositionDirection} direction - Taker's trade direction
65
- * @param {BN} amount - Trade size in `inputAssetType` units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6))
66
- * @param {PerpMarketAccount} market - The perp market account
67
- * @param {AssetType} [inputAssetType] - Whether `amount` denominates base or quote; defaults to `'quote'`
68
- * @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used for spread reserve calc
69
- * @param {boolean} [useSpread] - Whether to consider the bid/ask spread when computing slippage; defaults to `true`
70
- * @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
71
- * @return {[BN, BN, BN, BN]} `[pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]`, all
72
- * PRICE_PRECISION (1e6): `pctAvgSlippage` is the percentage change from the pre-trade price to
73
- * `entryPrice` (average execution slippage); `pctMaxSlippage` is the percentage change from the
74
- * pre-trade price to `newPrice` (worst-case/marginal slippage); `entryPrice` is the trade's
75
- * average execution price; `newPrice` is the AMM's price after the trade
76
- */
77
- export function calculateTradeSlippage(
78
- direction: PositionDirection,
79
- amount: BN,
80
- market: PerpMarketAccount,
81
- inputAssetType: AssetType = 'quote',
82
- mmOraclePriceData: MMOraclePriceData,
83
- useSpread = true,
84
- latestSlot?: BN
85
- ): [BN, BN, BN, BN] {
86
- let oldPrice: BN;
87
-
88
- if (useSpread && market.amm.baseSpread > 0) {
89
- if (isVariant(direction, 'long')) {
90
- oldPrice = calculateAskPrice(market, mmOraclePriceData);
91
- } else {
92
- oldPrice = calculateBidPrice(market, mmOraclePriceData);
93
- }
94
- } else {
95
- oldPrice = calculateReservePrice(market, mmOraclePriceData);
96
- }
97
- if (amount.eq(ZERO)) {
98
- return [ZERO, ZERO, oldPrice, oldPrice];
99
- }
100
- const [acquiredBaseReserve, acquiredQuoteReserve, acquiredQuoteAssetAmount] =
101
- calculateTradeAcquiredAmounts(
102
- direction,
103
- amount,
104
- market,
105
- inputAssetType,
106
- mmOraclePriceData,
107
- useSpread
108
- );
109
-
110
- const entryPrice = acquiredQuoteAssetAmount
111
- .mul(AMM_TO_QUOTE_PRECISION_RATIO)
112
- .mul(PRICE_PRECISION)
113
- .div(acquiredBaseReserve.abs());
114
-
115
- let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
116
- if (useSpread && market.amm.baseSpread > 0) {
117
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
118
- calculateUpdatedAMMSpreadReserves(
119
- market.amm,
120
- market.marketStats,
121
- direction,
122
- mmOraclePriceData,
123
- latestSlot
124
- );
125
- amm = {
126
- baseAssetReserve,
127
- quoteAssetReserve,
128
- sqrtK: sqrtK,
129
- pegMultiplier: newPeg,
130
- };
131
- } else {
132
- amm = market.amm;
133
- }
134
-
135
- const newPrice = calculatePrice(
136
- amm.baseAssetReserve.sub(acquiredBaseReserve),
137
- amm.quoteAssetReserve.sub(acquiredQuoteReserve),
138
- amm.pegMultiplier
139
- );
140
-
141
- if (direction == PositionDirection.SHORT) {
142
- assert(newPrice.lte(oldPrice));
143
- } else {
144
- assert(oldPrice.lte(newPrice));
145
- }
146
-
147
- const pctMaxSlippage = newPrice
148
- .sub(oldPrice)
149
- .mul(PRICE_PRECISION)
150
- .div(oldPrice)
151
- .abs();
152
- const pctAvgSlippage = entryPrice
153
- .sub(oldPrice)
154
- .mul(PRICE_PRECISION)
155
- .div(oldPrice)
156
- .abs();
157
-
158
- return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice];
159
- }
160
-
161
- /**
162
- * Calculates the AMM reserve deltas and resulting quote amount for a hypothetical constant-product
163
- * swap against the vAMM, without executing anything on-chain.
164
- *
165
- * @param {PositionDirection} direction - Taker's trade direction
166
- * @param {BN} amount - Trade size in `inputAssetType` units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6))
167
- * @param {PerpMarketAccount} market - The perp market account
168
- * @param {AssetType} [inputAssetType] - Whether `amount` denominates base or quote; defaults to `'quote'`
169
- * @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used for spread reserve calc
170
- * @param {boolean} [useSpread] - Whether to swap against the spread-adjusted reserves (bid/ask)
171
- * rather than the raw reserves; defaults to `true`
172
- * @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
173
- * @return {[BN, BN, BN]} `[acquiredBase, acquiredQuote, acquiredQuoteAssetAmount]` — the change
174
- * in the AMM's base and quote reserves (signed, `AMM_RESERVE_PRECISION` (1e9)), and the
175
- * resulting user-facing quote amount swapped, `QUOTE_PRECISION` (1e6)
176
- */
177
- export function calculateTradeAcquiredAmounts(
178
- direction: PositionDirection,
179
- amount: BN,
180
- market: PerpMarketAccount,
181
- inputAssetType: AssetType = 'quote',
182
- mmOraclePriceData: MMOraclePriceData,
183
- useSpread = true,
184
- latestSlot?: BN
185
- ): [BN, BN, BN] {
186
- if (amount.eq(ZERO)) {
187
- return [ZERO, ZERO, ZERO];
188
- }
189
-
190
- const swapDirection = getSwapDirection(inputAssetType, direction);
191
-
192
- let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
193
- if (useSpread && market.amm.baseSpread > 0) {
194
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
195
- calculateUpdatedAMMSpreadReserves(
196
- market.amm,
197
- market.marketStats,
198
- direction,
199
- mmOraclePriceData,
200
- latestSlot
201
- );
202
- amm = {
203
- baseAssetReserve,
204
- quoteAssetReserve,
205
- sqrtK: sqrtK,
206
- pegMultiplier: newPeg,
207
- };
208
- } else {
209
- amm = market.amm;
210
- }
211
-
212
- const [newQuoteAssetReserve, newBaseAssetReserve] =
213
- calculateAmmReservesAfterSwap(amm, inputAssetType, amount, swapDirection);
214
-
215
- const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
216
- const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
217
- const acquiredQuoteAssetAmount = calculateQuoteAssetAmountSwapped(
218
- acquiredQuote.abs(),
219
- amm.pegMultiplier,
220
- swapDirection
221
- );
222
-
223
- return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
224
- }
225
-
226
- /**
227
- * Calculates the AMM-only trade (direction + size) required to push the market's reserve price
228
- * to (or `pct` of the way to) `targetPrice` — a simple arbitrage-sizing helper.
229
- *
230
- * @deprecated No longer actively maintained; ignores DLOB liquidity.
231
- *
232
- * @param {PerpMarketAccount} market - The perp market account
233
- * @param {BN} targetPrice - The price to arbitrage toward, PRICE_PRECISION (1e6)
234
- * @param {BN} [pct] - Fraction of the full price gap to close, out of `MAXPCT` (1000 = 100%);
235
- * defaults to fully closing the gap
236
- * @param {AssetType} [outputAssetType] - Whether the returned trade size is denominated in base
237
- * or quote; defaults to `'quote'`
238
- * @param {MMOraclePriceData} [mmOraclePriceData] - MM oracle price data used for spread reserve calc
239
- * @param {boolean} [useSpread] - Whether to consider the bid/ask spread when sizing the trade;
240
- * defaults to `true`. If `targetPrice` already sits within the current bid/ask spread, returns
241
- * a zero-size trade
242
- * @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
243
- * @return {[PositionDirection, BN, BN, BN]} `[direction, tradeSize, entryPrice, targetPrice]` —
244
- * `direction` required to move price toward `targetPrice`; `tradeSize` in `outputAssetType`
245
- * units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6)); `entryPrice`/`targetPrice`
246
- * PRICE_PRECISION (1e6)
247
- */
248
- export function calculateTargetPriceTrade(
249
- market: PerpMarketAccount,
250
- targetPrice: BN,
251
- pct: BN = MAXPCT,
252
- outputAssetType: AssetType = 'quote',
253
- mmOraclePriceData?: MMOraclePriceData,
254
- useSpread = true,
255
- latestSlot?: BN
256
- ): [PositionDirection, BN, BN, BN] {
257
- assert(market.amm.baseAssetReserve.gt(ZERO));
258
- assert(targetPrice.gt(ZERO));
259
- assert(pct.lte(MAXPCT) && pct.gt(ZERO));
260
-
261
- const reservePriceBefore = calculateReservePrice(market, mmOraclePriceData);
262
- const bidPriceBefore = calculateBidPrice(market, mmOraclePriceData);
263
- const askPriceBefore = calculateAskPrice(market, mmOraclePriceData);
264
-
265
- let direction;
266
- if (targetPrice.gt(reservePriceBefore)) {
267
- const priceGap = targetPrice.sub(reservePriceBefore);
268
- const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
269
- targetPrice = reservePriceBefore.add(priceGapScaled);
270
- direction = PositionDirection.LONG;
271
- } else {
272
- const priceGap = reservePriceBefore.sub(targetPrice);
273
- const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
274
- targetPrice = reservePriceBefore.sub(priceGapScaled);
275
- direction = PositionDirection.SHORT;
276
- }
277
-
278
- let tradeSize;
279
- let baseSize;
280
-
281
- let baseAssetReserveBefore: BN;
282
- let quoteAssetReserveBefore: BN;
283
-
284
- let peg = market.amm.pegMultiplier;
285
-
286
- if (useSpread && market.amm.baseSpread > 0) {
287
- const { baseAssetReserve, quoteAssetReserve, newPeg } =
288
- calculateUpdatedAMMSpreadReserves(
289
- market.amm,
290
- market.marketStats,
291
- direction,
292
- mmOraclePriceData,
293
- latestSlot
294
- );
295
- baseAssetReserveBefore = baseAssetReserve;
296
- quoteAssetReserveBefore = quoteAssetReserve;
297
- peg = newPeg;
298
- } else {
299
- baseAssetReserveBefore = market.amm.baseAssetReserve;
300
- quoteAssetReserveBefore = market.amm.quoteAssetReserve;
301
- }
302
-
303
- const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
304
- const k = invariant.mul(PRICE_PRECISION);
305
-
306
- let baseAssetReserveAfter;
307
- let quoteAssetReserveAfter;
308
- const biasModifier = new BN(1);
309
- let markPriceAfter;
310
-
311
- if (
312
- useSpread &&
313
- targetPrice.lt(askPriceBefore) &&
314
- targetPrice.gt(bidPriceBefore)
315
- ) {
316
- // no trade, market is at target
317
- if (reservePriceBefore.gt(targetPrice)) {
318
- direction = PositionDirection.SHORT;
319
- } else {
320
- direction = PositionDirection.LONG;
321
- }
322
- tradeSize = ZERO;
323
- return [direction, tradeSize, targetPrice, targetPrice];
324
- } else if (reservePriceBefore.gt(targetPrice)) {
325
- // overestimate y2
326
- baseAssetReserveAfter = squareRootBN(
327
- k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
328
- ).sub(new BN(1));
329
- quoteAssetReserveAfter = k.div(PRICE_PRECISION).div(baseAssetReserveAfter);
330
-
331
- markPriceAfter = calculatePrice(
332
- baseAssetReserveAfter,
333
- quoteAssetReserveAfter,
334
- peg
335
- );
336
- direction = PositionDirection.SHORT;
337
- tradeSize = quoteAssetReserveBefore
338
- .sub(quoteAssetReserveAfter)
339
- .mul(peg)
340
- .div(PEG_PRECISION)
341
- .div(AMM_TO_QUOTE_PRECISION_RATIO);
342
- baseSize = baseAssetReserveAfter.sub(baseAssetReserveBefore);
343
- } else if (reservePriceBefore.lt(targetPrice)) {
344
- // underestimate y2
345
- baseAssetReserveAfter = squareRootBN(
346
- k.div(targetPrice).mul(peg).div(PEG_PRECISION).add(biasModifier)
347
- ).add(new BN(1));
348
- quoteAssetReserveAfter = k.div(PRICE_PRECISION).div(baseAssetReserveAfter);
349
-
350
- markPriceAfter = calculatePrice(
351
- baseAssetReserveAfter,
352
- quoteAssetReserveAfter,
353
- peg
354
- );
355
-
356
- direction = PositionDirection.LONG;
357
- tradeSize = quoteAssetReserveAfter
358
- .sub(quoteAssetReserveBefore)
359
- .mul(peg)
360
- .div(PEG_PRECISION)
361
- .div(AMM_TO_QUOTE_PRECISION_RATIO);
362
- baseSize = baseAssetReserveBefore.sub(baseAssetReserveAfter);
363
- } else {
364
- // no trade, market is at target
365
- direction = PositionDirection.LONG;
366
- tradeSize = ZERO;
367
- return [direction, tradeSize, targetPrice, targetPrice];
368
- }
369
-
370
- let tp1 = targetPrice;
371
- let tp2 = markPriceAfter;
372
- let originalDiff = targetPrice.sub(reservePriceBefore);
373
-
374
- if (direction == PositionDirection.SHORT) {
375
- tp1 = markPriceAfter;
376
- tp2 = targetPrice;
377
- originalDiff = reservePriceBefore.sub(targetPrice);
378
- }
379
-
380
- const entryPrice = tradeSize
381
- .mul(AMM_TO_QUOTE_PRECISION_RATIO)
382
- .mul(PRICE_PRECISION)
383
- .div(baseSize.abs());
384
-
385
- assert(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
386
- assert(
387
- tp2.lte(tp1) || tp2.sub(tp1).abs().ltn(100000),
388
- 'Target Price Calculation incorrect' +
389
- tp2.toString() +
390
- '>=' +
391
- tp1.toString() +
392
- 'err: ' +
393
- tp2.sub(tp1).abs().toString()
394
- );
395
- if (outputAssetType == 'quote') {
396
- return [direction, tradeSize, entryPrice, targetPrice];
397
- } else {
398
- return [direction, baseSize, entryPrice, targetPrice];
399
- }
400
- }
401
-
402
- /**
403
- * Simulates walking the combined DLOB + vAMM liquidity to estimate the entry price and price
404
- * impact of a hypothetical taker order, filling against resting limit orders and the AMM's
405
- * spread-adjusted reserves in whichever is cheaper at each step. Price impact is the difference
406
- * between the estimated entry price and the best available price (top of book/AMM) before any
407
- * fill.
408
- *
409
- * @param {AssetType} assetType - Whether `amount` denominates base or quote
410
- * @param {BN} amount - Order size, `assetType === 'base'`: BASE_PRECISION (1e9); `'quote'`: QUOTE_PRECISION (1e6)
411
- * @param {PositionDirection} direction - Taker's trade direction
412
- * @param {PerpMarketAccount} market - The perp market account
413
- * @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used to price both the DLOB
414
- * resting orders and the AMM's spread-adjusted reserves
415
- * @param {DLOB} dlob - The order book to walk for resting limit orders
416
- * @param {number} slot - Current slot, used to resolve oracle-pegged/auction limit order prices
417
- * @param {Map<PublicKey, boolean>} [usersToSkip] - Maker user accounts to exclude from the fill
418
- * simulation (e.g. the taker's own resting orders); defaults to none
419
- * @return {{ entryPrice: BN; priceImpact: BN; bestPrice: BN; worstPrice: BN; baseFilled: BN;
420
- * quoteFilled: BN }} `entryPrice`/`bestPrice`/`worstPrice` are PRICE_PRECISION (1e6);
421
- * `priceImpact` is `|entryPrice - bestPrice| / bestPrice`, also scaled by PRICE_PRECISION
422
- * (1e6) but represents a ratio, not a price (e.g. `1e4` = 1% impact); `baseFilled` is
423
- * BASE_PRECISION (1e9); `quoteFilled` is QUOTE_PRECISION (1e6). All-zero only if `amount` is
424
- * zero; if liquidity runs out before `amount` fully fills, the returned fields reflect the
425
- * partial fill
426
- */
427
- export function calculateEstimatedPerpEntryPrice(
428
- assetType: AssetType,
429
- amount: BN,
430
- direction: PositionDirection,
431
- market: PerpMarketAccount,
432
- mmOraclePriceData: MMOraclePriceData,
433
- dlob: DLOB,
434
- slot: number,
435
- usersToSkip = new Map<PublicKey, boolean>()
436
- ): {
437
- entryPrice: BN;
438
- priceImpact: BN;
439
- bestPrice: BN;
440
- worstPrice: BN;
441
- baseFilled: BN;
442
- quoteFilled: BN;
443
- } {
444
- if (amount.eq(ZERO)) {
445
- return {
446
- entryPrice: ZERO,
447
- priceImpact: ZERO,
448
- bestPrice: ZERO,
449
- worstPrice: ZERO,
450
- baseFilled: ZERO,
451
- quoteFilled: ZERO,
452
- };
453
- }
454
-
455
- const takerIsLong = isVariant(direction, 'long');
456
- const limitOrders = dlob[
457
- takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'
458
- ](
459
- market.marketIndex,
460
- slot,
461
- MarketType.PERP,
462
- mmOraclePriceData,
463
- undefined,
464
- market.orderTickSize
465
- );
466
-
467
- const swapDirection = getSwapDirection(assetType, direction);
468
-
469
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
470
- calculateUpdatedAMMSpreadReserves(
471
- market.amm,
472
- market.marketStats,
473
- direction,
474
- mmOraclePriceData,
475
- new BN(slot)
476
- );
477
- const amm = {
478
- baseAssetReserve,
479
- quoteAssetReserve,
480
- sqrtK: sqrtK,
481
- pegMultiplier: newPeg,
482
- };
483
-
484
- const [ammBids, ammAsks] = calculateMarketOpenBidAsk(
485
- market.amm.baseAssetReserve,
486
- market.amm.minBaseAssetReserve,
487
- market.amm.maxBaseAssetReserve,
488
- market.orderStepSize
489
- );
490
-
491
- let ammLiquidity: BN;
492
- if (assetType === 'base') {
493
- ammLiquidity = takerIsLong ? ammAsks.abs() : ammBids;
494
- } else {
495
- const [afterSwapQuoteReserves, _] = calculateAmmReservesAfterSwap(
496
- amm,
497
- 'base',
498
- takerIsLong ? ammAsks.abs() : ammBids,
499
- getSwapDirection('base', direction)
500
- );
501
-
502
- ammLiquidity = calculateQuoteAssetAmountSwapped(
503
- amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
504
- amm.pegMultiplier,
505
- swapDirection
506
- );
507
- }
508
-
509
- const invariant = amm.sqrtK.mul(amm.sqrtK);
510
-
511
- let bestPrice = calculatePrice(
512
- amm.baseAssetReserve,
513
- amm.quoteAssetReserve,
514
- amm.pegMultiplier
515
- );
516
-
517
- let cumulativeBaseFilled = ZERO;
518
- let cumulativeQuoteFilled = ZERO;
519
-
520
- let limitOrder = limitOrders.next().value;
521
- if (limitOrder) {
522
- const limitOrderPrice = limitOrder.getPriceOrThrow(
523
- mmOraclePriceData,
524
- slot,
525
- market.orderTickSize
526
- );
527
- bestPrice = takerIsLong
528
- ? BN.min(limitOrderPrice, bestPrice)
529
- : BN.max(limitOrderPrice, bestPrice);
530
- }
531
-
532
- let worstPrice = bestPrice;
533
-
534
- if (assetType === 'base') {
535
- while (
536
- !cumulativeBaseFilled.eq(amount) &&
537
- (ammLiquidity.gt(ZERO) || limitOrder)
538
- ) {
539
- const limitOrderPrice = limitOrder?.getPrice(
540
- mmOraclePriceData,
541
- slot,
542
- market.orderTickSize
543
- );
544
-
545
- let maxAmmFill: BN;
546
- if (limitOrderPrice) {
547
- const newBaseReserves = squareRootBN(
548
- invariant
549
- .mul(PRICE_PRECISION)
550
- .mul(amm.pegMultiplier)
551
- .div(limitOrderPrice)
552
- .div(PEG_PRECISION)
553
- );
554
-
555
- // will be zero if the limit order price is better than the amm price
556
- maxAmmFill = takerIsLong
557
- ? amm.baseAssetReserve.sub(newBaseReserves)
558
- : newBaseReserves.sub(amm.baseAssetReserve);
559
- } else {
560
- maxAmmFill = amount.sub(cumulativeBaseFilled);
561
- }
562
-
563
- maxAmmFill = BN.min(maxAmmFill, ammLiquidity);
564
-
565
- if (maxAmmFill.gt(ZERO)) {
566
- const baseFilled = BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill);
567
- const [afterSwapQuoteReserves, afterSwapBaseReserves] =
568
- calculateAmmReservesAfterSwap(amm, 'base', baseFilled, swapDirection);
569
-
570
- ammLiquidity = ammLiquidity.sub(baseFilled);
571
-
572
- const quoteFilled = calculateQuoteAssetAmountSwapped(
573
- amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
574
- amm.pegMultiplier,
575
- swapDirection
576
- );
577
-
578
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
579
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
580
-
581
- amm.baseAssetReserve = afterSwapBaseReserves;
582
- amm.quoteAssetReserve = afterSwapQuoteReserves;
583
-
584
- worstPrice = calculatePrice(
585
- amm.baseAssetReserve,
586
- amm.quoteAssetReserve,
587
- amm.pegMultiplier
588
- );
589
-
590
- if (cumulativeBaseFilled.eq(amount)) {
591
- break;
592
- }
593
- }
594
-
595
- if (!limitOrder) {
596
- continue;
597
- }
598
-
599
- if (usersToSkip.has(limitOrder.userAccount)) {
600
- continue;
601
- }
602
-
603
- const baseFilled = BN.min(
604
- limitOrder.order.baseAssetAmount.sub(
605
- limitOrder.order.baseAssetAmountFilled
606
- ),
607
- amount.sub(cumulativeBaseFilled)
608
- );
609
- const quoteFilled = baseFilled.mul(limitOrderPrice).div(BASE_PRECISION);
610
-
611
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
612
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
613
-
614
- worstPrice = limitOrderPrice;
615
-
616
- if (cumulativeBaseFilled.eq(amount)) {
617
- break;
618
- }
619
-
620
- limitOrder = limitOrders.next().value;
621
- }
622
- } else {
623
- while (
624
- !cumulativeQuoteFilled.eq(amount) &&
625
- (ammLiquidity.gt(ZERO) || limitOrder)
626
- ) {
627
- const limitOrderPrice = limitOrder?.getPrice(
628
- mmOraclePriceData,
629
- slot,
630
- market.orderTickSize
631
- );
632
-
633
- let maxAmmFill: BN;
634
- if (limitOrderPrice) {
635
- const newQuoteReserves = squareRootBN(
636
- invariant
637
- .mul(PEG_PRECISION)
638
- .mul(limitOrderPrice)
639
- .div(amm.pegMultiplier)
640
- .div(PRICE_PRECISION)
641
- );
642
-
643
- // will be zero if the limit order price is better than the amm price
644
- maxAmmFill = takerIsLong
645
- ? newQuoteReserves.sub(amm.quoteAssetReserve)
646
- : amm.quoteAssetReserve.sub(newQuoteReserves);
647
- } else {
648
- maxAmmFill = amount.sub(cumulativeQuoteFilled);
649
- }
650
-
651
- maxAmmFill = BN.min(maxAmmFill, ammLiquidity);
652
-
653
- if (maxAmmFill.gt(ZERO)) {
654
- const quoteFilled = BN.min(
655
- amount.sub(cumulativeQuoteFilled),
656
- maxAmmFill
657
- );
658
- const [afterSwapQuoteReserves, afterSwapBaseReserves] =
659
- calculateAmmReservesAfterSwap(
660
- amm,
661
- 'quote',
662
- quoteFilled,
663
- swapDirection
664
- );
665
-
666
- ammLiquidity = ammLiquidity.sub(quoteFilled);
667
-
668
- const baseFilled = afterSwapBaseReserves
669
- .sub(amm.baseAssetReserve)
670
- .abs();
671
-
672
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
673
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
674
-
675
- amm.baseAssetReserve = afterSwapBaseReserves;
676
- amm.quoteAssetReserve = afterSwapQuoteReserves;
677
-
678
- worstPrice = calculatePrice(
679
- amm.baseAssetReserve,
680
- amm.quoteAssetReserve,
681
- amm.pegMultiplier
682
- );
683
-
684
- if (cumulativeQuoteFilled.eq(amount)) {
685
- break;
686
- }
687
- }
688
-
689
- if (!limitOrder) {
690
- continue;
691
- }
692
-
693
- if (usersToSkip.has(limitOrder.userAccount)) {
694
- continue;
695
- }
696
-
697
- const quoteFilled = BN.min(
698
- limitOrder.order.baseAssetAmount
699
- .sub(limitOrder.order.baseAssetAmountFilled)
700
- .mul(limitOrderPrice)
701
- .div(BASE_PRECISION),
702
- amount.sub(cumulativeQuoteFilled)
703
- );
704
-
705
- const baseFilled = quoteFilled.mul(BASE_PRECISION).div(limitOrderPrice);
706
-
707
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
708
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
709
-
710
- worstPrice = limitOrderPrice;
711
-
712
- if (cumulativeQuoteFilled.eq(amount)) {
713
- break;
714
- }
715
-
716
- limitOrder = limitOrders.next().value;
717
- }
718
- }
719
-
720
- const entryPrice =
721
- cumulativeBaseFilled && cumulativeBaseFilled.gt(ZERO)
722
- ? cumulativeQuoteFilled.mul(BASE_PRECISION).div(cumulativeBaseFilled)
723
- : ZERO;
724
-
725
- const priceImpact =
726
- bestPrice && bestPrice.gt(ZERO)
727
- ? entryPrice.sub(bestPrice).mul(PRICE_PRECISION).div(bestPrice).abs()
728
- : ZERO;
729
-
730
- return {
731
- entryPrice,
732
- priceImpact,
733
- bestPrice,
734
- worstPrice,
735
- baseFilled: cumulativeBaseFilled,
736
- quoteFilled: cumulativeQuoteFilled,
737
- };
738
- }
739
-
740
- /**
741
- * Estimates entry price and price impact of a hypothetical taker order by walking a pre-built L2
742
- * order book snapshot (asks for a long taker, bids for a short taker), rather than the live DLOB.
743
- * Useful when an L2 snapshot is already available and a fresh DLOB walk isn't needed.
744
- *
745
- * @param {AssetType} assetType - Whether `amount` denominates base or quote
746
- * @param {BN} amount - Order size, `basePrecision` for `'base'`; QUOTE_PRECISION (1e6) for `'quote'`
747
- * @param {PositionDirection} direction - Taker's trade direction
748
- * @param {BN} basePrecision - The base precision to use for size/price math (e.g. `BASE_PRECISION`)
749
- * @param {L2OrderBook} l2 - Pre-computed L2 order book (bids/asks with price + size levels)
750
- * @return {{ entryPrice: BN; priceImpact: BN; bestPrice: BN; worstPrice: BN; baseFilled: BN;
751
- * quoteFilled: BN }} `entryPrice`/`bestPrice`/`worstPrice` are PRICE_PRECISION (1e6);
752
- * `priceImpact` is `|entryPrice - bestPrice| / bestPrice` scaled by PRICE_PRECISION (1e6);
753
- * `baseFilled` is `basePrecision`-scaled; `quoteFilled` is QUOTE_PRECISION (1e6). If the book
754
- * is empty, `bestPrice`/`worstPrice` are `BN_MAX` (long) or `ZERO` (short) and `entryPrice`/
755
- * `priceImpact` are `ZERO`
756
- */
757
- export function calculateEstimatedEntryPriceWithL2(
758
- assetType: AssetType,
759
- amount: BN,
760
- direction: PositionDirection,
761
- basePrecision: BN,
762
- l2: L2OrderBook
763
- ): {
764
- entryPrice: BN;
765
- priceImpact: BN;
766
- bestPrice: BN;
767
- worstPrice: BN;
768
- baseFilled: BN;
769
- quoteFilled: BN;
770
- } {
771
- const takerIsLong = isVariant(direction, 'long');
772
-
773
- let cumulativeBaseFilled = ZERO;
774
- let cumulativeQuoteFilled = ZERO;
775
-
776
- const levels = [...(takerIsLong ? l2.asks : l2.bids)];
777
- let nextLevel = levels.shift();
778
-
779
- let bestPrice: BN;
780
- let worstPrice: BN;
781
- if (nextLevel) {
782
- bestPrice = nextLevel.price;
783
- worstPrice = nextLevel.price;
784
- } else {
785
- bestPrice = takerIsLong ? BN_MAX : ZERO;
786
- worstPrice = bestPrice;
787
- }
788
-
789
- if (assetType === 'base') {
790
- while (!cumulativeBaseFilled.eq(amount) && nextLevel) {
791
- const price = nextLevel.price;
792
- const size = nextLevel.size;
793
-
794
- worstPrice = price;
795
-
796
- const baseFilled = BN.min(size, amount.sub(cumulativeBaseFilled));
797
- const quoteFilled = baseFilled.mul(price).div(basePrecision);
798
-
799
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
800
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
801
-
802
- nextLevel = levels.shift();
803
- }
804
- } else {
805
- while (!cumulativeQuoteFilled.eq(amount) && nextLevel) {
806
- const price = nextLevel.price;
807
- const size = nextLevel.size;
808
-
809
- worstPrice = price;
810
-
811
- const quoteFilled = BN.min(
812
- size.mul(price).div(basePrecision),
813
- amount.sub(cumulativeQuoteFilled)
814
- );
815
- const baseFilled = quoteFilled.mul(basePrecision).div(price);
816
-
817
- cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
818
- cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
819
-
820
- nextLevel = levels.shift();
821
- }
822
- }
823
-
824
- const entryPrice =
825
- cumulativeBaseFilled && cumulativeBaseFilled.gt(ZERO)
826
- ? cumulativeQuoteFilled.mul(basePrecision).div(cumulativeBaseFilled)
827
- : ZERO;
828
-
829
- const priceImpact =
830
- bestPrice && bestPrice.gt(ZERO)
831
- ? entryPrice.sub(bestPrice).mul(PRICE_PRECISION).div(bestPrice).abs()
832
- : ZERO;
833
-
834
- return {
835
- entryPrice,
836
- priceImpact,
837
- bestPrice,
838
- worstPrice,
839
- baseFilled: cumulativeBaseFilled,
840
- quoteFilled: cumulativeQuoteFilled,
841
- };
842
- }
843
-
844
- /**
845
- * Estimates a user's trailing-30-day taker + maker volume as of `now`, using the same
846
- * time-weighted decay shape as the on-chain `update_taker_volume_30d` / `update_maker_volume_30d`
847
- * (`calculate_rolling_sum`) but without requiring a new fill to trigger the on-chain update —
848
- * useful for e.g. displaying live fee-tier progress between actual `UserStats` refreshes.
849
- *
850
- * @param {UserStatsAccount} userStatsAccount - The user's stats account (`takerVolume30D`,
851
- * `makerVolume30D`, and their respective last-update timestamps)
852
- * @param {BN} [now] - Current unix timestamp (seconds); defaults to `Date.now() / 1000`
853
- * @return {BN} Estimated combined 30-day taker + maker volume, QUOTE_PRECISION (1e6)
854
- */
855
- export function getUser30dRollingVolumeEstimate(
856
- userStatsAccount: UserStatsAccount,
857
- now?: BN
858
- ) {
859
- now = now || new BN(new Date().getTime() / 1000);
860
- const sinceLastTaker = BN.max(
861
- now.sub(userStatsAccount.lastTakerVolume30DTs),
862
- ZERO
863
- );
864
- const sinceLastMaker = BN.max(
865
- now.sub(userStatsAccount.lastMakerVolume30DTs),
866
- ZERO
867
- );
868
- const thirtyDaysInSeconds = new BN(60 * 60 * 24 * 30);
869
- const last30dVolume = userStatsAccount.takerVolume30D
870
- .mul(BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), ZERO))
871
- .div(thirtyDaysInSeconds)
872
- .add(
873
- userStatsAccount.makerVolume30D
874
- .mul(BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), ZERO))
875
- .div(thirtyDaysInSeconds)
876
- );
877
-
878
- return last30dVolume;
879
- }