@velocity-exchange/sdk 0.2.4 → 0.3.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 (85) hide show
  1. package/CHANGELOG.md +60 -4
  2. package/README.md +8 -8
  3. package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +0 -1
  4. package/lib/browser/accounts/grpcMultiAccountSubscriber.js +2 -7
  5. package/lib/browser/accounts/grpcMultiUserAccountSubscriber.js +3 -0
  6. package/lib/browser/accounts/webSocketAccountSubscriberV2.js +1 -2
  7. package/lib/browser/accounts/websocketProgramUserAccountSubscriber.js +3 -0
  8. package/lib/browser/adminClient.d.ts +3 -1
  9. package/lib/browser/adminClient.js +16 -0
  10. package/lib/browser/idl/velocity.d.ts +50 -1
  11. package/lib/browser/idl/velocity.json +50 -1
  12. package/lib/browser/math/exchangeStatus.d.ts +1 -0
  13. package/lib/browser/math/exchangeStatus.js +8 -1
  14. package/lib/browser/math/orders.d.ts +3 -0
  15. package/lib/browser/math/orders.js +6 -2
  16. package/lib/browser/math/state.js +1 -1
  17. package/lib/browser/memcmp.js +26 -5
  18. package/lib/browser/orderSubscriber/OrderSubscriber.js +11 -2
  19. package/lib/browser/swift/swiftOrderSubscriber.js +2 -2
  20. package/lib/browser/tokenFaucet.d.ts +2 -2
  21. package/lib/browser/tokenFaucet.js +11 -4
  22. package/lib/browser/types.d.ts +5 -0
  23. package/lib/browser/types.js +9 -2
  24. package/lib/browser/user.d.ts +22 -6
  25. package/lib/browser/user.js +24 -8
  26. package/lib/browser/velocityClient.d.ts +1 -3
  27. package/lib/browser/velocityClient.js +16 -22
  28. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +0 -1
  29. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
  30. package/lib/node/accounts/grpcMultiAccountSubscriber.js +2 -7
  31. package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts.map +1 -1
  32. package/lib/node/accounts/grpcMultiUserAccountSubscriber.js +3 -0
  33. package/lib/node/accounts/webSocketAccountSubscriberV2.d.ts.map +1 -1
  34. package/lib/node/accounts/webSocketAccountSubscriberV2.js +1 -2
  35. package/lib/node/accounts/websocketProgramUserAccountSubscriber.d.ts.map +1 -1
  36. package/lib/node/accounts/websocketProgramUserAccountSubscriber.js +3 -0
  37. package/lib/node/adminClient.d.ts +3 -1
  38. package/lib/node/adminClient.d.ts.map +1 -1
  39. package/lib/node/adminClient.js +16 -0
  40. package/lib/node/idl/velocity.d.ts +50 -1
  41. package/lib/node/idl/velocity.d.ts.map +1 -1
  42. package/lib/node/idl/velocity.json +50 -1
  43. package/lib/node/math/exchangeStatus.d.ts +1 -0
  44. package/lib/node/math/exchangeStatus.d.ts.map +1 -1
  45. package/lib/node/math/exchangeStatus.js +8 -1
  46. package/lib/node/math/orders.d.ts +3 -0
  47. package/lib/node/math/orders.d.ts.map +1 -1
  48. package/lib/node/math/orders.js +6 -2
  49. package/lib/node/math/state.js +1 -1
  50. package/lib/node/memcmp.d.ts.map +1 -1
  51. package/lib/node/memcmp.js +26 -5
  52. package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
  53. package/lib/node/orderSubscriber/OrderSubscriber.js +11 -2
  54. package/lib/node/swift/swiftOrderSubscriber.js +2 -2
  55. package/lib/node/tokenFaucet.d.ts +2 -2
  56. package/lib/node/tokenFaucet.d.ts.map +1 -1
  57. package/lib/node/tokenFaucet.js +11 -4
  58. package/lib/node/types.d.ts +5 -0
  59. package/lib/node/types.d.ts.map +1 -1
  60. package/lib/node/types.js +9 -2
  61. package/lib/node/user.d.ts +22 -6
  62. package/lib/node/user.d.ts.map +1 -1
  63. package/lib/node/user.js +24 -8
  64. package/lib/node/velocityClient.d.ts +1 -3
  65. package/lib/node/velocityClient.d.ts.map +1 -1
  66. package/lib/node/velocityClient.js +16 -22
  67. package/package.json +2 -2
  68. package/src/accounts/grpcMultiAccountSubscriber.ts +2 -10
  69. package/src/accounts/grpcMultiUserAccountSubscriber.ts +5 -0
  70. package/src/accounts/webSocketAccountSubscriberV2.ts +1 -5
  71. package/src/accounts/websocketProgramUserAccountSubscriber.ts +5 -0
  72. package/src/adminClient.ts +28 -0
  73. package/src/idl/velocity.json +50 -1
  74. package/src/idl/velocity.ts +50 -1
  75. package/src/math/exchangeStatus.ts +10 -0
  76. package/src/math/orders.ts +6 -2
  77. package/src/math/state.ts +1 -1
  78. package/src/memcmp.ts +27 -5
  79. package/src/orderSubscriber/OrderSubscriber.ts +18 -2
  80. package/src/swift/swiftOrderSubscriber.ts +2 -2
  81. package/src/tokenFaucet.ts +10 -7
  82. package/src/types.ts +8 -0
  83. package/src/user.ts +24 -8
  84. package/src/velocityClient.ts +17 -36
  85. package/tests/dlob/helpers.ts +1 -0
@@ -22,6 +22,7 @@ import {
22
22
  OracleGuardRails,
23
23
  OracleSource,
24
24
  ExchangeStatus,
25
+ SolvencyStatus,
25
26
  MarketStatus,
26
27
  ContractTier,
27
28
  AssetTier,
@@ -3223,6 +3224,33 @@ export class AdminClient extends VelocityClient {
3223
3224
  });
3224
3225
  }
3225
3226
 
3227
+ public async updateSolvencyStatus(
3228
+ solvencyStatus: SolvencyStatus
3229
+ ): Promise<TransactionSignature> {
3230
+ const updateSolvencyStatusIx = await this.getUpdateSolvencyStatusIx(
3231
+ solvencyStatus
3232
+ );
3233
+
3234
+ const tx = await this.buildTransaction(updateSolvencyStatusIx);
3235
+
3236
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
3237
+
3238
+ return txSig;
3239
+ }
3240
+
3241
+ public async getUpdateSolvencyStatusIx(
3242
+ solvencyStatus: SolvencyStatus
3243
+ ): Promise<TransactionInstruction> {
3244
+ return await this.program.instruction.updateSolvencyStatus(solvencyStatus, {
3245
+ accounts: {
3246
+ admin: this.isSubscribed
3247
+ ? this.getStateAccount().coldAdmin
3248
+ : this.wallet.publicKey,
3249
+ state: await this.getStatePublicKey(),
3250
+ },
3251
+ });
3252
+ }
3253
+
3226
3254
  public async updatePerpAuctionDuration(
3227
3255
  minDuration: BN | number
3228
3256
  ): Promise<TransactionSignature> {
@@ -11068,6 +11068,35 @@
11068
11068
  }
11069
11069
  ]
11070
11070
  },
11071
+ {
11072
+ "name": "update_solvency_status",
11073
+ "discriminator": [
11074
+ 81,
11075
+ 136,
11076
+ 15,
11077
+ 6,
11078
+ 24,
11079
+ 165,
11080
+ 44,
11081
+ 133
11082
+ ],
11083
+ "accounts": [
11084
+ {
11085
+ "name": "admin",
11086
+ "signer": true
11087
+ },
11088
+ {
11089
+ "name": "state",
11090
+ "writable": true
11091
+ }
11092
+ ],
11093
+ "args": [
11094
+ {
11095
+ "name": "solvency_status",
11096
+ "type": "u8"
11097
+ }
11098
+ ]
11099
+ },
11071
11100
  {
11072
11101
  "name": "update_special_user_status",
11073
11102
  "discriminator": [
@@ -15972,6 +16001,16 @@
15972
16001
  "code": 6354,
15973
16002
  "name": "InsufficientProtocolFees",
15974
16003
  "msg": "Insufficient protocol fees available to withdraw"
16004
+ },
16005
+ {
16006
+ "code": 6355,
16007
+ "name": "InvalidNativeStateAccount",
16008
+ "msg": "Native dispatch: supplied state account is not the canonical Velocity state PDA"
16009
+ },
16010
+ {
16011
+ "code": 6356,
16012
+ "name": "InvalidNativePerpMarketAccount",
16013
+ "msg": "Native dispatch: supplied market account is not a Velocity perp market"
15975
16014
  }
15976
16015
  ],
15977
16016
  "types": [
@@ -23207,6 +23246,16 @@
23207
23246
  "name": "lp_pool_feature_bit_flags",
23208
23247
  "type": "u8"
23209
23248
  },
23249
+ {
23250
+ "name": "solvency_status",
23251
+ "docs": [
23252
+ "Bitmask of `SolvencyStatus` flags. Gates internal solvency-repair flows",
23253
+ "(bankruptcy / pnl-deficit resolution) independently of `WithdrawPaused`,",
23254
+ "so user withdrawals can be halted while repair keeps running, or repair",
23255
+ "can be frozen on its own when an oracle is suspect. `0` = repair allowed."
23256
+ ],
23257
+ "type": "u8"
23258
+ },
23210
23259
  {
23211
23260
  "name": "protocol_fee_recipient_perp",
23212
23261
  "docs": [
@@ -23241,7 +23290,7 @@
23241
23290
  "type": {
23242
23291
  "array": [
23243
23292
  "u8",
23244
- 272
23293
+ 271
23245
23294
  ]
23246
23295
  }
23247
23296
  }
@@ -11074,6 +11074,35 @@ export type Velocity = {
11074
11074
  }
11075
11075
  ]
11076
11076
  },
11077
+ {
11078
+ "name": "updateSolvencyStatus",
11079
+ "discriminator": [
11080
+ 81,
11081
+ 136,
11082
+ 15,
11083
+ 6,
11084
+ 24,
11085
+ 165,
11086
+ 44,
11087
+ 133
11088
+ ],
11089
+ "accounts": [
11090
+ {
11091
+ "name": "admin",
11092
+ "signer": true
11093
+ },
11094
+ {
11095
+ "name": "state",
11096
+ "writable": true
11097
+ }
11098
+ ],
11099
+ "args": [
11100
+ {
11101
+ "name": "solvencyStatus",
11102
+ "type": "u8"
11103
+ }
11104
+ ]
11105
+ },
11077
11106
  {
11078
11107
  "name": "updateSpecialUserStatus",
11079
11108
  "discriminator": [
@@ -15978,6 +16007,16 @@ export type Velocity = {
15978
16007
  "code": 6354,
15979
16008
  "name": "insufficientProtocolFees",
15980
16009
  "msg": "Insufficient protocol fees available to withdraw"
16010
+ },
16011
+ {
16012
+ "code": 6355,
16013
+ "name": "invalidNativeStateAccount",
16014
+ "msg": "Native dispatch: supplied state account is not the canonical Velocity state PDA"
16015
+ },
16016
+ {
16017
+ "code": 6356,
16018
+ "name": "invalidNativePerpMarketAccount",
16019
+ "msg": "Native dispatch: supplied market account is not a Velocity perp market"
15981
16020
  }
15982
16021
  ],
15983
16022
  "types": [
@@ -23213,6 +23252,16 @@ export type Velocity = {
23213
23252
  "name": "lpPoolFeatureBitFlags",
23214
23253
  "type": "u8"
23215
23254
  },
23255
+ {
23256
+ "name": "solvencyStatus",
23257
+ "docs": [
23258
+ "Bitmask of `SolvencyStatus` flags. Gates internal solvency-repair flows",
23259
+ "(bankruptcy / pnl-deficit resolution) independently of `WithdrawPaused`,",
23260
+ "so user withdrawals can be halted while repair keeps running, or repair",
23261
+ "can be frozen on its own when an oracle is suspect. `0` = repair allowed."
23262
+ ],
23263
+ "type": "u8"
23264
+ },
23216
23265
  {
23217
23266
  "name": "protocolFeeRecipientPerp",
23218
23267
  "docs": [
@@ -23247,7 +23296,7 @@ export type Velocity = {
23247
23296
  "type": {
23248
23297
  "array": [
23249
23298
  "u8",
23250
- 272
23299
+ 271
23251
23300
  ]
23252
23301
  }
23253
23302
  }
@@ -7,6 +7,7 @@ import {
7
7
  ExchangeStatus,
8
8
  PerpMarketAccount,
9
9
  PerpOperation,
10
+ SolvencyStatus,
10
11
  SpotMarketAccount,
11
12
  SpotOperation,
12
13
  StateAccount,
@@ -20,6 +21,15 @@ export function exchangePaused(state: StateAccount): boolean {
20
21
  return state.exchangeStatus !== ExchangeStatus.ACTIVE;
21
22
  }
22
23
 
24
+ // Mirror of the program's `State::solvency_repair_paused`. Gates the resolve
25
+ // bankruptcy / pnl-deficit instructions, independent of WITHDRAW_PAUSED.
26
+ export function solvencyRepairPaused(state: StateAccount): boolean {
27
+ return (
28
+ (state.solvencyStatus & SolvencyStatus.SOLVENCY_REPAIR_PAUSED) ===
29
+ SolvencyStatus.SOLVENCY_REPAIR_PAUSED
30
+ );
31
+ }
32
+
23
33
  export function fillPaused(
24
34
  state: StateAccount,
25
35
  market: PerpMarketAccount | SpotMarketAccount
@@ -312,6 +312,9 @@ export function calculateOrderBaseAssetAmount(
312
312
  * Returns:
313
313
  * - BN size (>=0) if bounded
314
314
  * - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
315
+ *
316
+ * Note: a market.maxOpenInterest of 0 means "no configured cap" and is treated
317
+ * as unlimited rather than a hard cap of 0.
315
318
  */
316
319
  export function maxSizeForTargetLiabilityWeightBN(
317
320
  target: BN,
@@ -364,9 +367,10 @@ export function maxSizeForTargetLiabilityWeightBN(
364
367
  }
365
368
  }
366
369
 
367
- // cap at max OI
370
+ // cap at max OI. A maxOpenInterest of 0 means no configured cap (unlimited),
371
+ // matching the on-chain convention — do not treat it as a hard cap of 0.
368
372
  const maxOpenInterest = market.maxOpenInterest;
369
- if (lo.gt(maxOpenInterest)) {
373
+ if (!maxOpenInterest.isZero() && lo.gt(maxOpenInterest)) {
370
374
  return maxOpenInterest;
371
375
  }
372
376
 
package/src/math/state.ts CHANGED
@@ -15,7 +15,7 @@ export function calculateInitUserFee(stateAccount: StateAccount): BN {
15
15
  const accountSpaceUtilization = stateAccount.numberOfSubAccounts
16
16
  .addn(1)
17
17
  .mul(PERCENTAGE_PRECISION)
18
- .div(getMaxNumberOfSubAccounts(stateAccount));
18
+ .div(BN.max(getMaxNumberOfSubAccounts(stateAccount), new BN(1)));
19
19
 
20
20
  if (accountSpaceUtilization.gt(targetUtilization)) {
21
21
  return maxInitFee
package/src/memcmp.ts CHANGED
@@ -12,10 +12,32 @@ export function getUserFilter(): MemcmpFilter {
12
12
  };
13
13
  }
14
14
 
15
+ /*
16
+ * Byte offsets of the trailing scalar flags in the `User` account.
17
+ *
18
+ * These MUST match the on-chain `User` layout decoded in `decode/user.ts`. The
19
+ * current Velocity layout is 4496 bytes, with the tail block laid out as
20
+ * consecutive single bytes:
21
+ * status(4468) isMarginTradingEnabled(4469) idle(4470) openOrders(4471)
22
+ * hasOpenOrder(4472) openAuctions(4473) hasOpenAuction(4474) poolId(4475)
23
+ * specialUserStatus(4476)
24
+ *
25
+ * NOTE: these were previously hardcoded to the older (4376-byte) layout
26
+ * (idle@4350, hasOpenOrder@4352, ...). After Velocity added fields to
27
+ * `PerpPosition`, the account grew by 120 bytes and these flags shifted, but
28
+ * the filters were not updated — so `getUserWithOrderFilter()` matched zero
29
+ * accounts and the DLOB order book never populated. Keep these in sync with
30
+ * `decode/user.ts` if the `User` layout changes again.
31
+ */
32
+ const USER_IDLE_OFFSET = 4470;
33
+ const USER_HAS_OPEN_ORDER_OFFSET = 4472;
34
+ const USER_HAS_OPEN_AUCTION_OFFSET = 4474;
35
+ const USER_POOL_ID_OFFSET = 4475;
36
+
15
37
  export function getNonIdleUserFilter(): MemcmpFilter {
16
38
  return {
17
39
  memcmp: {
18
- offset: 4350,
40
+ offset: USER_IDLE_OFFSET,
19
41
  bytes: bs58.encode(Uint8Array.from([0])),
20
42
  },
21
43
  };
@@ -24,7 +46,7 @@ export function getNonIdleUserFilter(): MemcmpFilter {
24
46
  export function getUserWithOrderFilter(): MemcmpFilter {
25
47
  return {
26
48
  memcmp: {
27
- offset: 4352,
49
+ offset: USER_HAS_OPEN_ORDER_OFFSET,
28
50
  bytes: bs58.encode(Uint8Array.from([1])),
29
51
  },
30
52
  };
@@ -33,7 +55,7 @@ export function getUserWithOrderFilter(): MemcmpFilter {
33
55
  export function getUserWithoutOrderFilter(): MemcmpFilter {
34
56
  return {
35
57
  memcmp: {
36
- offset: 4352,
58
+ offset: USER_HAS_OPEN_ORDER_OFFSET,
37
59
  bytes: bs58.encode(Uint8Array.from([0])),
38
60
  },
39
61
  };
@@ -42,7 +64,7 @@ export function getUserWithoutOrderFilter(): MemcmpFilter {
42
64
  export function getUserWithAuctionFilter(): MemcmpFilter {
43
65
  return {
44
66
  memcmp: {
45
- offset: 4354,
67
+ offset: USER_HAS_OPEN_AUCTION_OFFSET,
46
68
  bytes: bs58.encode(Uint8Array.from([1])),
47
69
  },
48
70
  };
@@ -60,7 +82,7 @@ export function getUserWithName(name: string): MemcmpFilter {
60
82
  export function getUsersWithPoolId(poolId: number): MemcmpFilter {
61
83
  return {
62
84
  memcmp: {
63
- offset: 4356,
85
+ offset: USER_POOL_ID_OFFSET,
64
86
  bytes: bs58.encode(Uint8Array.from([poolId])),
65
87
  },
66
88
  };
@@ -19,6 +19,16 @@ import { grpcSubscription } from './grpcSubscription';
19
19
  import { calculateOrderBaseAssetAmount } from '../math/orders';
20
20
  import { ZERO } from '../constants/numericConstants';
21
21
 
22
+ /*
23
+ * Byte offset of `lastActiveSlot` (u64) in the `User` account, used here to
24
+ * cheaply detect stale updates without fully decoding the buffer. Must match
25
+ * the on-chain `User` layout (see `decode/user.ts`). The previous value (4328)
26
+ * was for the older 4376-byte layout; the current Velocity layout is 4496
27
+ * bytes, shifting this field +120 bytes. With the wrong offset this read 8
28
+ * zero-padding bytes, so the staleness guard rejected every post-load update.
29
+ */
30
+ const USER_LAST_ACTIVE_SLOT_OFFSET = 4448;
31
+
22
32
  export class OrderSubscriber {
23
33
  velocityClient: VelocityClient;
24
34
  usersAccounts = new Map<string, { slot: number; userAccount: UserAccount }>();
@@ -176,7 +186,10 @@ export class OrderSubscriber {
176
186
  const buffer = Buffer.from(data[0], data[1]);
177
187
 
178
188
  const newLastActiveSlot = new BN(
179
- buffer.subarray(4328, 4328 + 8),
189
+ buffer.subarray(
190
+ USER_LAST_ACTIVE_SLOT_OFFSET,
191
+ USER_LAST_ACTIVE_SLOT_OFFSET + 8
192
+ ),
180
193
  undefined,
181
194
  'le'
182
195
  );
@@ -191,7 +204,10 @@ export class OrderSubscriber {
191
204
  } else if (dataType === 'buffer') {
192
205
  const buffer: Buffer = data as Buffer;
193
206
  const newLastActiveSlot = new BN(
194
- buffer.subarray(4328, 4328 + 8),
207
+ buffer.subarray(
208
+ USER_LAST_ACTIVE_SLOT_OFFSET,
209
+ USER_LAST_ACTIVE_SLOT_OFFSET + 8
210
+ ),
195
211
  undefined,
196
212
  'le'
197
213
  );
@@ -161,8 +161,8 @@ export class SwiftOrderSubscriber {
161
161
  const endpoint =
162
162
  this.config.endpoint ??
163
163
  (env === 'devnet'
164
- ? 'wss://master.swift.drift.trade/ws'
165
- : 'wss://swift.drift.trade/ws');
164
+ ? 'wss://swift.master.velocity.exchange/ws'
165
+ : 'wss://swift.velocity.exchange/ws');
166
166
  const ws = new WebSocket(
167
167
  endpoint + '?pubkey=' + this.config.keypair.publicKey.toBase58()
168
168
  );
@@ -1,5 +1,5 @@
1
- import * as anchor from './isomorphic/anchor29';
2
- import { AnchorProvider, Idl, Program } from './isomorphic/anchor29';
1
+ import * as anchor from './isomorphic/anchor';
2
+ import { AnchorProvider, Idl, Program } from './isomorphic/anchor';
3
3
  import {
4
4
  TOKEN_PROGRAM_ID,
5
5
  Account,
@@ -51,11 +51,14 @@ export class TokenFaucet {
51
51
  this.opts
52
52
  );
53
53
  this.provider = provider;
54
- this.program = new Program(
55
- tokenFaucet as unknown as Idl,
56
- programId,
57
- provider
58
- );
54
+ // Anchor (>= 0.30) reads the program id from `idl.address` rather than a
55
+ // constructor argument. Override it with the caller-provided `programId`
56
+ // so PDAs and instructions target the intended program.
57
+ const idl = {
58
+ ...(tokenFaucet as unknown as Idl),
59
+ address: programId.toBase58(),
60
+ } as Idl;
61
+ this.program = new Program(idl, provider);
59
62
  this.mint = mint;
60
63
  }
61
64
 
package/src/types.ts CHANGED
@@ -39,6 +39,13 @@ export enum ExchangeStatus {
39
39
  PAUSED = 255,
40
40
  }
41
41
 
42
+ // Mirror of the Rust `SolvencyStatus` bitflag (State.solvencyStatus). Gates
43
+ // internal solvency-repair flows independently of ExchangeStatus.WITHDRAW_PAUSED.
44
+ export enum SolvencyStatus {
45
+ ACTIVE = 0,
46
+ SOLVENCY_REPAIR_PAUSED = 1,
47
+ }
48
+
42
49
  export enum FeatureBitFlags {
43
50
  MM_ORACLE_UPDATE = 1,
44
51
  MEDIAN_TRIGGER_PRICE = 2,
@@ -771,6 +778,7 @@ export type StateAccount = {
771
778
  maxInitializeUserFee: number;
772
779
  featureBitFlags: number;
773
780
  lpPoolFeatureBitFlags: number;
781
+ solvencyStatus: number;
774
782
  };
775
783
 
776
784
  export type PerpMarketAccount = {
package/src/user.ts CHANGED
@@ -225,20 +225,35 @@ export class User {
225
225
  this.isSubscribed = false;
226
226
  }
227
227
 
228
+ /**
229
+ * Returns the cached user account.
230
+ *
231
+ * - **Throws** `NotSubscribedError` if the subscriber has not been subscribed
232
+ * yet — reading the account before {@link subscribe} resolves is a
233
+ * programming error, not a missing-account condition.
234
+ * - Returns `undefined` when subscribed but no account was found on chain.
235
+ * Because `subscribe()` awaits the initial fetch, an `undefined` here means
236
+ * the account does not exist (or has not yet been observed), not that data
237
+ * is "still loading".
238
+ */
228
239
  public getUserAccount(): UserAccount | undefined {
229
240
  return this.accountSubscriber.getUserAccountAndSlot()?.data;
230
241
  }
231
242
 
232
243
  /**
233
- * Like {@link getUserAccount} but throws a named error instead of returning
234
- * `undefined` when the account has not been loaded yet. Use at call sites
235
- * that structurally require a loaded account.
244
+ * Like {@link getUserAccount} but throws instead of returning `undefined`
245
+ * when the account was not found. Use at call sites that structurally
246
+ * require the account to exist. (Still propagates `NotSubscribedError` when
247
+ * called before subscribing.)
248
+ *
249
+ * Delegates to {@link getUserAccount} (rather than the subscriber directly)
250
+ * so callers that override `getUserAccount` see the override here too.
236
251
  */
237
252
  public getUserAccountOrThrow(): UserAccount {
238
253
  const userAccount = this.getUserAccount();
239
254
  if (!userAccount) {
240
255
  throw new Error(
241
- `User account not loaded: ${this.getUserAccountPublicKey().toString()}`
256
+ `User account not found: ${this.getUserAccountPublicKey().toString()}`
242
257
  );
243
258
  }
244
259
  return userAccount;
@@ -255,14 +270,15 @@ export class User {
255
270
  }
256
271
 
257
272
  /**
258
- * Like {@link getUserAccountAndSlot} but throws a named error instead of
259
- * returning `undefined` when the account has not been loaded yet. Use at
260
- * call sites that structurally require a loaded account.
273
+ * Like {@link getUserAccountAndSlot} but throws instead of returning
274
+ * `undefined` when the account was not found. Use at call sites that
275
+ * structurally require the account to exist. (Still propagates
276
+ * `NotSubscribedError` when called before subscribing.)
261
277
  */
262
278
  public getUserAccountAndSlotOrThrow(): DataAndSlot<UserAccount> {
263
279
  return assertDataAndSlot(
264
280
  this.accountSubscriber.getUserAccountAndSlot(),
265
- `User account not loaded: ${this.getUserAccountPublicKey().toString()}`
281
+ `User account not found: ${this.getUserAccountPublicKey().toString()}`
266
282
  );
267
283
  }
268
284
 
@@ -1297,37 +1297,6 @@ export class VelocityClient {
1297
1297
  });
1298
1298
  }
1299
1299
 
1300
- public async migrateReferrer(
1301
- authority: PublicKey,
1302
- txParams?: TxParams
1303
- ): Promise<TransactionSignature> {
1304
- const ix = await this.getMigrateReferrerIx(authority);
1305
- const tx = await this.buildTransaction([ix], txParams);
1306
- const { txSig } = await this.sendTransaction(tx, [], this.opts);
1307
- return txSig;
1308
- }
1309
-
1310
- public async getMigrateReferrerIx(
1311
- authority: PublicKey
1312
- ): Promise<TransactionInstruction> {
1313
- const escrow = getRevenueShareEscrowAccountPublicKey(
1314
- this.program.programId,
1315
- authority
1316
- );
1317
- return (this.program.instruction as any).migrateReferrer({
1318
- accounts: {
1319
- escrow,
1320
- authority,
1321
- userStats: getUserStatsAccountPublicKey(
1322
- this.program.programId,
1323
- authority
1324
- ),
1325
- state: await this.getStatePublicKey(),
1326
- payer: this.wallet.publicKey,
1327
- },
1328
- });
1329
- }
1330
-
1331
1300
  public async resizeRevenueShareEscrowOrders(
1332
1301
  authority: PublicKey,
1333
1302
  numOrders: number,
@@ -2335,7 +2304,7 @@ export class VelocityClient {
2335
2304
  subAccountId = subAccountId ?? this.activeSubAccountId;
2336
2305
  authority = authority ?? this.authority;
2337
2306
  if (subAccountId === undefined || authority === undefined) {
2338
- throw new Error('Subaccount ID and authority are required');
2307
+ return false;
2339
2308
  }
2340
2309
  const userMapKey = this.getUserMapKey(subAccountId, authority);
2341
2310
 
@@ -10360,7 +10329,11 @@ export class VelocityClient {
10360
10329
  );
10361
10330
 
10362
10331
  const tx = await this.buildTransaction(updateMmOracleIx, {
10363
- computeUnits: 1000,
10332
+ // Headroom for the native handler's owner + discriminator validation
10333
+ // of the state + perp-market accounts (was 1000 when it bytemuck-cast
10334
+ // the market without any checks). Measured ~1.3k CU; 2000 leaves
10335
+ // margin for the production-only signer check.
10336
+ computeUnits: 2000,
10364
10337
  computeUnitsPrice: 0,
10365
10338
  });
10366
10339
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
@@ -10368,16 +10341,19 @@ export class VelocityClient {
10368
10341
  return txSig;
10369
10342
  }
10370
10343
 
10371
- public getUpdateAmmSpreadAdjustmentNativeIx(
10344
+ public async getUpdateAmmSpreadAdjustmentNativeIx(
10372
10345
  marketIndex: number,
10373
10346
  ammSpreadAdjustment: number // i8
10374
- ): TransactionInstruction {
10347
+ ): Promise<TransactionInstruction> {
10375
10348
  const discriminatorBuffer = createNativeInstructionDiscriminatorBuffer(1);
10376
10349
  const data = Buffer.alloc(discriminatorBuffer.length + 4);
10377
10350
  data.set(discriminatorBuffer, 0);
10378
10351
  data.writeInt8(ammSpreadAdjustment, 5); // next byte
10379
10352
 
10380
- // Build the instruction manually
10353
+ // Build the instruction manually. The native handler re-establishes the
10354
+ // account guarantees Anchor would normally provide: it loads `state` as
10355
+ // the program-owned State account and authenticates the signer against
10356
+ // `state.hotAmmSpreadAdjust`, so the state account is required at index 2.
10381
10357
  return new TransactionInstruction({
10382
10358
  programId: this.program.programId,
10383
10359
  keys: [
@@ -10391,6 +10367,11 @@ export class VelocityClient {
10391
10367
  isWritable: false,
10392
10368
  isSigner: true,
10393
10369
  },
10370
+ {
10371
+ pubkey: await this.getStatePublicKey(),
10372
+ isWritable: false,
10373
+ isSigner: false,
10374
+ },
10394
10375
  ],
10395
10376
  data,
10396
10377
  });
@@ -560,6 +560,7 @@ export const mockStateAccount: StateAccount = {
560
560
  protocolFeeRecipientSpot: PublicKey.default,
561
561
  featureBitFlags: 0,
562
562
  lpPoolFeatureBitFlags: 0,
563
+ solvencyStatus: 0,
563
564
  defaultMarketOrderTimeInForce: 0,
564
565
  defaultSpotAuctionDuration: 0,
565
566
  discountMint: PublicKey.default,