@bluefin-exchange/pro-sdk 0.1.61 → 0.2.0-pre-release.1

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 (210) hide show
  1. package/dist/{example.js → cjs/example.js} +3 -0
  2. package/dist/cjs/example.js.map +1 -0
  3. package/dist/{index.js → cjs/index.js} +1 -0
  4. package/dist/cjs/index.js.map +1 -0
  5. package/dist/{src → cjs/src}/api.js +75 -1
  6. package/dist/cjs/src/api.js.map +1 -0
  7. package/dist/{src → cjs/src}/base.js +5 -1
  8. package/dist/cjs/src/base.js.map +1 -0
  9. package/dist/{src → cjs/src}/common.js +1 -0
  10. package/dist/cjs/src/common.js.map +1 -0
  11. package/dist/{src → cjs/src}/configuration.js +1 -0
  12. package/dist/cjs/src/configuration.js.map +1 -0
  13. package/dist/{src → cjs/src}/index.js +1 -0
  14. package/dist/cjs/src/index.js.map +1 -0
  15. package/dist/{src → cjs/src}/request-signer.js +11 -0
  16. package/dist/cjs/src/request-signer.js.map +1 -0
  17. package/dist/{src → cjs/src}/sdk.js +47 -4
  18. package/dist/cjs/src/sdk.js.map +1 -0
  19. package/dist/{src → cjs/src}/utils.js +1 -0
  20. package/dist/cjs/src/utils.js.map +1 -0
  21. package/dist/{src → cjs/src}/websocket.js +1 -0
  22. package/dist/cjs/src/websocket.js.map +1 -0
  23. package/dist/esm/example.js +218 -0
  24. package/dist/esm/example.js.map +1 -0
  25. package/dist/esm/index.js +18 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/src/api.js +4318 -0
  28. package/dist/esm/src/api.js.map +1 -0
  29. package/dist/esm/src/base.js +69 -0
  30. package/dist/esm/src/base.js.map +1 -0
  31. package/dist/esm/src/common.js +162 -0
  32. package/dist/esm/src/common.js.map +1 -0
  33. package/dist/esm/src/configuration.js +45 -0
  34. package/dist/esm/src/configuration.js.map +1 -0
  35. package/dist/esm/src/index.js +23 -0
  36. package/dist/esm/src/index.js.map +1 -0
  37. package/dist/esm/src/request-signer.js +231 -0
  38. package/dist/esm/src/request-signer.js.map +1 -0
  39. package/dist/esm/src/sdk.js +786 -0
  40. package/dist/esm/src/sdk.js.map +1 -0
  41. package/dist/esm/src/utils.js +10 -0
  42. package/dist/esm/src/utils.js.map +1 -0
  43. package/dist/esm/src/websocket.js +159 -0
  44. package/dist/esm/src/websocket.js.map +1 -0
  45. package/dist/types/example.d.ts +2 -0
  46. package/dist/types/example.d.ts.map +1 -0
  47. package/dist/types/index.d.ts +2 -0
  48. package/dist/types/index.d.ts.map +1 -0
  49. package/dist/{src → types/src}/api.d.ts +126 -47
  50. package/dist/types/src/api.d.ts.map +1 -0
  51. package/dist/{src → types/src}/base.d.ts +1 -0
  52. package/dist/types/src/base.d.ts.map +1 -0
  53. package/dist/{src → types/src}/common.d.ts +1 -0
  54. package/dist/types/src/common.d.ts.map +1 -0
  55. package/dist/{src → types/src}/configuration.d.ts +1 -0
  56. package/dist/types/src/configuration.d.ts.map +1 -0
  57. package/dist/{src → types/src}/index.d.ts +1 -0
  58. package/dist/types/src/index.d.ts.map +1 -0
  59. package/dist/{src → types/src}/request-signer.d.ts +6 -1
  60. package/dist/types/src/request-signer.d.ts.map +1 -0
  61. package/dist/{src → types/src}/sdk.d.ts +12 -1
  62. package/dist/types/src/sdk.d.ts.map +1 -0
  63. package/dist/{src → types/src}/utils.d.ts +1 -0
  64. package/dist/types/src/utils.d.ts.map +1 -0
  65. package/dist/{src → types/src}/websocket.d.ts +1 -0
  66. package/dist/types/src/websocket.d.ts.map +1 -0
  67. package/package.json +21 -7
  68. package/dist/example.d.ts +0 -1
  69. package/dist/index.d.ts +0 -1
  70. package/example.ts +0 -323
  71. package/index.ts +0 -1
  72. package/openapitools.json +0 -24
  73. package/src/.openapi-generator/FILES +0 -128
  74. package/src/.openapi-generator/VERSION +0 -1
  75. package/src/.openapi-generator-ignore +0 -28
  76. package/src/README.md +0 -46
  77. package/src/api.ts +0 -8918
  78. package/src/base.ts +0 -86
  79. package/src/common.ts +0 -150
  80. package/src/configuration.ts +0 -115
  81. package/src/docs/Account.md +0 -64
  82. package/src/docs/AccountAggregatedTradeUpdate.md +0 -21
  83. package/src/docs/AccountAuthorizationRequest.md +0 -24
  84. package/src/docs/AccountAuthorizationRequestSignedFields.md +0 -28
  85. package/src/docs/AccountCommandFailureUpdate.md +0 -29
  86. package/src/docs/AccountDataApi.md +0 -374
  87. package/src/docs/AccountDataStream.md +0 -21
  88. package/src/docs/AccountEventReason.md +0 -25
  89. package/src/docs/AccountEventType.md +0 -21
  90. package/src/docs/AccountFundingRateHistory.md +0 -20
  91. package/src/docs/AccountFundingRateHistoryData.md +0 -30
  92. package/src/docs/AccountMarketPreference.md +0 -22
  93. package/src/docs/AccountOrderUpdate.md +0 -71
  94. package/src/docs/AccountPositionLeverageUpdateRequest.md +0 -22
  95. package/src/docs/AccountPositionLeverageUpdateRequestSignedFields.md +0 -30
  96. package/src/docs/AccountPositionUpdate.md +0 -47
  97. package/src/docs/AccountPreference.md +0 -24
  98. package/src/docs/AccountStreamMessage.md +0 -25
  99. package/src/docs/AccountStreamMessagePayload.md +0 -153
  100. package/src/docs/AccountSubscriptionMessage.md +0 -25
  101. package/src/docs/AccountTradeUpdate.md +0 -21
  102. package/src/docs/AccountTransactionUpdate.md +0 -31
  103. package/src/docs/AccountUpdate.md +0 -61
  104. package/src/docs/ActiveOrderUpdate.md +0 -65
  105. package/src/docs/AdjustIsolatedMarginRequest.md +0 -22
  106. package/src/docs/AdjustIsolatedMarginRequestSignedFields.md +0 -32
  107. package/src/docs/AdjustMarginOperation.md +0 -11
  108. package/src/docs/AffiliateIntervalOverview.md +0 -56
  109. package/src/docs/AffiliateLeaderDashboard.md +0 -38
  110. package/src/docs/AffiliateMetadata.md +0 -36
  111. package/src/docs/AffiliateMetadataFees.md +0 -31
  112. package/src/docs/AffiliateOnboardResponse.md +0 -22
  113. package/src/docs/AffiliateOverview.md +0 -46
  114. package/src/docs/AffiliateSummary.md +0 -30
  115. package/src/docs/Asset.md +0 -29
  116. package/src/docs/AssetConfig.md +0 -28
  117. package/src/docs/AuthApi.md +0 -243
  118. package/src/docs/AuthorizedWallet.md +0 -24
  119. package/src/docs/CampaignMetadata.md +0 -28
  120. package/src/docs/CampaignRewards.md +0 -18
  121. package/src/docs/CancelOrdersRequest.md +0 -23
  122. package/src/docs/CancelOrdersResponse.md +0 -21
  123. package/src/docs/CandlePriceType.md +0 -14
  124. package/src/docs/CandlestickUpdate.md +0 -41
  125. package/src/docs/CommandFailureReasonCode.md +0 -24
  126. package/src/docs/ContractsConfig.md +0 -31
  127. package/src/docs/CreateOrderRequest.md +0 -36
  128. package/src/docs/CreateOrderRequestSignedFields.md +0 -40
  129. package/src/docs/CreateOrderResponse.md +0 -20
  130. package/src/docs/EpochConfigs.md +0 -34
  131. package/src/docs/EpochConfigsResponse.md +0 -24
  132. package/src/docs/EpochMetadata.md +0 -30
  133. package/src/docs/Error.md +0 -22
  134. package/src/docs/ExchangeApi.md +0 -473
  135. package/src/docs/ExchangeInfoResponse.md +0 -30
  136. package/src/docs/FailedCommandType.md +0 -12
  137. package/src/docs/FeeConfigs.md +0 -33
  138. package/src/docs/FundingRateEntry.md +0 -24
  139. package/src/docs/GetAffiliateIntervalOverview200Response.md +0 -26
  140. package/src/docs/GetAffiliateLeaderDashboard200Response.md +0 -26
  141. package/src/docs/GetAffiliateOverview200Response.md +0 -26
  142. package/src/docs/IntervalMetadata.md +0 -28
  143. package/src/docs/IntervalRewards.md +0 -30
  144. package/src/docs/KlineInterval.md +0 -36
  145. package/src/docs/LoginRequest.md +0 -25
  146. package/src/docs/LoginResponse.md +0 -26
  147. package/src/docs/MarginType.md +0 -13
  148. package/src/docs/MarkPriceUpdate.md +0 -26
  149. package/src/docs/Market.md +0 -82
  150. package/src/docs/MarketDataStreamName.md +0 -142
  151. package/src/docs/MarketEventType.md +0 -25
  152. package/src/docs/MarketPriceUpdate.md +0 -26
  153. package/src/docs/MarketStatus.md +0 -16
  154. package/src/docs/MarketStreamMessage.md +0 -23
  155. package/src/docs/MarketStreamMessagePayload.md +0 -123
  156. package/src/docs/MarketSubscriptionMessage.md +0 -23
  157. package/src/docs/MarketSubscriptionStreams.md +0 -23
  158. package/src/docs/OnboardAffiliateRequest.md +0 -26
  159. package/src/docs/OnboardAffiliateRequestSocialUserNames.md +0 -27
  160. package/src/docs/OnboardRefereeRequest.md +0 -20
  161. package/src/docs/OpenOrderResponse.md +0 -64
  162. package/src/docs/Operators.md +0 -28
  163. package/src/docs/OraclePriceUpdate.md +0 -26
  164. package/src/docs/OrderCancelReason.md +0 -65
  165. package/src/docs/OrderCancellationFailureReason.md +0 -19
  166. package/src/docs/OrderCancellationUpdate.md +0 -35
  167. package/src/docs/OrderSide.md +0 -13
  168. package/src/docs/OrderStatus.md +0 -25
  169. package/src/docs/OrderTimeInForce.md +0 -15
  170. package/src/docs/OrderType.md +0 -29
  171. package/src/docs/OrderbookDepthResponse.md +0 -38
  172. package/src/docs/OrderbookDiffDepthUpdate.md +0 -30
  173. package/src/docs/OrderbookPartialDepthUpdate.md +0 -30
  174. package/src/docs/Position.md +0 -52
  175. package/src/docs/PositionSide.md +0 -13
  176. package/src/docs/RecentTradesUpdates.md +0 -20
  177. package/src/docs/RefereeOnboardResponse.md +0 -20
  178. package/src/docs/RefreshTokenRequest.md +0 -20
  179. package/src/docs/RefreshTokenResponse.md +0 -26
  180. package/src/docs/RewardsApi.md +0 -859
  181. package/src/docs/RewardsSummary.md +0 -26
  182. package/src/docs/SelfTradePreventionType.md +0 -15
  183. package/src/docs/StatsEntry.md +0 -26
  184. package/src/docs/StatsInterval.md +0 -14
  185. package/src/docs/StatsResponse.md +0 -20
  186. package/src/docs/StreamsApi.md +0 -126
  187. package/src/docs/SubscriptionResponseMessage.md +0 -23
  188. package/src/docs/SubscriptionType.md +0 -11
  189. package/src/docs/TickerAllUpdate.md +0 -20
  190. package/src/docs/TickerResponse.md +0 -82
  191. package/src/docs/TickerUpdate.md +0 -83
  192. package/src/docs/Trade.md +0 -54
  193. package/src/docs/TradeApi.md +0 -589
  194. package/src/docs/TradeSide.md +0 -13
  195. package/src/docs/TradeType.md +0 -15
  196. package/src/docs/TradingFees.md +0 -24
  197. package/src/docs/Transaction.md +0 -34
  198. package/src/docs/TransactionType.md +0 -25
  199. package/src/docs/UpdateAccountPreferenceRequest.md +0 -24
  200. package/src/docs/UpdateAffiliateFeeConfigRequest.md +0 -20
  201. package/src/docs/UserCampaignRewards.md +0 -48
  202. package/src/docs/WithdrawRequest.md +0 -22
  203. package/src/docs/WithdrawRequestSignedFields.md +0 -30
  204. package/src/git_push.sh +0 -57
  205. package/src/index.ts +0 -6
  206. package/src/request-signer.ts +0 -405
  207. package/src/sdk.ts +0 -982
  208. package/src/utils.ts +0 -6
  209. package/src/websocket.ts +0 -165
  210. package/tsconfig.json +0 -23
package/src/sdk.ts DELETED
@@ -1,982 +0,0 @@
1
- import {
2
- OrderType,
3
- OrderTimeInForce,
4
- SelfTradePreventionType,
5
- OrderSide,
6
- WithdrawRequestSignedFields,
7
- CancelOrdersRequest,
8
- AccountPositionLeverageUpdateRequestSignedFields,
9
- CreateOrderRequestSignedFields,
10
- CreateOrderRequest,
11
- AccountDataApi,
12
- AuthApi,
13
- ExchangeApi,
14
- TradeApi,
15
- LoginRequest,
16
- MarketStreamMessage,
17
- AccountStreamMessage,
18
- LoginResponse,
19
- ContractsConfig,
20
- AssetConfig,
21
- AccountAuthorizationRequestSignedFields,
22
- AdjustIsolatedMarginRequestSignedFields,
23
- AdjustMarginOperation,
24
- RewardsApi,
25
- UpdateAccountPreferenceRequest,
26
- } from './api';
27
-
28
- import { Configuration } from './configuration';
29
- import { IBluefinSigner } from './request-signer';
30
- import { WebSocket } from 'ws';
31
- import { IAsset, TxBuilder } from '@firefly-exchange/library-sui/dist/src/v3';
32
- import {
33
- CoinUtils,
34
- SuiClient,
35
- TransactionBlock,
36
- Ed25519Keypair,
37
- decodeSuiPrivateKey,
38
- } from '@firefly-exchange/library-sui';
39
- import { toHex } from '@mysten/bcs';
40
-
41
- interface EnvironmentConfig {
42
- [key: string]: {
43
- authHost: string;
44
- apiHost: string;
45
- tradeHost: string;
46
- marketWsHost: string;
47
- accountWsHost: string;
48
- };
49
- }
50
-
51
- const environmentConfig: EnvironmentConfig = {
52
- mainnet: {
53
- authHost: 'https://auth.api.sui-prod.bluefin.io',
54
- apiHost: 'https://api.sui-prod.bluefin.io',
55
- tradeHost: 'https://trade.api.sui-prod.bluefin.io',
56
- marketWsHost: 'wss://stream.api.sui-prod.bluefin.io/ws/market',
57
- accountWsHost: 'wss://stream.api.sui-prod.bluefin.io/ws/account',
58
- },
59
- testnet: {
60
- authHost: 'https://auth.api.sui-staging.bluefin.io',
61
- apiHost: 'https://api.sui-staging.bluefin.io',
62
- tradeHost: 'https://trade.api.sui-staging.bluefin.io',
63
- marketWsHost: 'wss://stream.api.sui-staging.bluefin.io/ws/market',
64
- accountWsHost: 'wss://stream.api.sui-staging.bluefin.io/ws/account',
65
- },
66
- devnet: {
67
- authHost: 'https://auth.api.sui-dev.bluefin.io',
68
- apiHost: 'https://api.sui-dev.bluefin.io',
69
- tradeHost: 'https://trade.api.sui-dev.bluefin.io',
70
- marketWsHost: 'wss://stream.api.sui-dev.bluefin.io/ws/market',
71
- accountWsHost: 'wss://stream.api.sui-dev.bluefin.io/ws/account',
72
- },
73
- };
74
-
75
- export interface OrderParams {
76
- clientOrderId: string;
77
- type: OrderType;
78
- symbol: string;
79
- priceE9: string;
80
- quantityE9: string;
81
- side: OrderSide;
82
- leverageE9: string;
83
- isIsolated: boolean;
84
- expiresAtMillis: number;
85
- reduceOnly?: boolean;
86
- postOnly?: boolean;
87
- timeInForce?: OrderTimeInForce;
88
- triggerPriceE9?: string;
89
- selfTradePreventionType?: SelfTradePreventionType;
90
- signerAddress?: string;
91
- }
92
-
93
- enum Services {
94
- Account,
95
- Exchange,
96
- Rewards,
97
- Trade,
98
- Auth,
99
- MarketWebsocket,
100
- AccountWebsocket,
101
- }
102
-
103
- export interface BluefinProSdkOptions {
104
- currentAccountAddress?: string;
105
- refreshToken?: string;
106
- refreshTokenValidForSeconds?: number;
107
- disableLoginPromptOnLogout?: boolean;
108
- currentTimeMs?: number;
109
- onLogout?: () => void;
110
- onAccessTokenUpdate?: (accessToken: string) => void;
111
-
112
- // if needed to point to different services
113
- authHost?: string;
114
- apiHost?: string;
115
- tradeHost?: string;
116
- marketWsHost?: string;
117
- accountWsHost?: string;
118
- }
119
-
120
- export interface InitializeOptions {
121
- refreshTokenValidForSeconds?: number;
122
- readOnly?: boolean;
123
- }
124
-
125
- export class BluefinProSdk {
126
- private static readonly TOKEN_REFRESH_THRESHOLD_PERCENTAGE = 0.8;
127
- private readonly configs: Partial<Record<Services, Configuration>> = {};
128
- public readonly exchangeDataApi: ExchangeApi;
129
- public readonly rewardsDataApi: RewardsApi;
130
- public readonly accountDataApi: AccountDataApi;
131
- private readonly tradeApi: TradeApi;
132
- private readonly authApi: AuthApi;
133
- private initializeOptions: InitializeOptions | undefined;
134
- private tokenResponse: LoginResponse | null;
135
- private tokenSetAtSeconds: number | null;
136
- private isConnected: boolean;
137
- private updateTokenTimeout: ReturnType<typeof setTimeout> | null;
138
- private contractsConfig: ContractsConfig | undefined;
139
- private assets: Array<AssetConfig> | undefined;
140
- private txBuilder: TxBuilder | undefined;
141
- private currentAccountAddress: string | undefined;
142
- private disableLoginPromptOnLogout: boolean;
143
- private onLogout?: () => void;
144
- private onAccessTokenUpdate?: (accessToken: string) => void;
145
- private isRefreshing: boolean;
146
- private refreshPromise: Promise<void> | null;
147
- private visibilityChangeHandler?: () => void;
148
- private onlineHandler?: () => void;
149
- private offlineHandler?: () => void;
150
- private timeOffsetMs: number;
151
-
152
- constructor(
153
- private readonly bfSigner: IBluefinSigner,
154
- private environment: 'mainnet' | 'testnet' | 'devnet' = 'mainnet',
155
- private suiClient: SuiClient,
156
- opts?: BluefinProSdkOptions
157
- ) {
158
- this.currentAccountAddress = opts?.currentAccountAddress;
159
- this.isConnected = false;
160
- this.updateTokenTimeout = null;
161
- this.contractsConfig = undefined;
162
- this.tokenResponse = null;
163
- this.tokenSetAtSeconds = null;
164
- this.disableLoginPromptOnLogout = opts?.disableLoginPromptOnLogout ?? false;
165
- this.onLogout = opts?.onLogout;
166
- this.onAccessTokenUpdate = opts?.onAccessTokenUpdate;
167
- this.isRefreshing = false;
168
- this.refreshPromise = null;
169
- this.visibilityChangeHandler = undefined;
170
- this.onlineHandler = undefined;
171
- this.offlineHandler = undefined;
172
-
173
- // Initialize time offset based on provided currentTimeMs
174
- if (opts?.currentTimeMs !== undefined) {
175
- this.timeOffsetMs = opts.currentTimeMs - Date.now();
176
- } else {
177
- this.timeOffsetMs = 0;
178
- }
179
-
180
- if (opts?.refreshToken && opts?.refreshTokenValidForSeconds) {
181
- this.tokenResponse = {
182
- accessToken: '',
183
- accessTokenValidForSeconds: 0,
184
- refreshToken: opts.refreshToken,
185
- refreshTokenValidForSeconds: opts.refreshTokenValidForSeconds,
186
- };
187
- // Set timestamp when refresh token was provided
188
- this.tokenSetAtSeconds = Date.now() / 1000;
189
- }
190
-
191
- const defaultConfig = environmentConfig[this.environment];
192
-
193
- const basePaths = {
194
- authHost: opts?.authHost ?? defaultConfig.authHost,
195
- apiHost: opts?.apiHost ?? defaultConfig.apiHost,
196
- tradeHost: opts?.tradeHost ?? defaultConfig.tradeHost,
197
- marketWsHost: opts?.marketWsHost ?? defaultConfig.marketWsHost,
198
- accountWsHost: opts?.accountWsHost ?? defaultConfig.accountWsHost,
199
- };
200
-
201
- const authApiConfig = new Configuration({
202
- basePath: basePaths.authHost,
203
- });
204
- this.configs[Services.Auth] = authApiConfig;
205
- this.authApi = new AuthApi(authApiConfig);
206
-
207
- const exchangeApiConfig = new Configuration({
208
- basePath: basePaths.apiHost,
209
- });
210
- this.configs[Services.Exchange] = exchangeApiConfig;
211
- this.exchangeDataApi = new ExchangeApi(exchangeApiConfig);
212
-
213
- const rewardsApiConfig = new Configuration({
214
- basePath: basePaths.apiHost,
215
- });
216
- this.configs[Services.Rewards] = rewardsApiConfig;
217
- this.rewardsDataApi = new RewardsApi(rewardsApiConfig);
218
-
219
- const accountDataApiConfig = new Configuration({
220
- basePath: basePaths.apiHost,
221
- });
222
- this.configs[Services.Account] = accountDataApiConfig;
223
- this.accountDataApi = new AccountDataApi(accountDataApiConfig);
224
-
225
- const tradeApiConfig = new Configuration({
226
- basePath: basePaths.tradeHost,
227
- });
228
- this.configs[Services.Trade] = tradeApiConfig;
229
- this.tradeApi = new TradeApi(tradeApiConfig);
230
-
231
- const marketWsConfig = new Configuration({
232
- basePath: basePaths.marketWsHost,
233
- });
234
- this.configs[Services.MarketWebsocket] = marketWsConfig;
235
-
236
- const accountWsConfig = new Configuration({
237
- basePath: basePaths.accountWsHost,
238
- });
239
- this.configs[Services.AccountWebsocket] = accountWsConfig;
240
- }
241
-
242
- public createWallet() {
243
- const wallet = Ed25519Keypair.generate();
244
- const signerKey = wallet.getSecretKey();
245
- const keyPair = decodeSuiPrivateKey(signerKey);
246
-
247
- const publicAddress = wallet.toSuiAddress();
248
-
249
- return {
250
- privateKey: toHex(keyPair.secretKey),
251
- publicAddress,
252
- };
253
- }
254
-
255
- public getTokenResponse() {
256
- return this.tokenResponse;
257
- }
258
-
259
- private generateSalt(): string {
260
- return (Date.now() + Math.floor(Math.random() * 1000000)).toString();
261
- }
262
-
263
- private getCurrentTimeMs(): number {
264
- return Date.now() + this.timeOffsetMs;
265
- }
266
-
267
- public updateCurrentTimeMs(currentTimeMs: number): void {
268
- this.timeOffsetMs = currentTimeMs - Date.now();
269
- }
270
-
271
- private isRefreshTokenValid(): boolean {
272
- if (!this.tokenResponse?.refreshToken || !this.tokenSetAtSeconds) {
273
- return false;
274
- }
275
-
276
- const currentTimeSeconds = Date.now() / 1000;
277
- const refreshTokenExpiryTime =
278
- this.tokenSetAtSeconds + this.tokenResponse.refreshTokenValidForSeconds;
279
-
280
- // Add 60 second buffer to prevent using token right at expiry
281
- return currentTimeSeconds < refreshTokenExpiryTime - 60;
282
- }
283
-
284
- private isAccessTokenExpired(): boolean {
285
- if (!this.tokenResponse?.accessToken || !this.tokenSetAtSeconds) {
286
- return true;
287
- }
288
-
289
- const currentTimeSeconds = Date.now() / 1000;
290
- const tokenLifetimeSeconds = this.tokenResponse.accessTokenValidForSeconds;
291
- const refreshAtLifetimePercentage =
292
- BluefinProSdk.TOKEN_REFRESH_THRESHOLD_PERCENTAGE;
293
- const refreshAtSeconds =
294
- this.tokenSetAtSeconds +
295
- tokenLifetimeSeconds * refreshAtLifetimePercentage;
296
-
297
- // Token is considered "expired" if we've passed the 80% lifetime mark
298
- return currentTimeSeconds >= refreshAtSeconds;
299
- }
300
-
301
- private async initializeTxBuilder() {
302
- this.txBuilder = new TxBuilder({
303
- AdminCap: '',
304
- ExternalDataStore: this.contractsConfig?.edsId || '',
305
- InternalDataStore: this.contractsConfig?.idsId || '',
306
- Operators: {
307
- admin: this.contractsConfig?.operators.admin || '',
308
- fee: this.contractsConfig?.operators.fee || '',
309
- funding: this.contractsConfig?.operators.funding || '',
310
- pruning: '',
311
- sequencer: this.contractsConfig?.operators.sequencer || '',
312
- },
313
- Package: this.contractsConfig?.currentContractAddress || '',
314
- Perpetuals: {},
315
- SupportedAssets:
316
- this.assets?.reduce((agg: Record<string, IAsset>, x: AssetConfig) => {
317
- agg[x.symbol] = { ...x, coinType: x.assetType };
318
- return agg;
319
- }, {}) || {},
320
- TreasuryCap: '',
321
- UpgradeCap: '',
322
- });
323
- }
324
-
325
- public async initialize(options?: InitializeOptions): Promise<void> {
326
- this.initializeOptions = options;
327
- await this.setContractsConfig();
328
- await this.initializeTxBuilder();
329
- await this.loginAndUpdateToken();
330
- this.setupVisibilityChangeListener();
331
- this.setupNetworkChangeListener();
332
- }
333
-
334
- private async setContractsConfig() {
335
- const response = await this.exchangeDataApi.getExchangeInfo();
336
- this.contractsConfig = response.data.contractsConfig;
337
- this.assets = response.data.assets;
338
- }
339
-
340
- private async loginAndUpdateToken(): Promise<void> {
341
- await this.login();
342
-
343
- // Safety check - if login failed or logout was called, tokenResponse might be null
344
- if (!this.tokenResponse) {
345
- throw new Error('Login failed - no token response available');
346
- }
347
-
348
- this.isConnected = true;
349
-
350
- this.configs[Services.Account]!.accessToken =
351
- this.tokenResponse.accessToken;
352
- this.configs[Services.Trade]!.accessToken = this.tokenResponse.accessToken;
353
- this.configs[Services.Rewards]!.accessToken =
354
- this.tokenResponse.accessToken;
355
-
356
- // Notify about token refresh
357
- this.onAccessTokenUpdate?.(this.tokenResponse.accessToken);
358
-
359
- // Schedule the next token refresh
360
- this.scheduleTokenRefresh();
361
- }
362
-
363
- private scheduleTokenRefresh(): void {
364
- // Clear any existing timeout
365
- if (this.updateTokenTimeout) {
366
- clearTimeout(this.updateTokenTimeout);
367
- this.updateTokenTimeout = null;
368
- }
369
-
370
- if (
371
- !this.isConnected ||
372
- !this.tokenResponse ||
373
- !this.tokenSetAtSeconds
374
- ) {
375
- return;
376
- }
377
-
378
- // Calculate when to refresh: at 80% of token lifetime (or 20% before expiry)
379
- const currentTimeSeconds = Date.now() / 1000;
380
- const tokenLifetimeSeconds = this.tokenResponse.accessTokenValidForSeconds;
381
- const refreshAtLifetimePercentage =
382
- BluefinProSdk.TOKEN_REFRESH_THRESHOLD_PERCENTAGE;
383
- const refreshAtSeconds =
384
- this.tokenSetAtSeconds +
385
- tokenLifetimeSeconds * refreshAtLifetimePercentage;
386
- const millisecondsUntilRefresh =
387
- (refreshAtSeconds - currentTimeSeconds) * 1000;
388
-
389
- const delayMs = Math.max(millisecondsUntilRefresh, 0);
390
-
391
- console.log(
392
- `Scheduling token refresh in ${Math.round(delayMs / 1000)} seconds`
393
- );
394
-
395
- this.updateTokenTimeout = setTimeout(() => {
396
- this.refreshToken(true);
397
- }, delayMs);
398
- }
399
-
400
- private setupVisibilityChangeListener(): void {
401
- // Only set up in browser environment
402
- if (typeof document !== 'undefined') {
403
- this.visibilityChangeHandler = () => {
404
- if (document.visibilityState === 'visible' && this.isConnected) {
405
- console.log('Page became visible, checking token status');
406
- this.handleVisible();
407
- }
408
- };
409
-
410
- document.addEventListener(
411
- 'visibilitychange',
412
- this.visibilityChangeHandler
413
- );
414
- }
415
- }
416
-
417
- private setupNetworkChangeListener(): void {
418
- // Only set up in browser environment
419
- if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
420
- window.addEventListener('online', this.handleOnline);
421
- window.addEventListener('offline', this.handleOffline);
422
- }
423
- }
424
-
425
- private async handleVisible(): Promise<void> {
426
- if (!this.tokenResponse || !this.tokenSetAtSeconds) {
427
- return;
428
- }
429
-
430
- this.refreshToken();
431
- }
432
-
433
- private async handleOnline(): Promise<void> {
434
- if (!this.isConnected || !this.tokenResponse || !this.tokenSetAtSeconds) {
435
- return;
436
- }
437
-
438
- console.log(
439
- 'Network reconnected, resuming token refresh and checking token status'
440
- );
441
-
442
- this.refreshToken(true);
443
- }
444
-
445
- private handleOffline(): void {
446
- if (this.updateTokenTimeout) {
447
- clearTimeout(this.updateTokenTimeout);
448
- this.updateTokenTimeout = null;
449
- }
450
- }
451
-
452
- private async login(): Promise<void> {
453
- console.log('Logging in to get the access token');
454
- this.tokenSetAtSeconds = Date.now() / 1000;
455
-
456
- if (!this.currentAccountAddress) {
457
- this.currentAccountAddress = this.bfSigner.getAddress();
458
- }
459
-
460
- console.log(`Logging in as ${this.currentAccountAddress}`);
461
-
462
- // Check if we have a valid refresh token first
463
- if (this.tokenResponse?.refreshToken && this.isRefreshTokenValid()) {
464
- try {
465
- console.log('Attempting to refresh token using refresh token');
466
- const response = await this.authApi.authTokenRefreshPut({
467
- refreshToken: this.tokenResponse.refreshToken,
468
- });
469
- this.tokenResponse = response.data;
470
- console.log('Token refreshed successfully');
471
- return;
472
- } catch (e) {
473
- console.error('Error refreshing token:', e);
474
-
475
- // Throw the error to let the caller handle it
476
- throw new Error(
477
- `Token refresh failed: ${
478
- e instanceof Error ? e.message : 'Unknown error'
479
- }`
480
- );
481
- }
482
- }
483
-
484
- // Fallback to full login only if refresh token is not available or invalid
485
- // This should only happen during initial login or when refresh token is truly expired
486
- try {
487
- const loginRequest: LoginRequest = {
488
- accountAddress: this.currentAccountAddress,
489
- signedAtMillis: this.getCurrentTimeMs(),
490
- audience: 'api',
491
- };
492
-
493
- const signature = await this.bfSigner.signLoginRequest(loginRequest);
494
- const response = await this.authApi.authV2TokenPost(
495
- signature,
496
- loginRequest,
497
- this.initializeOptions?.refreshTokenValidForSeconds,
498
- this.initializeOptions?.readOnly
499
- );
500
- this.tokenResponse = response.data;
501
- } catch (e) {
502
- console.error('Full login failed:', e);
503
-
504
- // If this fails and we're in a mode where logout should be disabled, don't logout
505
- if (this.disableLoginPromptOnLogout) {
506
- throw new Error(
507
- `Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`
508
- );
509
- }
510
-
511
- // Otherwise, throw the error and let the caller decide
512
- throw new Error(
513
- `Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`
514
- );
515
- }
516
- }
517
-
518
- public async getAccessToken() {
519
- if (!this.tokenResponse) {
520
- await this.login();
521
- }
522
- return this.tokenResponse!.accessToken;
523
- }
524
-
525
- public async getOpenOrders(symbol?: string) {
526
- return await this.tradeApi.getOpenOrders(symbol);
527
- }
528
-
529
- public async getStandbyOrders(symbol?: string) {
530
- return await this.tradeApi.getStandbyOrders(symbol);
531
- }
532
-
533
- public async updateLeverage(symbol: string, leverageE9: string) {
534
- if (!this.contractsConfig) {
535
- throw new Error('Missing contracts config');
536
- }
537
-
538
- const signedFields: AccountPositionLeverageUpdateRequestSignedFields = {
539
- accountAddress: this.currentAccountAddress!,
540
- idsId: this.contractsConfig.idsId,
541
- symbol: symbol,
542
- leverageE9: leverageE9,
543
- salt: this.generateSalt(),
544
- signedAtMillis: this.getCurrentTimeMs(),
545
- };
546
-
547
- const request = await this.bfSigner.signLeverageUpdateRequest(signedFields);
548
-
549
- return await this.tradeApi.putLeverageUpdate({
550
- signedFields,
551
- signature: request,
552
- });
553
- }
554
-
555
- public async createOrder(params: OrderParams): Promise<any> {
556
- if (!this.contractsConfig) {
557
- throw new Error('Missing contracts config');
558
- }
559
-
560
- const signedFields: CreateOrderRequestSignedFields = {
561
- symbol: params.symbol,
562
- idsId: this.contractsConfig.idsId,
563
- accountAddress: this.currentAccountAddress!,
564
- priceE9: params.priceE9,
565
- quantityE9: params.quantityE9,
566
- side: params.side,
567
- leverageE9: params.leverageE9,
568
- isIsolated: params.isIsolated,
569
- salt: this.generateSalt(),
570
- expiresAtMillis: params.expiresAtMillis,
571
- signedAtMillis: this.getCurrentTimeMs(),
572
- };
573
-
574
- const signature = await this.bfSigner.signOrderRequest(signedFields);
575
-
576
- const createOrderRequest: CreateOrderRequest = {
577
- signedFields,
578
- signature,
579
- clientOrderId: params.clientOrderId,
580
- type: params.type,
581
- reduceOnly: params.reduceOnly ?? false,
582
- postOnly: params.postOnly ?? false,
583
- timeInForce: params.timeInForce,
584
- triggerPriceE9: params.triggerPriceE9,
585
- selfTradePreventionType: params.selfTradePreventionType,
586
- };
587
- console.log('Creating order:', createOrderRequest);
588
- return await this.tradeApi.postCreateOrder(createOrderRequest);
589
- }
590
-
591
- public async cancelOrder(cancelOrdersRequest: CancelOrdersRequest) {
592
- return await this.tradeApi.cancelOrders(cancelOrdersRequest);
593
- }
594
-
595
- public async cancelStandbyOrder(cancelOrdersRequest: CancelOrdersRequest) {
596
- return await this.tradeApi.cancelStandbyOrders(cancelOrdersRequest);
597
- }
598
-
599
- public async withdraw(assetSymbol: string, amountE9: string) {
600
- const exchangeInfo = await this.exchangeDataApi.getExchangeInfo();
601
- const asset = exchangeInfo.data.assets.find(
602
- (asset) => asset.symbol === assetSymbol
603
- );
604
-
605
- if (!asset) {
606
- throw new Error(`Asset ${assetSymbol} not found`);
607
- }
608
-
609
- if (!this.contractsConfig) {
610
- throw new Error('Missing contractsConfig');
611
- }
612
-
613
- const signedFields: WithdrawRequestSignedFields = {
614
- assetSymbol,
615
- edsId: this.contractsConfig.edsId,
616
- accountAddress: this.currentAccountAddress!,
617
- amountE9,
618
- salt: this.generateSalt(),
619
- signedAtMillis: this.getCurrentTimeMs(),
620
- };
621
-
622
- const signature = await this.bfSigner.signWithdrawRequest(signedFields);
623
-
624
- await this.tradeApi.postWithdraw({
625
- signedFields,
626
- signature,
627
- });
628
- console.log('Withdraw request sent:', signedFields);
629
- }
630
-
631
- public async authorizeAccount(accountAddress: string, alias?: string) {
632
- if (!this.contractsConfig) {
633
- throw new Error('Missing contractsConfig');
634
- }
635
-
636
- const signedFields: AccountAuthorizationRequestSignedFields = {
637
- accountAddress: this.currentAccountAddress!,
638
- idsId: this.contractsConfig.idsId,
639
- authorizedAccountAddress: accountAddress,
640
- salt: this.generateSalt(),
641
- signedAtMillis: this.getCurrentTimeMs(),
642
- };
643
-
644
- const signature = await this.bfSigner.signAccountAuthorizationRequest(
645
- signedFields,
646
- true
647
- );
648
-
649
- await this.tradeApi.putAuthorizeAccount({
650
- signedFields,
651
- signature,
652
- alias,
653
- });
654
- console.log('Authorize account request sent:', signedFields);
655
- }
656
-
657
- public async deauthorizeAccount(accountAddress: string) {
658
- if (!this.contractsConfig) {
659
- throw new Error('Missing contractsConfig');
660
- }
661
-
662
- const signedFields: AccountAuthorizationRequestSignedFields = {
663
- accountAddress: this.currentAccountAddress!,
664
- idsId: this.contractsConfig.idsId,
665
- authorizedAccountAddress: accountAddress,
666
- salt: this.generateSalt(),
667
- signedAtMillis: this.getCurrentTimeMs(),
668
- };
669
-
670
- const signature = await this.bfSigner.signAccountAuthorizationRequest(
671
- signedFields,
672
- false
673
- );
674
-
675
- await this.tradeApi.putDeauthorizeAccount({
676
- signedFields,
677
- signature,
678
- });
679
- console.log('Deauthorize account request sent:', signedFields);
680
- }
681
-
682
- public async adjustIsolatedMargin(
683
- symbol: string,
684
- amountE9: string,
685
- add: boolean
686
- ) {
687
- if (!this.contractsConfig) {
688
- throw new Error('Missing contractsConfig');
689
- }
690
-
691
- const signedFields: AdjustIsolatedMarginRequestSignedFields = {
692
- symbol,
693
- idsId: this.contractsConfig.idsId,
694
- accountAddress: this.currentAccountAddress!,
695
- operation: add
696
- ? AdjustMarginOperation.Add
697
- : AdjustMarginOperation.Subtract,
698
- quantityE9: amountE9,
699
- salt: this.generateSalt(),
700
- signedAtMillis: this.getCurrentTimeMs(),
701
- };
702
-
703
- const signature = await this.bfSigner.signAdjustIsolatedMarginRequest(
704
- signedFields
705
- );
706
-
707
- await this.tradeApi.putAdjustIsolatedMargin({
708
- signedFields,
709
- signature,
710
- });
711
- console.log('Adjust isolated margin request sent:', signedFields);
712
- }
713
-
714
- public async getAccountPreferences() {
715
- return await this.accountDataApi.getAccountPreferences();
716
- }
717
-
718
- public async updateAccountPreferences(
719
- updateAccountPreferenceRequest: UpdateAccountPreferenceRequest
720
- ) {
721
- return await this.accountDataApi.putAccountPreferences(
722
- updateAccountPreferenceRequest
723
- );
724
- }
725
-
726
- public async deposit(amountE9: string, accountAddress?: string) {
727
- const assetSymbol = 'USDC';
728
- const txb = new TransactionBlock();
729
- const assetType = this.assets?.find(
730
- (x) => x.symbol === assetSymbol
731
- )?.assetType;
732
- if (!assetType) {
733
- throw new Error('Missing USDC asset type');
734
- }
735
- const [splitCoin, mergedCoin] = await CoinUtils.createCoinWithBalance(
736
- this.suiClient,
737
- txb,
738
- amountE9,
739
- assetType,
740
- this.currentAccountAddress || this.bfSigner.getAddress()
741
- );
742
-
743
- this.txBuilder?.depositToAssetBank(
744
- assetSymbol,
745
- accountAddress ||
746
- this.currentAccountAddress ||
747
- this.bfSigner.getAddress(),
748
- amountE9,
749
- splitCoin,
750
- {
751
- txBlock: txb,
752
- }
753
- );
754
-
755
- if (mergedCoin) {
756
- txb.transferObjects(
757
- [mergedCoin],
758
- this.currentAccountAddress || this.bfSigner.getAddress()
759
- );
760
- }
761
- if (splitCoin) {
762
- txb.transferObjects(
763
- [splitCoin],
764
- this.currentAccountAddress || this.bfSigner.getAddress()
765
- );
766
- }
767
-
768
- return this.bfSigner.executeTx(txb, this.suiClient);
769
- }
770
-
771
- public async refreshToken(force: boolean = false): Promise<void> {
772
- if (!this.isConnected) return;
773
-
774
- // If already refreshing, wait for that operation to complete
775
- if (this.isRefreshing) {
776
- if (this.refreshPromise) {
777
- await this.refreshPromise;
778
- }
779
- return;
780
- }
781
-
782
- // Check if token needs refreshing
783
- if (!this.isAccessTokenExpired() && !force) {
784
- return;
785
- }
786
-
787
- this.isRefreshing = true;
788
- this.refreshPromise = this.performTokenRefresh();
789
-
790
- try {
791
- await this.refreshPromise;
792
- } finally {
793
- this.isRefreshing = false;
794
- this.refreshPromise = null;
795
- }
796
- }
797
-
798
- private async performTokenRefresh(): Promise<void> {
799
- const maxRetries = 5;
800
- let retryCount = 0;
801
- let consecutiveNetworkErrors = 0;
802
-
803
- while (retryCount < maxRetries) {
804
- try {
805
- console.log(
806
- `Refreshing token (attempt ${retryCount + 1}/${maxRetries})`
807
- );
808
-
809
- // Check if refresh token is still valid
810
- if (!this.isRefreshTokenValid()) {
811
- console.log('Refresh token is expired or invalid, triggering logout');
812
- this.logout();
813
- return;
814
- }
815
-
816
- await this.loginAndUpdateToken();
817
- console.log('Token refreshed successfully');
818
- consecutiveNetworkErrors = 0; // Reset network error counter on success
819
- return;
820
- } catch (error) {
821
- retryCount++;
822
- console.error(`Error refreshing token (attempt ${retryCount}):`, error);
823
-
824
- // Check if this is a network error that we should retry
825
- const isNetworkError =
826
- error instanceof Error &&
827
- (error.message.includes('Network Error') ||
828
- error.message.includes('ERR_NETWORK') ||
829
- error.message.includes('ECONNREFUSED') ||
830
- error.message.includes('timeout') ||
831
- error.message.includes('ERR_NETWORK_IO_SUSPENDED'));
832
-
833
- // Check if we're currently offline (browser environment only)
834
- const isOffline =
835
- typeof navigator !== 'undefined' &&
836
- 'onLine' in navigator &&
837
- !navigator.onLine;
838
-
839
- if (isNetworkError) {
840
- consecutiveNetworkErrors++;
841
- }
842
-
843
- // If we're offline, don't count this as a "real" error - just wait for network to come back
844
- if (isOffline) {
845
- console.log(
846
- 'User is offline, will retry when network comes back online'
847
- );
848
- return;
849
- }
850
-
851
- // If it's not a network error and login failed (tokenResponse is null), logout immediately
852
- if (!isNetworkError && !this.tokenResponse) {
853
- console.error('Login failed permanently, triggering logout');
854
- this.logout();
855
- return;
856
- }
857
-
858
- // If we've had too many consecutive network errors, pause refresh attempts temporarily
859
- if (consecutiveNetworkErrors >= 10) {
860
- // Clear any scheduled refresh
861
- if (this.updateTokenTimeout) {
862
- clearTimeout(this.updateTokenTimeout);
863
- this.updateTokenTimeout = null;
864
- }
865
-
866
- setTimeout(() => {
867
- console.log('Resuming token refresh attempts');
868
- this.scheduleTokenRefresh(); // Reschedule refresh when resuming
869
- }, 300000); // 5 minutes
870
- return;
871
- }
872
-
873
- if (retryCount >= maxRetries && !isOffline) {
874
- if (isNetworkError) {
875
- console.warn(
876
- 'Max retries reached due to network errors, will retry later'
877
- );
878
- return; // Don't logout for network issues, just wait for next interval
879
- } else {
880
- console.error('Max retries reached, triggering logout');
881
- this.logout();
882
- return;
883
- }
884
- }
885
-
886
- // Exponential backoff: wait 2^retryCount seconds, but cap at 30 seconds for network errors
887
- const baseDelay = isNetworkError ? 5000 : 2000; // Start with 5s for network errors, 2s for others
888
- const delayMs = Math.min(
889
- baseDelay * Math.pow(2, retryCount - 1),
890
- 30000
891
- );
892
- console.log(`Retrying in ${delayMs}ms...`);
893
- await new Promise((resolve) => setTimeout(resolve, delayMs));
894
- }
895
- }
896
- }
897
-
898
- public async createAccountDataStreamListener(
899
- handler: (data: AccountStreamMessage) => Promise<void>
900
- ): Promise<WebSocket> {
901
- return new Promise((resolve) => {
902
- if (!this.tokenResponse) {
903
- throw new Error('Missing tokenResponse');
904
- }
905
- const ws = new WebSocket(
906
- this.configs[Services.AccountWebsocket]!.basePath!,
907
- {
908
- headers: {
909
- Authorization: `Bearer ${this.tokenResponse.accessToken}`,
910
- },
911
- }
912
- );
913
- ws.onmessage = async (event) => {
914
- await handler(JSON.parse(<string>event.data));
915
- };
916
- ws.on('open', () => {
917
- resolve(ws);
918
- });
919
- });
920
- }
921
-
922
- public async createMarketDataStreamListener(
923
- handler: (data: MarketStreamMessage) => Promise<void>
924
- ): Promise<WebSocket> {
925
- return new Promise((resolve) => {
926
- const ws = new WebSocket(
927
- this.configs[Services.MarketWebsocket]!.basePath!
928
- );
929
- ws.onmessage = async (event) => {
930
- await handler(JSON.parse(<string>event.data));
931
- };
932
- ws.on('open', () => {
933
- resolve(ws);
934
- });
935
- });
936
- }
937
-
938
- public async logout(): Promise<void> {
939
- console.log('Logging out');
940
- if (this.updateTokenTimeout) {
941
- clearTimeout(this.updateTokenTimeout);
942
- this.updateTokenTimeout = null;
943
- }
944
- this.tokenResponse = null;
945
- this.tokenSetAtSeconds = null;
946
- this.isConnected = false;
947
-
948
- this.onLogout?.();
949
- }
950
-
951
- public async dispose(): Promise<void> {
952
- console.log('Disposing SDK resources');
953
-
954
- if (this.updateTokenTimeout) {
955
- clearTimeout(this.updateTokenTimeout);
956
- this.updateTokenTimeout = null;
957
- }
958
-
959
- // Clean up visibility change listener
960
- if (this.visibilityChangeHandler && typeof document !== 'undefined') {
961
- document.removeEventListener(
962
- 'visibilitychange',
963
- this.visibilityChangeHandler
964
- );
965
- this.visibilityChangeHandler = undefined;
966
- }
967
-
968
- // Clean up network change listeners
969
- if (typeof window !== 'undefined') {
970
- if (this.onlineHandler) {
971
- window.removeEventListener('online', this.onlineHandler);
972
- this.onlineHandler = undefined;
973
- }
974
- if (this.offlineHandler) {
975
- window.removeEventListener('offline', this.offlineHandler);
976
- this.offlineHandler = undefined;
977
- }
978
- }
979
-
980
- this.isConnected = false;
981
- }
982
- }