@bluefin-exchange/pro-sdk 2.0.0-beta.0 → 2.0.0-beta.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.
- package/README.md +2 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/api.js +609 -810
- package/dist/esm/src/api.js.map +1 -1
- package/dist/esm/src/base.js +5 -2
- package/dist/esm/src/base.js.map +1 -1
- package/dist/esm/src/common.js +23 -39
- package/dist/esm/src/common.js.map +1 -1
- package/dist/esm/src/configuration.js +62 -2
- package/dist/esm/src/configuration.js.map +1 -1
- package/dist/esm/src/index.js +6 -6
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/request-signer.js +61 -81
- package/dist/esm/src/request-signer.js.map +1 -1
- package/dist/esm/src/sdk.js +529 -570
- package/dist/esm/src/sdk.js.map +1 -1
- package/dist/esm/src/websocket.js +69 -85
- package/dist/esm/src/websocket.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/src/api.d.ts +7 -7
- package/dist/types/src/api.d.ts.map +1 -1
- package/dist/types/src/base.d.ts +1 -1
- package/dist/types/src/base.d.ts.map +1 -1
- package/dist/types/src/common.d.ts +2 -2
- package/dist/types/src/common.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -6
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/request-signer.d.ts +3 -2
- package/dist/types/src/request-signer.d.ts.map +1 -1
- package/dist/types/src/sdk.d.ts +9 -9
- package/dist/types/src/sdk.d.ts.map +1 -1
- package/package.json +6 -7
- package/dist/esm/example.js +0 -276
- package/dist/esm/example.js.map +0 -1
- package/dist/types/example.d.ts +0 -2
- package/dist/types/example.d.ts.map +0 -1
package/dist/esm/src/sdk.js
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var _a;
|
|
11
|
-
import { AccountDataApi, AuthApi, ExchangeApi, TradeApi, AdjustMarginOperation, RewardsApi, } from './api';
|
|
12
|
-
import { Configuration } from './configuration';
|
|
1
|
+
import { AccountDataApi, AuthApi, ExchangeApi, TradeApi, AdjustMarginOperation, RewardsApi, } from './api.js';
|
|
2
|
+
import { Configuration } from './configuration.js';
|
|
13
3
|
import { WebSocket } from 'ws';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
import { RewardsDistributorInteractor } from '@firefly-exchange/library-sui/index';
|
|
4
|
+
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
5
|
+
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
6
|
+
import { Transaction as TransactionBlock } from '@mysten/sui/transactions';
|
|
18
7
|
import { toHex } from '@mysten/bcs';
|
|
19
8
|
import globalAxios from 'axios';
|
|
20
9
|
// Keep HTTP/2 as default in Node.js. Consumers can opt out with
|
|
@@ -22,7 +11,7 @@ import globalAxios from 'axios';
|
|
|
22
11
|
if (typeof process !== 'undefined' &&
|
|
23
12
|
process.versions &&
|
|
24
13
|
process.versions.node) {
|
|
25
|
-
const requestedHttpVersion = Number.parseInt(
|
|
14
|
+
const requestedHttpVersion = Number.parseInt(process.env.BLUEFIN_SDK_HTTP_VERSION ?? '2', 10);
|
|
26
15
|
globalAxios.defaults.httpVersion = requestedHttpVersion === 2 ? 2 : 1;
|
|
27
16
|
}
|
|
28
17
|
const environmentConfig = {
|
|
@@ -48,6 +37,15 @@ const environmentConfig = {
|
|
|
48
37
|
accountWsHost: 'wss://stream.api.sui-dev.bluefin.io/ws/account',
|
|
49
38
|
},
|
|
50
39
|
};
|
|
40
|
+
async function loadLibrarySuiRoot() {
|
|
41
|
+
return await import('@firefly-exchange/library-sui');
|
|
42
|
+
}
|
|
43
|
+
async function loadLibrarySuiV3() {
|
|
44
|
+
return await import('@firefly-exchange/library-sui/v3');
|
|
45
|
+
}
|
|
46
|
+
async function loadRewardsDistributorInteractor() {
|
|
47
|
+
return await import('@firefly-exchange/library-sui/index');
|
|
48
|
+
}
|
|
51
49
|
var Services;
|
|
52
50
|
(function (Services) {
|
|
53
51
|
Services[Services["Account"] = 0] = "Account";
|
|
@@ -59,52 +57,60 @@ var Services;
|
|
|
59
57
|
Services[Services["AccountWebsocket"] = 6] = "AccountWebsocket";
|
|
60
58
|
})(Services || (Services = {}));
|
|
61
59
|
export class BluefinProSdk {
|
|
60
|
+
bfSigner;
|
|
61
|
+
environment;
|
|
62
|
+
static TOKEN_REFRESH_THRESHOLD_PERCENTAGE = 0.8;
|
|
63
|
+
configs = {};
|
|
64
|
+
exchangeDataApi;
|
|
65
|
+
rewardsDataApi;
|
|
66
|
+
accountDataApi;
|
|
67
|
+
tradeApi;
|
|
68
|
+
authApi;
|
|
69
|
+
initializeOptions;
|
|
70
|
+
tokenResponse;
|
|
71
|
+
tokenSetAtSeconds;
|
|
72
|
+
isConnected;
|
|
73
|
+
updateTokenTimeout;
|
|
74
|
+
contractsConfig;
|
|
75
|
+
assets;
|
|
76
|
+
txBuilder;
|
|
77
|
+
currentAccountAddress;
|
|
78
|
+
disableLoginPromptOnLogout;
|
|
79
|
+
onLogout;
|
|
80
|
+
onAccessTokenUpdate;
|
|
81
|
+
isRefreshing;
|
|
82
|
+
refreshTokenPromise;
|
|
83
|
+
visibilityChangeHandler;
|
|
84
|
+
onlineHandler;
|
|
85
|
+
offlineHandler;
|
|
86
|
+
timeOffsetMs;
|
|
87
|
+
suiClient;
|
|
62
88
|
constructor(bfSigner, environment = 'mainnet', suiClient, opts) {
|
|
63
|
-
var _a, _b, _c, _d, _e, _f;
|
|
64
89
|
this.bfSigner = bfSigner;
|
|
65
90
|
this.environment = environment;
|
|
66
|
-
this.configs = {};
|
|
67
|
-
/**
|
|
68
|
-
* @description
|
|
69
|
-
* build gasless transaction payload bytes
|
|
70
|
-
* @param tx transcation block
|
|
71
|
-
* @returns string
|
|
72
|
-
* */
|
|
73
|
-
this.buildGaslessTxPayloadBytes = (txb) => __awaiter(this, void 0, void 0, function* () {
|
|
74
|
-
try {
|
|
75
|
-
return yield SuiBlocks.buildGaslessTxPayloadBytes(txb, this.suiClient);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
throw new Error(error instanceof Error
|
|
79
|
-
? error.message
|
|
80
|
-
: 'Build gasless tx payload bytes failed');
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
// SuiJsonRpcClient and ClientWithCoreApi are structurally compatible
|
|
84
|
-
// for all library-sui operations; cast once at the boundary.
|
|
85
91
|
this.suiClient = suiClient;
|
|
86
|
-
this.currentAccountAddress = opts
|
|
92
|
+
this.currentAccountAddress = opts?.currentAccountAddress;
|
|
87
93
|
this.isConnected = false;
|
|
88
94
|
this.updateTokenTimeout = null;
|
|
89
95
|
this.contractsConfig = undefined;
|
|
90
96
|
this.tokenResponse = null;
|
|
91
97
|
this.tokenSetAtSeconds = null;
|
|
92
|
-
this.disableLoginPromptOnLogout =
|
|
93
|
-
this.onLogout = opts
|
|
94
|
-
this.onAccessTokenUpdate = opts
|
|
98
|
+
this.disableLoginPromptOnLogout = opts?.disableLoginPromptOnLogout ?? false;
|
|
99
|
+
this.onLogout = opts?.onLogout;
|
|
100
|
+
this.onAccessTokenUpdate = opts?.onAccessTokenUpdate;
|
|
95
101
|
this.isRefreshing = false;
|
|
96
102
|
this.refreshTokenPromise = null;
|
|
97
103
|
this.visibilityChangeHandler = undefined;
|
|
98
104
|
this.onlineHandler = undefined;
|
|
99
105
|
this.offlineHandler = undefined;
|
|
100
106
|
// Initialize time offset based on provided currentTimeMs
|
|
101
|
-
if (
|
|
107
|
+
if (opts?.currentTimeMs !== undefined) {
|
|
102
108
|
this.timeOffsetMs = opts.currentTimeMs - Date.now();
|
|
103
109
|
}
|
|
104
110
|
else {
|
|
105
111
|
this.timeOffsetMs = 0;
|
|
106
112
|
}
|
|
107
|
-
if (
|
|
113
|
+
if (opts?.refreshToken && opts?.refreshTokenValidForSeconds) {
|
|
108
114
|
this.tokenResponse = {
|
|
109
115
|
accessToken: '',
|
|
110
116
|
accessTokenValidForSeconds: 0,
|
|
@@ -116,11 +122,11 @@ export class BluefinProSdk {
|
|
|
116
122
|
}
|
|
117
123
|
const defaultConfig = environmentConfig[this.environment];
|
|
118
124
|
const basePaths = {
|
|
119
|
-
authHost:
|
|
120
|
-
apiHost:
|
|
121
|
-
tradeHost:
|
|
122
|
-
marketWsHost:
|
|
123
|
-
accountWsHost:
|
|
125
|
+
authHost: opts?.authHost ?? defaultConfig.authHost,
|
|
126
|
+
apiHost: opts?.apiHost ?? defaultConfig.apiHost,
|
|
127
|
+
tradeHost: opts?.tradeHost ?? defaultConfig.tradeHost,
|
|
128
|
+
marketWsHost: opts?.marketWsHost ?? defaultConfig.marketWsHost,
|
|
129
|
+
accountWsHost: opts?.accountWsHost ?? defaultConfig.accountWsHost,
|
|
124
130
|
};
|
|
125
131
|
const authApiConfig = new Configuration({
|
|
126
132
|
basePath: basePaths.authHost,
|
|
@@ -184,8 +190,7 @@ export class BluefinProSdk {
|
|
|
184
190
|
this.timeOffsetMs = currentTimeMs - Date.now();
|
|
185
191
|
}
|
|
186
192
|
isRefreshTokenValid() {
|
|
187
|
-
|
|
188
|
-
if (!((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.refreshToken) || !this.tokenSetAtSeconds) {
|
|
193
|
+
if (!this.tokenResponse?.refreshToken || !this.tokenSetAtSeconds) {
|
|
189
194
|
return false;
|
|
190
195
|
}
|
|
191
196
|
const currentTimeSeconds = Date.now() / 1000;
|
|
@@ -194,8 +199,7 @@ export class BluefinProSdk {
|
|
|
194
199
|
return currentTimeSeconds < refreshTokenExpiryTime - 60;
|
|
195
200
|
}
|
|
196
201
|
isAccessTokenExpired() {
|
|
197
|
-
|
|
198
|
-
if (!((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.accessToken) || !this.tokenSetAtSeconds) {
|
|
202
|
+
if (!this.tokenResponse?.accessToken || !this.tokenSetAtSeconds) {
|
|
199
203
|
return true;
|
|
200
204
|
}
|
|
201
205
|
const currentTimeSeconds = Date.now() / 1000;
|
|
@@ -206,62 +210,53 @@ export class BluefinProSdk {
|
|
|
206
210
|
// Token is considered "expired" if we've passed the 80% lifetime mark
|
|
207
211
|
return currentTimeSeconds >= refreshAtSeconds;
|
|
208
212
|
}
|
|
209
|
-
initializeTxBuilder() {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!this.tokenResponse) {
|
|
257
|
-
throw new Error('Login failed - no token response available');
|
|
258
|
-
}
|
|
259
|
-
this.isConnected = true;
|
|
260
|
-
// Notify about token refresh
|
|
261
|
-
(_a = this.onAccessTokenUpdate) === null || _a === void 0 ? void 0 : _a.call(this, this.tokenResponse.accessToken);
|
|
262
|
-
// Schedule the next token refresh
|
|
263
|
-
this.scheduleTokenRefresh();
|
|
264
|
-
});
|
|
213
|
+
async initializeTxBuilder() {
|
|
214
|
+
const { TxBuilder } = await loadLibrarySuiV3();
|
|
215
|
+
this.txBuilder = new TxBuilder({
|
|
216
|
+
AdminCap: '',
|
|
217
|
+
ExternalDataStore: this.contractsConfig?.edsId || '',
|
|
218
|
+
InternalDataStore: this.contractsConfig?.idsId || '',
|
|
219
|
+
Operators: {
|
|
220
|
+
admin: this.contractsConfig?.operators.admin || '',
|
|
221
|
+
fee: this.contractsConfig?.operators.fee || '',
|
|
222
|
+
funding: this.contractsConfig?.operators.funding || '',
|
|
223
|
+
pruning: '',
|
|
224
|
+
sequencer: this.contractsConfig?.operators.sequencer || '',
|
|
225
|
+
},
|
|
226
|
+
Package: this.contractsConfig?.currentContractAddress || '',
|
|
227
|
+
Perpetuals: {},
|
|
228
|
+
SupportedAssets: this.assets?.reduce((agg, x) => {
|
|
229
|
+
agg[x.symbol] = { ...x, coinType: x.assetType };
|
|
230
|
+
return agg;
|
|
231
|
+
}, {}) || {},
|
|
232
|
+
TreasuryCap: '',
|
|
233
|
+
UpgradeCap: '',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
async initialize(options) {
|
|
237
|
+
this.initializeOptions = options;
|
|
238
|
+
await this.setContractsConfig();
|
|
239
|
+
await this.initializeTxBuilder();
|
|
240
|
+
await this.loginAndUpdateToken();
|
|
241
|
+
this.setupVisibilityChangeListener();
|
|
242
|
+
this.setupNetworkChangeListener();
|
|
243
|
+
}
|
|
244
|
+
async setContractsConfig() {
|
|
245
|
+
const response = await this.exchangeDataApi.getExchangeInfo();
|
|
246
|
+
this.contractsConfig = response.data.contractsConfig;
|
|
247
|
+
this.assets = response.data.assets;
|
|
248
|
+
}
|
|
249
|
+
async loginAndUpdateToken() {
|
|
250
|
+
await this.login();
|
|
251
|
+
// Safety check - if login failed or logout was called, tokenResponse might be null
|
|
252
|
+
if (!this.tokenResponse) {
|
|
253
|
+
throw new Error('Login failed - no token response available');
|
|
254
|
+
}
|
|
255
|
+
this.isConnected = true;
|
|
256
|
+
// Notify about token refresh
|
|
257
|
+
this.onAccessTokenUpdate?.(this.tokenResponse.accessToken);
|
|
258
|
+
// Schedule the next token refresh
|
|
259
|
+
this.scheduleTokenRefresh();
|
|
265
260
|
}
|
|
266
261
|
scheduleTokenRefresh() {
|
|
267
262
|
// Clear any existing timeout
|
|
@@ -304,22 +299,18 @@ export class BluefinProSdk {
|
|
|
304
299
|
window.addEventListener('offline', this.handleOffline);
|
|
305
300
|
}
|
|
306
301
|
}
|
|
307
|
-
handleVisible() {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.refreshToken();
|
|
313
|
-
});
|
|
302
|
+
async handleVisible() {
|
|
303
|
+
if (!this.tokenResponse || !this.tokenSetAtSeconds) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.refreshToken();
|
|
314
307
|
}
|
|
315
|
-
handleOnline() {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.refreshToken(true);
|
|
322
|
-
});
|
|
308
|
+
async handleOnline() {
|
|
309
|
+
if (!this.isConnected || !this.tokenResponse || !this.tokenSetAtSeconds) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
console.log('Network reconnected, resuming token refresh and checking token status');
|
|
313
|
+
this.refreshToken(true);
|
|
323
314
|
}
|
|
324
315
|
handleOffline() {
|
|
325
316
|
if (this.updateTokenTimeout) {
|
|
@@ -327,517 +318,486 @@ export class BluefinProSdk {
|
|
|
327
318
|
this.updateTokenTimeout = null;
|
|
328
319
|
}
|
|
329
320
|
}
|
|
330
|
-
login() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// Check if we have a valid refresh token first
|
|
339
|
-
if (((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.refreshToken) && this.isRefreshTokenValid()) {
|
|
340
|
-
try {
|
|
341
|
-
console.log('Attempting to refresh token using refresh token');
|
|
342
|
-
const response = yield this.authApi.authTokenRefreshPut({
|
|
343
|
-
refreshToken: this.tokenResponse.refreshToken,
|
|
344
|
-
});
|
|
345
|
-
this.tokenResponse = response.data;
|
|
346
|
-
this.tokenSetAtSeconds = Date.now() / 1000;
|
|
347
|
-
console.log('Token refreshed successfully');
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
catch (e) {
|
|
351
|
-
console.error('Error refreshing token:', e);
|
|
352
|
-
// Throw the error to let the caller handle it
|
|
353
|
-
throw new Error(`Token refresh failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// Fallback to full login only if refresh token is not available or invalid
|
|
357
|
-
// This should only happen during initial login or when refresh token is truly expired
|
|
321
|
+
async login() {
|
|
322
|
+
console.log('Logging in to get the access token');
|
|
323
|
+
if (!this.currentAccountAddress) {
|
|
324
|
+
this.currentAccountAddress = this.bfSigner.getAddress();
|
|
325
|
+
}
|
|
326
|
+
console.log(`Logging in as ${this.currentAccountAddress}`);
|
|
327
|
+
// Check if we have a valid refresh token first
|
|
328
|
+
if (this.tokenResponse?.refreshToken && this.isRefreshTokenValid()) {
|
|
358
329
|
try {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
};
|
|
364
|
-
const signature = yield this.bfSigner.signLoginRequest(loginRequest);
|
|
365
|
-
const response = yield this.authApi.authV2TokenPost(signature, loginRequest, (_b = this.initializeOptions) === null || _b === void 0 ? void 0 : _b.refreshTokenValidForSeconds, (_c = this.initializeOptions) === null || _c === void 0 ? void 0 : _c.readOnly);
|
|
330
|
+
console.log('Attempting to refresh token using refresh token');
|
|
331
|
+
const response = await this.authApi.authTokenRefreshPut({
|
|
332
|
+
refreshToken: this.tokenResponse.refreshToken,
|
|
333
|
+
});
|
|
366
334
|
this.tokenResponse = response.data;
|
|
367
335
|
this.tokenSetAtSeconds = Date.now() / 1000;
|
|
336
|
+
console.log('Token refreshed successfully');
|
|
337
|
+
return;
|
|
368
338
|
}
|
|
369
339
|
catch (e) {
|
|
370
|
-
console.error('
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
340
|
+
console.error('Error refreshing token:', e);
|
|
341
|
+
// Throw the error to let the caller handle it
|
|
342
|
+
throw new Error(`Token refresh failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Fallback to full login only if refresh token is not available or invalid
|
|
346
|
+
// This should only happen during initial login or when refresh token is truly expired
|
|
347
|
+
try {
|
|
348
|
+
const loginRequest = {
|
|
349
|
+
accountAddress: this.currentAccountAddress,
|
|
350
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
351
|
+
audience: 'api',
|
|
352
|
+
};
|
|
353
|
+
const signature = await this.bfSigner.signLoginRequest(loginRequest);
|
|
354
|
+
const response = await this.authApi.authV2TokenPost(signature, loginRequest, this.initializeOptions?.refreshTokenValidForSeconds, this.initializeOptions?.readOnly);
|
|
355
|
+
this.tokenResponse = response.data;
|
|
356
|
+
this.tokenSetAtSeconds = Date.now() / 1000;
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
console.error('Full login failed:', e);
|
|
360
|
+
// If this fails and we're in a mode where logout should be disabled, don't logout
|
|
361
|
+
if (this.disableLoginPromptOnLogout) {
|
|
376
362
|
throw new Error(`Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
377
363
|
}
|
|
378
|
-
|
|
364
|
+
// Otherwise, throw the error and let the caller decide
|
|
365
|
+
throw new Error(`Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
366
|
+
}
|
|
379
367
|
}
|
|
380
|
-
getAccessToken() {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (this.
|
|
389
|
-
|
|
390
|
-
yield this.refreshToken();
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
else if (this.isAccessTokenExpired()) {
|
|
394
|
-
yield this.login();
|
|
395
|
-
}
|
|
396
|
-
if (!this.tokenResponse) {
|
|
397
|
-
throw new Error('Access token unavailable after refresh');
|
|
368
|
+
async getAccessToken() {
|
|
369
|
+
if (!this.tokenResponse) {
|
|
370
|
+
await this.login();
|
|
371
|
+
}
|
|
372
|
+
if (!this.tokenResponse) {
|
|
373
|
+
throw new Error('Unable to obtain access token');
|
|
374
|
+
}
|
|
375
|
+
if (this.isConnected) {
|
|
376
|
+
if (this.isAccessTokenExpired()) {
|
|
377
|
+
await this.refreshToken();
|
|
398
378
|
}
|
|
399
|
-
|
|
400
|
-
|
|
379
|
+
}
|
|
380
|
+
else if (this.isAccessTokenExpired()) {
|
|
381
|
+
await this.login();
|
|
382
|
+
}
|
|
383
|
+
if (!this.tokenResponse) {
|
|
384
|
+
throw new Error('Access token unavailable after refresh');
|
|
385
|
+
}
|
|
386
|
+
return this.tokenResponse.accessToken;
|
|
401
387
|
}
|
|
402
|
-
getOpenOrders(symbol) {
|
|
403
|
-
return
|
|
404
|
-
return yield this.tradeApi.getOpenOrders(symbol);
|
|
405
|
-
});
|
|
388
|
+
async getOpenOrders(symbol) {
|
|
389
|
+
return await this.tradeApi.getOpenOrders(symbol);
|
|
406
390
|
}
|
|
407
|
-
getStandbyOrders(symbol) {
|
|
408
|
-
return
|
|
409
|
-
return yield this.tradeApi.getStandbyOrders(symbol);
|
|
410
|
-
});
|
|
391
|
+
async getStandbyOrders(symbol) {
|
|
392
|
+
return await this.tradeApi.getStandbyOrders(symbol);
|
|
411
393
|
}
|
|
412
|
-
updateLeverage(symbol, leverageE9) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
signature: request,
|
|
429
|
-
});
|
|
394
|
+
async updateLeverage(symbol, leverageE9) {
|
|
395
|
+
if (!this.contractsConfig) {
|
|
396
|
+
throw new Error('Missing contracts config');
|
|
397
|
+
}
|
|
398
|
+
const signedFields = {
|
|
399
|
+
accountAddress: this.currentAccountAddress,
|
|
400
|
+
idsId: this.contractsConfig.idsId,
|
|
401
|
+
symbol: symbol,
|
|
402
|
+
leverageE9: leverageE9,
|
|
403
|
+
salt: this.generateSalt(),
|
|
404
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
405
|
+
};
|
|
406
|
+
const request = await this.bfSigner.signLeverageUpdateRequest(signedFields);
|
|
407
|
+
return await this.tradeApi.putLeverageUpdate({
|
|
408
|
+
signedFields,
|
|
409
|
+
signature: request,
|
|
430
410
|
});
|
|
431
411
|
}
|
|
432
|
-
createOrder(params) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
console.log('Creating order:', createOrderRequest);
|
|
465
|
-
return yield this.tradeApi.postCreateOrder(createOrderRequest);
|
|
466
|
-
});
|
|
412
|
+
async createOrder(params) {
|
|
413
|
+
if (!this.contractsConfig) {
|
|
414
|
+
throw new Error('Missing contracts config');
|
|
415
|
+
}
|
|
416
|
+
const signedFields = {
|
|
417
|
+
symbol: params.symbol,
|
|
418
|
+
idsId: this.contractsConfig.idsId,
|
|
419
|
+
accountAddress: this.currentAccountAddress,
|
|
420
|
+
priceE9: params.priceE9,
|
|
421
|
+
quantityE9: params.quantityE9,
|
|
422
|
+
side: params.side,
|
|
423
|
+
leverageE9: params.leverageE9,
|
|
424
|
+
isIsolated: params.isIsolated,
|
|
425
|
+
salt: this.generateSalt(),
|
|
426
|
+
expiresAtMillis: params.expiresAtMillis,
|
|
427
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
428
|
+
};
|
|
429
|
+
const signature = await this.bfSigner.signOrderRequest(signedFields);
|
|
430
|
+
const createOrderRequest = {
|
|
431
|
+
signedFields,
|
|
432
|
+
signature,
|
|
433
|
+
clientOrderId: params.clientOrderId,
|
|
434
|
+
type: params.type,
|
|
435
|
+
reduceOnly: params.reduceOnly ?? false,
|
|
436
|
+
postOnly: params.postOnly ?? false,
|
|
437
|
+
timeInForce: params.timeInForce,
|
|
438
|
+
triggerPriceE9: params.triggerPriceE9,
|
|
439
|
+
selfTradePreventionType: params.selfTradePreventionType,
|
|
440
|
+
twapConfig: params.twapConfig,
|
|
441
|
+
};
|
|
442
|
+
console.log('Creating order:', createOrderRequest);
|
|
443
|
+
return await this.tradeApi.postCreateOrder(createOrderRequest);
|
|
467
444
|
}
|
|
468
|
-
cancelOrder(cancelOrdersRequest) {
|
|
469
|
-
return
|
|
470
|
-
return yield this.tradeApi.cancelOrders(cancelOrdersRequest);
|
|
471
|
-
});
|
|
445
|
+
async cancelOrder(cancelOrdersRequest) {
|
|
446
|
+
return await this.tradeApi.cancelOrders(cancelOrdersRequest);
|
|
472
447
|
}
|
|
473
|
-
cancelStandbyOrder(cancelOrdersRequest) {
|
|
474
|
-
return
|
|
475
|
-
return yield this.tradeApi.cancelStandbyOrders(cancelOrdersRequest);
|
|
476
|
-
});
|
|
448
|
+
async cancelStandbyOrder(cancelOrdersRequest) {
|
|
449
|
+
return await this.tradeApi.cancelStandbyOrders(cancelOrdersRequest);
|
|
477
450
|
}
|
|
478
|
-
withdraw(assetSymbol, amountE9) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
signature,
|
|
500
|
-
});
|
|
501
|
-
console.log('Withdraw request sent:', signedFields);
|
|
451
|
+
async withdraw(assetSymbol, amountE9) {
|
|
452
|
+
const exchangeInfo = await this.exchangeDataApi.getExchangeInfo();
|
|
453
|
+
const asset = exchangeInfo.data.assets.find((asset) => asset.symbol === assetSymbol);
|
|
454
|
+
if (!asset) {
|
|
455
|
+
throw new Error(`Asset ${assetSymbol} not found`);
|
|
456
|
+
}
|
|
457
|
+
if (!this.contractsConfig) {
|
|
458
|
+
throw new Error('Missing contractsConfig');
|
|
459
|
+
}
|
|
460
|
+
const signedFields = {
|
|
461
|
+
assetSymbol,
|
|
462
|
+
edsId: this.contractsConfig.edsId,
|
|
463
|
+
accountAddress: this.currentAccountAddress,
|
|
464
|
+
amountE9,
|
|
465
|
+
salt: this.generateSalt(),
|
|
466
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
467
|
+
};
|
|
468
|
+
const signature = await this.bfSigner.signWithdrawRequest(signedFields);
|
|
469
|
+
await this.tradeApi.postWithdraw({
|
|
470
|
+
signedFields,
|
|
471
|
+
signature,
|
|
502
472
|
});
|
|
473
|
+
console.log('Withdraw request sent:', signedFields);
|
|
503
474
|
}
|
|
504
|
-
authorizeAccount(accountAddress, alias) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
alias,
|
|
521
|
-
});
|
|
522
|
-
console.log('Authorize account request sent:', signedFields);
|
|
475
|
+
async authorizeAccount(accountAddress, alias) {
|
|
476
|
+
if (!this.contractsConfig) {
|
|
477
|
+
throw new Error('Missing contractsConfig');
|
|
478
|
+
}
|
|
479
|
+
const signedFields = {
|
|
480
|
+
accountAddress: this.currentAccountAddress,
|
|
481
|
+
idsId: this.contractsConfig.idsId,
|
|
482
|
+
authorizedAccountAddress: accountAddress,
|
|
483
|
+
salt: this.generateSalt(),
|
|
484
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
485
|
+
};
|
|
486
|
+
const signature = await this.bfSigner.signAccountAuthorizationRequest(signedFields, true);
|
|
487
|
+
await this.tradeApi.putAuthorizeAccount({
|
|
488
|
+
signedFields,
|
|
489
|
+
signature,
|
|
490
|
+
alias,
|
|
523
491
|
});
|
|
492
|
+
console.log('Authorize account request sent:', signedFields);
|
|
524
493
|
}
|
|
525
|
-
deauthorizeAccount(accountAddress) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
signature,
|
|
541
|
-
});
|
|
542
|
-
console.log('Deauthorize account request sent:', signedFields);
|
|
494
|
+
async deauthorizeAccount(accountAddress) {
|
|
495
|
+
if (!this.contractsConfig) {
|
|
496
|
+
throw new Error('Missing contractsConfig');
|
|
497
|
+
}
|
|
498
|
+
const signedFields = {
|
|
499
|
+
accountAddress: this.currentAccountAddress,
|
|
500
|
+
idsId: this.contractsConfig.idsId,
|
|
501
|
+
authorizedAccountAddress: accountAddress,
|
|
502
|
+
salt: this.generateSalt(),
|
|
503
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
504
|
+
};
|
|
505
|
+
const signature = await this.bfSigner.signAccountAuthorizationRequest(signedFields, false);
|
|
506
|
+
await this.tradeApi.putDeauthorizeAccount({
|
|
507
|
+
signedFields,
|
|
508
|
+
signature,
|
|
543
509
|
});
|
|
510
|
+
console.log('Deauthorize account request sent:', signedFields);
|
|
544
511
|
}
|
|
545
|
-
adjustIsolatedMargin(symbol, amountE9, add) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
signature,
|
|
565
|
-
});
|
|
566
|
-
console.log('Adjust isolated margin request sent:', signedFields);
|
|
512
|
+
async adjustIsolatedMargin(symbol, amountE9, add) {
|
|
513
|
+
if (!this.contractsConfig) {
|
|
514
|
+
throw new Error('Missing contractsConfig');
|
|
515
|
+
}
|
|
516
|
+
const signedFields = {
|
|
517
|
+
symbol,
|
|
518
|
+
idsId: this.contractsConfig.idsId,
|
|
519
|
+
accountAddress: this.currentAccountAddress,
|
|
520
|
+
operation: add
|
|
521
|
+
? AdjustMarginOperation.Add
|
|
522
|
+
: AdjustMarginOperation.Subtract,
|
|
523
|
+
quantityE9: amountE9,
|
|
524
|
+
salt: this.generateSalt(),
|
|
525
|
+
signedAtMillis: this.getCurrentTimeMs(),
|
|
526
|
+
};
|
|
527
|
+
const signature = await this.bfSigner.signAdjustIsolatedMarginRequest(signedFields);
|
|
528
|
+
await this.tradeApi.putAdjustIsolatedMargin({
|
|
529
|
+
signedFields,
|
|
530
|
+
signature,
|
|
567
531
|
});
|
|
532
|
+
console.log('Adjust isolated margin request sent:', signedFields);
|
|
568
533
|
}
|
|
569
|
-
getAccountPreferences() {
|
|
570
|
-
return
|
|
571
|
-
return yield this.accountDataApi.getAccountPreferences();
|
|
572
|
-
});
|
|
534
|
+
async getAccountPreferences() {
|
|
535
|
+
return await this.accountDataApi.getAccountPreferences();
|
|
573
536
|
}
|
|
574
|
-
updateAccountPreferences(updateAccountPreferenceRequest) {
|
|
575
|
-
return
|
|
576
|
-
return yield this.accountDataApi.putAccountPreferences(updateAccountPreferenceRequest);
|
|
577
|
-
});
|
|
537
|
+
async updateAccountPreferences(updateAccountPreferenceRequest) {
|
|
538
|
+
return await this.accountDataApi.putAccountPreferences(updateAccountPreferenceRequest);
|
|
578
539
|
}
|
|
579
540
|
// Update Account Group ID.
|
|
580
541
|
// If groupID is null, it will remove the account from its group.
|
|
581
542
|
// An account may only belong to 1 group at a time.
|
|
582
|
-
updateAccountGroupId(updateAccountGroupIdRequest) {
|
|
583
|
-
return
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
543
|
+
async updateAccountGroupId(updateAccountGroupIdRequest) {
|
|
544
|
+
return await this.accountDataApi.patchAccountGroupID(updateAccountGroupIdRequest);
|
|
545
|
+
}
|
|
546
|
+
async deposit(amountE9, accountAddress, args) {
|
|
547
|
+
const assetSymbol = 'USDC';
|
|
548
|
+
const txb = new TransactionBlock();
|
|
549
|
+
const { CoinUtils } = await loadLibrarySuiRoot();
|
|
550
|
+
const assetType = this.assets?.find((x) => x.symbol === assetSymbol)?.assetType;
|
|
551
|
+
if (!assetType) {
|
|
552
|
+
throw new Error('Missing USDC asset type');
|
|
553
|
+
}
|
|
554
|
+
const [splitCoin, mergedCoin] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, amountE9, assetType, this.currentAccountAddress || this.bfSigner.getAddress());
|
|
555
|
+
//build the tx
|
|
556
|
+
this.txBuilder?.depositToAssetBank(assetSymbol, accountAddress ||
|
|
557
|
+
this.currentAccountAddress ||
|
|
558
|
+
this.bfSigner.getAddress(), amountE9, splitCoin, {
|
|
559
|
+
txBlock: txb,
|
|
560
|
+
});
|
|
561
|
+
//add the transfer objects to the tx
|
|
562
|
+
if (mergedCoin) {
|
|
563
|
+
txb.transferObjects([mergedCoin], this.currentAccountAddress || this.bfSigner.getAddress());
|
|
564
|
+
}
|
|
565
|
+
if (splitCoin) {
|
|
566
|
+
txb.transferObjects([splitCoin], this.currentAccountAddress || this.bfSigner.getAddress());
|
|
567
|
+
}
|
|
568
|
+
if (args && args.sponsored) {
|
|
569
|
+
// build the gasless tx payload bytes
|
|
570
|
+
const gaslessTxPayloadBytes = await this.buildGaslessTxPayloadBytes(txb);
|
|
571
|
+
try {
|
|
572
|
+
const request = {
|
|
573
|
+
txBytes: gaslessTxPayloadBytes,
|
|
574
|
+
};
|
|
575
|
+
// sponsor gas for the transaction
|
|
576
|
+
const sponsorTxApiResponse = await this.accountDataApi.sponsorTx(request);
|
|
577
|
+
const txBlockFromBytes = TransactionBlock.from(sponsorTxApiResponse.data.txBytes);
|
|
578
|
+
// sign the transaction with user's wallet
|
|
579
|
+
const userSignedTx = await this.bfSigner.signTx(txBlockFromBytes, this.suiClient);
|
|
580
|
+
// execute the transaction with both user's signature and sponsor's signature
|
|
581
|
+
const response = await this.bfSigner.executeSponsoredTx(userSignedTx.bytes, userSignedTx.signature, sponsorTxApiResponse.data.signature, this.suiClient);
|
|
582
|
+
return response;
|
|
609
583
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
try {
|
|
614
|
-
const request = {
|
|
615
|
-
txBytes: gaslessTxPayloadBytes,
|
|
616
|
-
};
|
|
617
|
-
// sponsor gas for the transaction
|
|
618
|
-
const sponsorTxApiResponse = yield this.accountDataApi.sponsorTx(request);
|
|
619
|
-
const txBlockFromBytes = TransactionBlock.from(sponsorTxApiResponse.data.txBytes);
|
|
620
|
-
// sign the transaction with user's wallet
|
|
621
|
-
const userSignedTx = yield this.bfSigner.signTx(txBlockFromBytes, this.suiClient);
|
|
622
|
-
// execute the transaction with both user's signature and sponsor's signature
|
|
623
|
-
const response = yield this.bfSigner.executeSponsoredTx(userSignedTx.bytes, userSignedTx.signature, sponsorTxApiResponse.data.signature, this.suiClient);
|
|
624
|
-
return response;
|
|
625
|
-
}
|
|
626
|
-
catch (error) {
|
|
627
|
-
if (args === null || args === void 0 ? void 0 : args.fallbackToExecuteTx) {
|
|
628
|
-
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
629
|
-
}
|
|
630
|
-
throw error;
|
|
584
|
+
catch (error) {
|
|
585
|
+
if (args?.fallbackToExecuteTx) {
|
|
586
|
+
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
631
587
|
}
|
|
588
|
+
throw error;
|
|
632
589
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
593
|
+
}
|
|
637
594
|
}
|
|
638
595
|
/**
|
|
639
596
|
* Batch claim rewards on-chain using RewardsDistributorInteractor
|
|
640
597
|
* @param payload Array of claim payloads with signatures
|
|
641
598
|
* @returns Transaction response
|
|
642
599
|
*/
|
|
643
|
-
batchClaimRewards(payload) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
600
|
+
async batchClaimRewards(payload) {
|
|
601
|
+
// Get the rewards contract config from API (same as useClaimConfigQuery)
|
|
602
|
+
const { data } = await this.rewardsDataApi.getContractConfig();
|
|
603
|
+
if (!data) {
|
|
604
|
+
throw new Error('Failed to get rewards contract config');
|
|
605
|
+
}
|
|
606
|
+
// Get the signer from BluefinRequestSigner
|
|
607
|
+
const signer = this.bfSigner.wallet;
|
|
608
|
+
// Create the RewardsDistributorInteractor
|
|
609
|
+
const { RewardsDistributorInteractor } = await loadRewardsDistributorInteractor();
|
|
610
|
+
const interactor = new RewardsDistributorInteractor(this.suiClient, data, signer, this.bfSigner.isUIWallet());
|
|
611
|
+
// Transform payload to the format expected by claimRewardsBatch
|
|
612
|
+
const batch = payload.map(({ signature, sigPayload }) => ({
|
|
613
|
+
signature,
|
|
614
|
+
payload: sigPayload,
|
|
615
|
+
}));
|
|
616
|
+
// Execute the batch claim
|
|
617
|
+
const tx = await interactor.claimRewardsBatch(batch);
|
|
618
|
+
return tx;
|
|
619
|
+
}
|
|
620
|
+
async refreshToken(force = false) {
|
|
621
|
+
if (!this.isConnected)
|
|
622
|
+
return;
|
|
623
|
+
// If already refreshing, wait for that operation to complete
|
|
624
|
+
if (this.isRefreshing) {
|
|
625
|
+
if (this.refreshTokenPromise) {
|
|
626
|
+
await this.refreshTokenPromise;
|
|
649
627
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
// Check if token needs refreshing
|
|
631
|
+
if (!this.isAccessTokenExpired() && !force) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
this.isRefreshing = true;
|
|
635
|
+
try {
|
|
636
|
+
this.refreshTokenPromise = this.performTokenRefresh();
|
|
637
|
+
await this.refreshTokenPromise;
|
|
638
|
+
}
|
|
639
|
+
finally {
|
|
640
|
+
this.isRefreshing = false;
|
|
641
|
+
this.refreshTokenPromise = null;
|
|
642
|
+
}
|
|
663
643
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
644
|
+
async performTokenRefresh() {
|
|
645
|
+
const maxRetries = 5;
|
|
646
|
+
let retryCount = 0;
|
|
647
|
+
let consecutiveNetworkErrors = 0;
|
|
648
|
+
while (retryCount < maxRetries) {
|
|
649
|
+
try {
|
|
650
|
+
console.log(`Refreshing token (attempt ${retryCount + 1}/${maxRetries})`);
|
|
651
|
+
// Check if refresh token is still valid
|
|
652
|
+
if (!this.isRefreshTokenValid()) {
|
|
653
|
+
console.log('Refresh token is expired or invalid, triggering logout');
|
|
654
|
+
this.logout();
|
|
655
|
+
return;
|
|
672
656
|
}
|
|
657
|
+
await this.loginAndUpdateToken();
|
|
658
|
+
console.log('Token refreshed successfully');
|
|
659
|
+
consecutiveNetworkErrors = 0; // Reset network error counter on success
|
|
673
660
|
return;
|
|
674
661
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
662
|
+
catch (error) {
|
|
663
|
+
retryCount++;
|
|
664
|
+
console.error(`Error refreshing token (attempt ${retryCount}):`, error);
|
|
665
|
+
// Check if this is a network error that we should retry
|
|
666
|
+
const isNetworkError = error instanceof Error &&
|
|
667
|
+
(error.message.includes('Network Error') ||
|
|
668
|
+
error.message.includes('ERR_NETWORK') ||
|
|
669
|
+
error.message.includes('ECONNREFUSED') ||
|
|
670
|
+
error.message.includes('timeout') ||
|
|
671
|
+
error.message.includes('ERR_NETWORK_IO_SUSPENDED'));
|
|
672
|
+
// Check if we're currently offline (browser environment only)
|
|
673
|
+
const isOffline = typeof navigator !== 'undefined' &&
|
|
674
|
+
'onLine' in navigator &&
|
|
675
|
+
!navigator.onLine;
|
|
676
|
+
if (isNetworkError) {
|
|
677
|
+
consecutiveNetworkErrors++;
|
|
678
|
+
}
|
|
679
|
+
// If we're offline, don't count this as a "real" error - just wait for network to come back
|
|
680
|
+
if (isOffline) {
|
|
681
|
+
console.log('User is offline, will retry when network comes back online');
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
// If it's not a network error and login failed (tokenResponse is null), logout immediately
|
|
685
|
+
if (!isNetworkError && !this.tokenResponse) {
|
|
686
|
+
console.error('Login failed permanently, triggering logout');
|
|
687
|
+
this.logout();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
// If we've had too many consecutive network errors, pause refresh attempts temporarily
|
|
691
|
+
if (consecutiveNetworkErrors >= 10) {
|
|
692
|
+
// Clear any scheduled refresh
|
|
693
|
+
if (this.updateTokenTimeout) {
|
|
694
|
+
clearTimeout(this.updateTokenTimeout);
|
|
695
|
+
this.updateTokenTimeout = null;
|
|
703
696
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
697
|
+
setTimeout(() => {
|
|
698
|
+
console.log('Resuming token refresh attempts');
|
|
699
|
+
this.scheduleTokenRefresh(); // Reschedule refresh when resuming
|
|
700
|
+
}, 300000); // 5 minutes
|
|
707
701
|
return;
|
|
708
702
|
}
|
|
709
|
-
|
|
710
|
-
retryCount++;
|
|
711
|
-
console.error(`Error refreshing token (attempt ${retryCount}):`, error);
|
|
712
|
-
// Check if this is a network error that we should retry
|
|
713
|
-
const isNetworkError = error instanceof Error &&
|
|
714
|
-
(error.message.includes('Network Error') ||
|
|
715
|
-
error.message.includes('ERR_NETWORK') ||
|
|
716
|
-
error.message.includes('ECONNREFUSED') ||
|
|
717
|
-
error.message.includes('timeout') ||
|
|
718
|
-
error.message.includes('ERR_NETWORK_IO_SUSPENDED'));
|
|
719
|
-
// Check if we're currently offline (browser environment only)
|
|
720
|
-
const isOffline = typeof navigator !== 'undefined' &&
|
|
721
|
-
'onLine' in navigator &&
|
|
722
|
-
!navigator.onLine;
|
|
703
|
+
if (retryCount >= maxRetries && !isOffline) {
|
|
723
704
|
if (isNetworkError) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
// If we're offline, don't count this as a "real" error - just wait for network to come back
|
|
727
|
-
if (isOffline) {
|
|
728
|
-
console.log('User is offline, will retry when network comes back online');
|
|
729
|
-
return;
|
|
705
|
+
console.warn('Max retries reached due to network errors, will retry later');
|
|
706
|
+
return; // Don't logout for network issues, just wait for next interval
|
|
730
707
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
console.error('Login failed permanently, triggering logout');
|
|
708
|
+
else {
|
|
709
|
+
console.error('Max retries reached, triggering logout');
|
|
734
710
|
this.logout();
|
|
735
711
|
return;
|
|
736
712
|
}
|
|
737
|
-
// If we've had too many consecutive network errors, pause refresh attempts temporarily
|
|
738
|
-
if (consecutiveNetworkErrors >= 10) {
|
|
739
|
-
// Clear any scheduled refresh
|
|
740
|
-
if (this.updateTokenTimeout) {
|
|
741
|
-
clearTimeout(this.updateTokenTimeout);
|
|
742
|
-
this.updateTokenTimeout = null;
|
|
743
|
-
}
|
|
744
|
-
setTimeout(() => {
|
|
745
|
-
console.log('Resuming token refresh attempts');
|
|
746
|
-
this.scheduleTokenRefresh(); // Reschedule refresh when resuming
|
|
747
|
-
}, 300000); // 5 minutes
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
if (retryCount >= maxRetries && !isOffline) {
|
|
751
|
-
if (isNetworkError) {
|
|
752
|
-
console.warn('Max retries reached due to network errors, will retry later');
|
|
753
|
-
return; // Don't logout for network issues, just wait for next interval
|
|
754
|
-
}
|
|
755
|
-
else {
|
|
756
|
-
console.error('Max retries reached, triggering logout');
|
|
757
|
-
this.logout();
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
// Exponential backoff: wait 2^retryCount seconds, but cap at 30 seconds for network errors
|
|
762
|
-
const baseDelay = isNetworkError ? 5000 : 2000; // Start with 5s for network errors, 2s for others
|
|
763
|
-
const delayMs = Math.min(baseDelay * Math.pow(2, retryCount - 1), 30000);
|
|
764
|
-
console.log(`Retrying in ${delayMs}ms...`);
|
|
765
|
-
yield new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
766
713
|
}
|
|
714
|
+
// Exponential backoff: wait 2^retryCount seconds, but cap at 30 seconds for network errors
|
|
715
|
+
const baseDelay = isNetworkError ? 5000 : 2000; // Start with 5s for network errors, 2s for others
|
|
716
|
+
const delayMs = Math.min(baseDelay * Math.pow(2, retryCount - 1), 30000);
|
|
717
|
+
console.log(`Retrying in ${delayMs}ms...`);
|
|
718
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
767
719
|
}
|
|
768
|
-
}
|
|
720
|
+
}
|
|
769
721
|
}
|
|
770
|
-
createAccountDataStreamListener(handler) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
ws
|
|
783
|
-
resolve(ws);
|
|
784
|
-
});
|
|
722
|
+
async createAccountDataStreamListener(handler) {
|
|
723
|
+
const accessToken = await this.getAccessToken();
|
|
724
|
+
return new Promise((resolve) => {
|
|
725
|
+
const ws = new WebSocket(this.configs[Services.AccountWebsocket].basePath, {
|
|
726
|
+
headers: {
|
|
727
|
+
Authorization: `Bearer ${accessToken}`,
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
ws.onmessage = async (event) => {
|
|
731
|
+
await handler(JSON.parse(event.data));
|
|
732
|
+
};
|
|
733
|
+
ws.on('open', () => {
|
|
734
|
+
resolve(ws);
|
|
785
735
|
});
|
|
786
736
|
});
|
|
787
737
|
}
|
|
788
|
-
createMarketDataStreamListener(handler) {
|
|
789
|
-
return
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
ws
|
|
796
|
-
resolve(ws);
|
|
797
|
-
});
|
|
738
|
+
async createMarketDataStreamListener(handler) {
|
|
739
|
+
return new Promise((resolve) => {
|
|
740
|
+
const ws = new WebSocket(this.configs[Services.MarketWebsocket].basePath);
|
|
741
|
+
ws.onmessage = async (event) => {
|
|
742
|
+
await handler(JSON.parse(event.data));
|
|
743
|
+
};
|
|
744
|
+
ws.on('open', () => {
|
|
745
|
+
resolve(ws);
|
|
798
746
|
});
|
|
799
747
|
});
|
|
800
748
|
}
|
|
801
|
-
logout() {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
this.isConnected = false;
|
|
812
|
-
(_a = this.onLogout) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
813
|
-
});
|
|
749
|
+
async logout() {
|
|
750
|
+
console.log('Logging out');
|
|
751
|
+
if (this.updateTokenTimeout) {
|
|
752
|
+
clearTimeout(this.updateTokenTimeout);
|
|
753
|
+
this.updateTokenTimeout = null;
|
|
754
|
+
}
|
|
755
|
+
this.tokenResponse = null;
|
|
756
|
+
this.tokenSetAtSeconds = null;
|
|
757
|
+
this.isConnected = false;
|
|
758
|
+
this.onLogout?.();
|
|
814
759
|
}
|
|
815
|
-
dispose() {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
760
|
+
async dispose() {
|
|
761
|
+
console.log('Disposing SDK resources');
|
|
762
|
+
if (this.updateTokenTimeout) {
|
|
763
|
+
clearTimeout(this.updateTokenTimeout);
|
|
764
|
+
this.updateTokenTimeout = null;
|
|
765
|
+
}
|
|
766
|
+
// Clean up visibility change listener
|
|
767
|
+
if (this.visibilityChangeHandler && typeof document !== 'undefined') {
|
|
768
|
+
document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
769
|
+
this.visibilityChangeHandler = undefined;
|
|
770
|
+
}
|
|
771
|
+
// Clean up network change listeners
|
|
772
|
+
if (typeof window !== 'undefined') {
|
|
773
|
+
if (this.onlineHandler) {
|
|
774
|
+
window.removeEventListener('online', this.onlineHandler);
|
|
775
|
+
this.onlineHandler = undefined;
|
|
826
776
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
window.removeEventListener('online', this.onlineHandler);
|
|
831
|
-
this.onlineHandler = undefined;
|
|
832
|
-
}
|
|
833
|
-
if (this.offlineHandler) {
|
|
834
|
-
window.removeEventListener('offline', this.offlineHandler);
|
|
835
|
-
this.offlineHandler = undefined;
|
|
836
|
-
}
|
|
777
|
+
if (this.offlineHandler) {
|
|
778
|
+
window.removeEventListener('offline', this.offlineHandler);
|
|
779
|
+
this.offlineHandler = undefined;
|
|
837
780
|
}
|
|
838
|
-
|
|
839
|
-
|
|
781
|
+
}
|
|
782
|
+
this.isConnected = false;
|
|
840
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* @description
|
|
786
|
+
* build gasless transaction payload bytes
|
|
787
|
+
* @param tx transcation block
|
|
788
|
+
* @returns string
|
|
789
|
+
* */
|
|
790
|
+
buildGaslessTxPayloadBytes = async (txb) => {
|
|
791
|
+
try {
|
|
792
|
+
const { SuiBlocks } = await loadLibrarySuiRoot();
|
|
793
|
+
return await SuiBlocks.buildGaslessTxPayloadBytes(txb, this.suiClient);
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
throw new Error(error instanceof Error
|
|
797
|
+
? error.message
|
|
798
|
+
: 'Build gasless tx payload bytes failed');
|
|
799
|
+
}
|
|
800
|
+
};
|
|
841
801
|
/**
|
|
842
802
|
* @description
|
|
843
803
|
* Get the transaction builder instance
|
|
@@ -855,5 +815,4 @@ export class BluefinProSdk {
|
|
|
855
815
|
return this.bfSigner;
|
|
856
816
|
}
|
|
857
817
|
}
|
|
858
|
-
BluefinProSdk.TOKEN_REFRESH_THRESHOLD_PERCENTAGE = 0.8;
|
|
859
818
|
//# sourceMappingURL=sdk.js.map
|