@bluefin-exchange/pro-sdk 1.17.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 +4 -4
- package/dist/esm/index.js +1 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/api.js +1041 -1277
- package/dist/esm/src/api.js.map +1 -1
- package/dist/esm/src/base.js +12 -17
- package/dist/esm/src/base.js.map +1 -1
- package/dist/esm/src/common.js +31 -59
- package/dist/esm/src/common.js.map +1 -1
- package/dist/esm/src/configuration.js +63 -7
- package/dist/esm/src/configuration.js.map +1 -1
- package/dist/esm/src/index.js +6 -22
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/request-signer.js +65 -90
- package/dist/esm/src/request-signer.js.map +1 -1
- package/dist/esm/src/sdk.js +549 -595
- package/dist/esm/src/sdk.js.map +1 -1
- package/dist/esm/src/utils.js +1 -4
- package/dist/esm/src/utils.js.map +1 -1
- package/dist/esm/src/websocket.js +72 -92
- 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 +3 -3
- 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 +4 -3
- package/dist/types/src/request-signer.d.ts.map +1 -1
- package/dist/types/src/sdk.d.ts +11 -10
- package/dist/types/src/sdk.d.ts.map +1 -1
- package/package.json +16 -13
- package/dist/cjs/example.js +0 -277
- package/dist/cjs/example.js.map +0 -1
- package/dist/cjs/index.js +0 -18
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/src/api.js +0 -5729
- package/dist/cjs/src/api.js.map +0 -1
- package/dist/cjs/src/base.js +0 -69
- package/dist/cjs/src/base.js.map +0 -1
- package/dist/cjs/src/common.js +0 -162
- package/dist/cjs/src/common.js.map +0 -1
- package/dist/cjs/src/configuration.js +0 -45
- package/dist/cjs/src/configuration.js.map +0 -1
- package/dist/cjs/src/index.js +0 -23
- package/dist/cjs/src/index.js.map +0 -1
- package/dist/cjs/src/request-signer.js +0 -234
- package/dist/cjs/src/request-signer.js.map +0 -1
- package/dist/cjs/src/sdk.js +0 -864
- package/dist/cjs/src/sdk.js.map +0 -1
- package/dist/cjs/src/utils.js +0 -10
- package/dist/cjs/src/utils.js.map +0 -1
- package/dist/cjs/src/websocket.js +0 -159
- package/dist/cjs/src/websocket.js.map +0 -1
- package/dist/esm/example.js +0 -277
- 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,35 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
var _a;
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.BluefinProSdk = void 0;
|
|
17
|
-
const api_1 = require("./api");
|
|
18
|
-
const configuration_1 = require("./configuration");
|
|
19
|
-
const ws_1 = require("ws");
|
|
20
|
-
const v3_1 = require("@firefly-exchange/library-sui/v3");
|
|
21
|
-
const library_sui_1 = require("@firefly-exchange/library-sui");
|
|
22
|
-
// RewardsDistributorInteractor for reward claiming
|
|
23
|
-
const index_1 = require("@firefly-exchange/library-sui/index");
|
|
24
|
-
const bcs_1 = require("@mysten/bcs");
|
|
25
|
-
const axios_1 = __importDefault(require("axios"));
|
|
1
|
+
import { AccountDataApi, AuthApi, ExchangeApi, TradeApi, AdjustMarginOperation, RewardsApi, } from './api.js';
|
|
2
|
+
import { Configuration } from './configuration.js';
|
|
3
|
+
import { WebSocket } from 'ws';
|
|
4
|
+
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
5
|
+
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
6
|
+
import { Transaction as TransactionBlock } from '@mysten/sui/transactions';
|
|
7
|
+
import { toHex } from '@mysten/bcs';
|
|
8
|
+
import globalAxios from 'axios';
|
|
26
9
|
// Keep HTTP/2 as default in Node.js. Consumers can opt out with
|
|
27
10
|
// BLUEFIN_SDK_HTTP_VERSION=1.
|
|
28
11
|
if (typeof process !== 'undefined' &&
|
|
29
12
|
process.versions &&
|
|
30
13
|
process.versions.node) {
|
|
31
|
-
const requestedHttpVersion = Number.parseInt(
|
|
32
|
-
|
|
14
|
+
const requestedHttpVersion = Number.parseInt(process.env.BLUEFIN_SDK_HTTP_VERSION ?? '2', 10);
|
|
15
|
+
globalAxios.defaults.httpVersion = requestedHttpVersion === 2 ? 2 : 1;
|
|
33
16
|
}
|
|
34
17
|
const environmentConfig = {
|
|
35
18
|
mainnet: {
|
|
@@ -54,6 +37,15 @@ const environmentConfig = {
|
|
|
54
37
|
accountWsHost: 'wss://stream.api.sui-dev.bluefin.io/ws/account',
|
|
55
38
|
},
|
|
56
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
|
+
}
|
|
57
49
|
var Services;
|
|
58
50
|
(function (Services) {
|
|
59
51
|
Services[Services["Account"] = 0] = "Account";
|
|
@@ -64,51 +56,61 @@ var Services;
|
|
|
64
56
|
Services[Services["MarketWebsocket"] = 5] = "MarketWebsocket";
|
|
65
57
|
Services[Services["AccountWebsocket"] = 6] = "AccountWebsocket";
|
|
66
58
|
})(Services || (Services = {}));
|
|
67
|
-
class BluefinProSdk {
|
|
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;
|
|
68
88
|
constructor(bfSigner, environment = 'mainnet', suiClient, opts) {
|
|
69
|
-
var _a, _b, _c, _d, _e, _f;
|
|
70
89
|
this.bfSigner = bfSigner;
|
|
71
90
|
this.environment = environment;
|
|
72
91
|
this.suiClient = suiClient;
|
|
73
|
-
this.
|
|
74
|
-
/**
|
|
75
|
-
* @description
|
|
76
|
-
* build gasless transaction payload bytes
|
|
77
|
-
* @param tx transcation block
|
|
78
|
-
* @returns string
|
|
79
|
-
* */
|
|
80
|
-
this.buildGaslessTxPayloadBytes = (txb) => __awaiter(this, void 0, void 0, function* () {
|
|
81
|
-
try {
|
|
82
|
-
return yield library_sui_1.SuiBlocks.buildGaslessTxPayloadBytes(txb, this.suiClient);
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
throw new Error(error instanceof Error
|
|
86
|
-
? error.message
|
|
87
|
-
: 'Build gasless tx payload bytes failed');
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
this.currentAccountAddress = opts === null || opts === void 0 ? void 0 : opts.currentAccountAddress;
|
|
92
|
+
this.currentAccountAddress = opts?.currentAccountAddress;
|
|
91
93
|
this.isConnected = false;
|
|
92
94
|
this.updateTokenTimeout = null;
|
|
93
95
|
this.contractsConfig = undefined;
|
|
94
96
|
this.tokenResponse = null;
|
|
95
97
|
this.tokenSetAtSeconds = null;
|
|
96
|
-
this.disableLoginPromptOnLogout =
|
|
97
|
-
this.onLogout = opts
|
|
98
|
-
this.onAccessTokenUpdate = opts
|
|
98
|
+
this.disableLoginPromptOnLogout = opts?.disableLoginPromptOnLogout ?? false;
|
|
99
|
+
this.onLogout = opts?.onLogout;
|
|
100
|
+
this.onAccessTokenUpdate = opts?.onAccessTokenUpdate;
|
|
99
101
|
this.isRefreshing = false;
|
|
100
102
|
this.refreshTokenPromise = null;
|
|
101
103
|
this.visibilityChangeHandler = undefined;
|
|
102
104
|
this.onlineHandler = undefined;
|
|
103
105
|
this.offlineHandler = undefined;
|
|
104
106
|
// Initialize time offset based on provided currentTimeMs
|
|
105
|
-
if (
|
|
107
|
+
if (opts?.currentTimeMs !== undefined) {
|
|
106
108
|
this.timeOffsetMs = opts.currentTimeMs - Date.now();
|
|
107
109
|
}
|
|
108
110
|
else {
|
|
109
111
|
this.timeOffsetMs = 0;
|
|
110
112
|
}
|
|
111
|
-
if (
|
|
113
|
+
if (opts?.refreshToken && opts?.refreshTokenValidForSeconds) {
|
|
112
114
|
this.tokenResponse = {
|
|
113
115
|
accessToken: '',
|
|
114
116
|
accessTokenValidForSeconds: 0,
|
|
@@ -120,58 +122,58 @@ class BluefinProSdk {
|
|
|
120
122
|
}
|
|
121
123
|
const defaultConfig = environmentConfig[this.environment];
|
|
122
124
|
const basePaths = {
|
|
123
|
-
authHost:
|
|
124
|
-
apiHost:
|
|
125
|
-
tradeHost:
|
|
126
|
-
marketWsHost:
|
|
127
|
-
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,
|
|
128
130
|
};
|
|
129
|
-
const authApiConfig = new
|
|
131
|
+
const authApiConfig = new Configuration({
|
|
130
132
|
basePath: basePaths.authHost,
|
|
131
133
|
});
|
|
132
134
|
this.configs[Services.Auth] = authApiConfig;
|
|
133
|
-
this.authApi = new
|
|
135
|
+
this.authApi = new AuthApi(authApiConfig);
|
|
134
136
|
const boundGetAccessToken = this.getAccessToken.bind(this);
|
|
135
|
-
const exchangeApiConfig = new
|
|
137
|
+
const exchangeApiConfig = new Configuration({
|
|
136
138
|
basePath: basePaths.apiHost,
|
|
137
139
|
});
|
|
138
140
|
exchangeApiConfig.accessToken = boundGetAccessToken;
|
|
139
141
|
this.configs[Services.Exchange] = exchangeApiConfig;
|
|
140
|
-
this.exchangeDataApi = new
|
|
141
|
-
const rewardsApiConfig = new
|
|
142
|
+
this.exchangeDataApi = new ExchangeApi(exchangeApiConfig);
|
|
143
|
+
const rewardsApiConfig = new Configuration({
|
|
142
144
|
basePath: basePaths.apiHost,
|
|
143
145
|
});
|
|
144
146
|
rewardsApiConfig.accessToken = boundGetAccessToken;
|
|
145
147
|
this.configs[Services.Rewards] = rewardsApiConfig;
|
|
146
|
-
this.rewardsDataApi = new
|
|
147
|
-
const accountDataApiConfig = new
|
|
148
|
+
this.rewardsDataApi = new RewardsApi(rewardsApiConfig);
|
|
149
|
+
const accountDataApiConfig = new Configuration({
|
|
148
150
|
basePath: basePaths.apiHost,
|
|
149
151
|
});
|
|
150
152
|
accountDataApiConfig.accessToken = boundGetAccessToken;
|
|
151
153
|
this.configs[Services.Account] = accountDataApiConfig;
|
|
152
|
-
this.accountDataApi = new
|
|
153
|
-
const tradeApiConfig = new
|
|
154
|
+
this.accountDataApi = new AccountDataApi(accountDataApiConfig);
|
|
155
|
+
const tradeApiConfig = new Configuration({
|
|
154
156
|
basePath: basePaths.tradeHost,
|
|
155
157
|
});
|
|
156
158
|
tradeApiConfig.accessToken = boundGetAccessToken;
|
|
157
159
|
this.configs[Services.Trade] = tradeApiConfig;
|
|
158
|
-
this.tradeApi = new
|
|
159
|
-
const marketWsConfig = new
|
|
160
|
+
this.tradeApi = new TradeApi(tradeApiConfig);
|
|
161
|
+
const marketWsConfig = new Configuration({
|
|
160
162
|
basePath: basePaths.marketWsHost,
|
|
161
163
|
});
|
|
162
164
|
this.configs[Services.MarketWebsocket] = marketWsConfig;
|
|
163
|
-
const accountWsConfig = new
|
|
165
|
+
const accountWsConfig = new Configuration({
|
|
164
166
|
basePath: basePaths.accountWsHost,
|
|
165
167
|
});
|
|
166
168
|
this.configs[Services.AccountWebsocket] = accountWsConfig;
|
|
167
169
|
}
|
|
168
170
|
createWallet() {
|
|
169
|
-
const wallet =
|
|
171
|
+
const wallet = Ed25519Keypair.generate();
|
|
170
172
|
const signerKey = wallet.getSecretKey();
|
|
171
|
-
const keyPair =
|
|
173
|
+
const keyPair = decodeSuiPrivateKey(signerKey);
|
|
172
174
|
const publicAddress = wallet.toSuiAddress();
|
|
173
175
|
return {
|
|
174
|
-
privateKey:
|
|
176
|
+
privateKey: toHex(keyPair.secretKey),
|
|
175
177
|
publicAddress,
|
|
176
178
|
};
|
|
177
179
|
}
|
|
@@ -188,8 +190,7 @@ class BluefinProSdk {
|
|
|
188
190
|
this.timeOffsetMs = currentTimeMs - Date.now();
|
|
189
191
|
}
|
|
190
192
|
isRefreshTokenValid() {
|
|
191
|
-
|
|
192
|
-
if (!((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.refreshToken) || !this.tokenSetAtSeconds) {
|
|
193
|
+
if (!this.tokenResponse?.refreshToken || !this.tokenSetAtSeconds) {
|
|
193
194
|
return false;
|
|
194
195
|
}
|
|
195
196
|
const currentTimeSeconds = Date.now() / 1000;
|
|
@@ -198,8 +199,7 @@ class BluefinProSdk {
|
|
|
198
199
|
return currentTimeSeconds < refreshTokenExpiryTime - 60;
|
|
199
200
|
}
|
|
200
201
|
isAccessTokenExpired() {
|
|
201
|
-
|
|
202
|
-
if (!((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.accessToken) || !this.tokenSetAtSeconds) {
|
|
202
|
+
if (!this.tokenResponse?.accessToken || !this.tokenSetAtSeconds) {
|
|
203
203
|
return true;
|
|
204
204
|
}
|
|
205
205
|
const currentTimeSeconds = Date.now() / 1000;
|
|
@@ -210,62 +210,53 @@ class BluefinProSdk {
|
|
|
210
210
|
// Token is considered "expired" if we've passed the 80% lifetime mark
|
|
211
211
|
return currentTimeSeconds >= refreshAtSeconds;
|
|
212
212
|
}
|
|
213
|
-
initializeTxBuilder() {
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (!this.tokenResponse) {
|
|
261
|
-
throw new Error('Login failed - no token response available');
|
|
262
|
-
}
|
|
263
|
-
this.isConnected = true;
|
|
264
|
-
// Notify about token refresh
|
|
265
|
-
(_a = this.onAccessTokenUpdate) === null || _a === void 0 ? void 0 : _a.call(this, this.tokenResponse.accessToken);
|
|
266
|
-
// Schedule the next token refresh
|
|
267
|
-
this.scheduleTokenRefresh();
|
|
268
|
-
});
|
|
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();
|
|
269
260
|
}
|
|
270
261
|
scheduleTokenRefresh() {
|
|
271
262
|
// Clear any existing timeout
|
|
@@ -308,22 +299,18 @@ class BluefinProSdk {
|
|
|
308
299
|
window.addEventListener('offline', this.handleOffline);
|
|
309
300
|
}
|
|
310
301
|
}
|
|
311
|
-
handleVisible() {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
this.refreshToken();
|
|
317
|
-
});
|
|
302
|
+
async handleVisible() {
|
|
303
|
+
if (!this.tokenResponse || !this.tokenSetAtSeconds) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.refreshToken();
|
|
318
307
|
}
|
|
319
|
-
handleOnline() {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
this.refreshToken(true);
|
|
326
|
-
});
|
|
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);
|
|
327
314
|
}
|
|
328
315
|
handleOffline() {
|
|
329
316
|
if (this.updateTokenTimeout) {
|
|
@@ -331,517 +318,486 @@ class BluefinProSdk {
|
|
|
331
318
|
this.updateTokenTimeout = null;
|
|
332
319
|
}
|
|
333
320
|
}
|
|
334
|
-
login() {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// Check if we have a valid refresh token first
|
|
343
|
-
if (((_a = this.tokenResponse) === null || _a === void 0 ? void 0 : _a.refreshToken) && this.isRefreshTokenValid()) {
|
|
344
|
-
try {
|
|
345
|
-
console.log('Attempting to refresh token using refresh token');
|
|
346
|
-
const response = yield this.authApi.authTokenRefreshPut({
|
|
347
|
-
refreshToken: this.tokenResponse.refreshToken,
|
|
348
|
-
});
|
|
349
|
-
this.tokenResponse = response.data;
|
|
350
|
-
this.tokenSetAtSeconds = Date.now() / 1000;
|
|
351
|
-
console.log('Token refreshed successfully');
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
catch (e) {
|
|
355
|
-
console.error('Error refreshing token:', e);
|
|
356
|
-
// Throw the error to let the caller handle it
|
|
357
|
-
throw new Error(`Token refresh failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
// Fallback to full login only if refresh token is not available or invalid
|
|
361
|
-
// 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()) {
|
|
362
329
|
try {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
};
|
|
368
|
-
const signature = yield this.bfSigner.signLoginRequest(loginRequest);
|
|
369
|
-
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
|
+
});
|
|
370
334
|
this.tokenResponse = response.data;
|
|
371
335
|
this.tokenSetAtSeconds = Date.now() / 1000;
|
|
336
|
+
console.log('Token refreshed successfully');
|
|
337
|
+
return;
|
|
372
338
|
}
|
|
373
339
|
catch (e) {
|
|
374
|
-
console.error('
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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) {
|
|
380
362
|
throw new Error(`Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
381
363
|
}
|
|
382
|
-
|
|
364
|
+
// Otherwise, throw the error and let the caller decide
|
|
365
|
+
throw new Error(`Login failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
366
|
+
}
|
|
383
367
|
}
|
|
384
|
-
getAccessToken() {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (this.
|
|
393
|
-
|
|
394
|
-
yield this.refreshToken();
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
else if (this.isAccessTokenExpired()) {
|
|
398
|
-
yield this.login();
|
|
399
|
-
}
|
|
400
|
-
if (!this.tokenResponse) {
|
|
401
|
-
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();
|
|
402
378
|
}
|
|
403
|
-
|
|
404
|
-
|
|
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;
|
|
405
387
|
}
|
|
406
|
-
getOpenOrders(symbol) {
|
|
407
|
-
return
|
|
408
|
-
return yield this.tradeApi.getOpenOrders(symbol);
|
|
409
|
-
});
|
|
388
|
+
async getOpenOrders(symbol) {
|
|
389
|
+
return await this.tradeApi.getOpenOrders(symbol);
|
|
410
390
|
}
|
|
411
|
-
getStandbyOrders(symbol) {
|
|
412
|
-
return
|
|
413
|
-
return yield this.tradeApi.getStandbyOrders(symbol);
|
|
414
|
-
});
|
|
391
|
+
async getStandbyOrders(symbol) {
|
|
392
|
+
return await this.tradeApi.getStandbyOrders(symbol);
|
|
415
393
|
}
|
|
416
|
-
updateLeverage(symbol, leverageE9) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
signature: request,
|
|
433
|
-
});
|
|
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,
|
|
434
410
|
});
|
|
435
411
|
}
|
|
436
|
-
createOrder(params) {
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
console.log('Creating order:', createOrderRequest);
|
|
469
|
-
return yield this.tradeApi.postCreateOrder(createOrderRequest);
|
|
470
|
-
});
|
|
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);
|
|
471
444
|
}
|
|
472
|
-
cancelOrder(cancelOrdersRequest) {
|
|
473
|
-
return
|
|
474
|
-
return yield this.tradeApi.cancelOrders(cancelOrdersRequest);
|
|
475
|
-
});
|
|
445
|
+
async cancelOrder(cancelOrdersRequest) {
|
|
446
|
+
return await this.tradeApi.cancelOrders(cancelOrdersRequest);
|
|
476
447
|
}
|
|
477
|
-
cancelStandbyOrder(cancelOrdersRequest) {
|
|
478
|
-
return
|
|
479
|
-
return yield this.tradeApi.cancelStandbyOrders(cancelOrdersRequest);
|
|
480
|
-
});
|
|
448
|
+
async cancelStandbyOrder(cancelOrdersRequest) {
|
|
449
|
+
return await this.tradeApi.cancelStandbyOrders(cancelOrdersRequest);
|
|
481
450
|
}
|
|
482
|
-
withdraw(assetSymbol, amountE9) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
signature,
|
|
504
|
-
});
|
|
505
|
-
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,
|
|
506
472
|
});
|
|
473
|
+
console.log('Withdraw request sent:', signedFields);
|
|
507
474
|
}
|
|
508
|
-
authorizeAccount(accountAddress, alias) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
alias,
|
|
525
|
-
});
|
|
526
|
-
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,
|
|
527
491
|
});
|
|
492
|
+
console.log('Authorize account request sent:', signedFields);
|
|
528
493
|
}
|
|
529
|
-
deauthorizeAccount(accountAddress) {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
signature,
|
|
545
|
-
});
|
|
546
|
-
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,
|
|
547
509
|
});
|
|
510
|
+
console.log('Deauthorize account request sent:', signedFields);
|
|
548
511
|
}
|
|
549
|
-
adjustIsolatedMargin(symbol, amountE9, add) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
signature,
|
|
569
|
-
});
|
|
570
|
-
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,
|
|
571
531
|
});
|
|
532
|
+
console.log('Adjust isolated margin request sent:', signedFields);
|
|
572
533
|
}
|
|
573
|
-
getAccountPreferences() {
|
|
574
|
-
return
|
|
575
|
-
return yield this.accountDataApi.getAccountPreferences();
|
|
576
|
-
});
|
|
534
|
+
async getAccountPreferences() {
|
|
535
|
+
return await this.accountDataApi.getAccountPreferences();
|
|
577
536
|
}
|
|
578
|
-
updateAccountPreferences(updateAccountPreferenceRequest) {
|
|
579
|
-
return
|
|
580
|
-
return yield this.accountDataApi.putAccountPreferences(updateAccountPreferenceRequest);
|
|
581
|
-
});
|
|
537
|
+
async updateAccountPreferences(updateAccountPreferenceRequest) {
|
|
538
|
+
return await this.accountDataApi.putAccountPreferences(updateAccountPreferenceRequest);
|
|
582
539
|
}
|
|
583
540
|
// Update Account Group ID.
|
|
584
541
|
// If groupID is null, it will remove the account from its group.
|
|
585
542
|
// An account may only belong to 1 group at a time.
|
|
586
|
-
updateAccountGroupId(updateAccountGroupIdRequest) {
|
|
587
|
-
return
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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;
|
|
613
583
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
try {
|
|
618
|
-
const request = {
|
|
619
|
-
txBytes: gaslessTxPayloadBytes,
|
|
620
|
-
};
|
|
621
|
-
// sponsor gas for the transaction
|
|
622
|
-
const sponsorTxApiResponse = yield this.accountDataApi.sponsorTx(request);
|
|
623
|
-
const txBlockFromBytes = library_sui_1.TransactionBlock.from(sponsorTxApiResponse.data.txBytes);
|
|
624
|
-
// sign the transaction with user's wallet
|
|
625
|
-
const userSignedTx = yield this.bfSigner.signTx(txBlockFromBytes, this.suiClient);
|
|
626
|
-
// execute the transaction with both user's signature and sponsor's signature
|
|
627
|
-
const response = yield this.bfSigner.executeSponsoredTx(userSignedTx.bytes, userSignedTx.signature, sponsorTxApiResponse.data.signature, this.suiClient);
|
|
628
|
-
return response;
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
if (args === null || args === void 0 ? void 0 : args.fallbackToExecuteTx) {
|
|
632
|
-
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
633
|
-
}
|
|
634
|
-
throw error;
|
|
584
|
+
catch (error) {
|
|
585
|
+
if (args?.fallbackToExecuteTx) {
|
|
586
|
+
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
635
587
|
}
|
|
588
|
+
throw error;
|
|
636
589
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
return this.bfSigner.executeTx(txb, this.suiClient);
|
|
593
|
+
}
|
|
641
594
|
}
|
|
642
595
|
/**
|
|
643
596
|
* Batch claim rewards on-chain using RewardsDistributorInteractor
|
|
644
597
|
* @param payload Array of claim payloads with signatures
|
|
645
598
|
* @returns Transaction response
|
|
646
599
|
*/
|
|
647
|
-
batchClaimRewards(payload) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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;
|
|
653
627
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
+
}
|
|
667
643
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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;
|
|
676
656
|
}
|
|
657
|
+
await this.loginAndUpdateToken();
|
|
658
|
+
console.log('Token refreshed successfully');
|
|
659
|
+
consecutiveNetworkErrors = 0; // Reset network error counter on success
|
|
677
660
|
return;
|
|
678
661
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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;
|
|
707
696
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
697
|
+
setTimeout(() => {
|
|
698
|
+
console.log('Resuming token refresh attempts');
|
|
699
|
+
this.scheduleTokenRefresh(); // Reschedule refresh when resuming
|
|
700
|
+
}, 300000); // 5 minutes
|
|
711
701
|
return;
|
|
712
702
|
}
|
|
713
|
-
|
|
714
|
-
retryCount++;
|
|
715
|
-
console.error(`Error refreshing token (attempt ${retryCount}):`, error);
|
|
716
|
-
// Check if this is a network error that we should retry
|
|
717
|
-
const isNetworkError = error instanceof Error &&
|
|
718
|
-
(error.message.includes('Network Error') ||
|
|
719
|
-
error.message.includes('ERR_NETWORK') ||
|
|
720
|
-
error.message.includes('ECONNREFUSED') ||
|
|
721
|
-
error.message.includes('timeout') ||
|
|
722
|
-
error.message.includes('ERR_NETWORK_IO_SUSPENDED'));
|
|
723
|
-
// Check if we're currently offline (browser environment only)
|
|
724
|
-
const isOffline = typeof navigator !== 'undefined' &&
|
|
725
|
-
'onLine' in navigator &&
|
|
726
|
-
!navigator.onLine;
|
|
703
|
+
if (retryCount >= maxRetries && !isOffline) {
|
|
727
704
|
if (isNetworkError) {
|
|
728
|
-
|
|
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
|
|
729
707
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
console.log('User is offline, will retry when network comes back online');
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
// If it's not a network error and login failed (tokenResponse is null), logout immediately
|
|
736
|
-
if (!isNetworkError && !this.tokenResponse) {
|
|
737
|
-
console.error('Login failed permanently, triggering logout');
|
|
708
|
+
else {
|
|
709
|
+
console.error('Max retries reached, triggering logout');
|
|
738
710
|
this.logout();
|
|
739
711
|
return;
|
|
740
712
|
}
|
|
741
|
-
// If we've had too many consecutive network errors, pause refresh attempts temporarily
|
|
742
|
-
if (consecutiveNetworkErrors >= 10) {
|
|
743
|
-
// Clear any scheduled refresh
|
|
744
|
-
if (this.updateTokenTimeout) {
|
|
745
|
-
clearTimeout(this.updateTokenTimeout);
|
|
746
|
-
this.updateTokenTimeout = null;
|
|
747
|
-
}
|
|
748
|
-
setTimeout(() => {
|
|
749
|
-
console.log('Resuming token refresh attempts');
|
|
750
|
-
this.scheduleTokenRefresh(); // Reschedule refresh when resuming
|
|
751
|
-
}, 300000); // 5 minutes
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
if (retryCount >= maxRetries && !isOffline) {
|
|
755
|
-
if (isNetworkError) {
|
|
756
|
-
console.warn('Max retries reached due to network errors, will retry later');
|
|
757
|
-
return; // Don't logout for network issues, just wait for next interval
|
|
758
|
-
}
|
|
759
|
-
else {
|
|
760
|
-
console.error('Max retries reached, triggering logout');
|
|
761
|
-
this.logout();
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
// Exponential backoff: wait 2^retryCount seconds, but cap at 30 seconds for network errors
|
|
766
|
-
const baseDelay = isNetworkError ? 5000 : 2000; // Start with 5s for network errors, 2s for others
|
|
767
|
-
const delayMs = Math.min(baseDelay * Math.pow(2, retryCount - 1), 30000);
|
|
768
|
-
console.log(`Retrying in ${delayMs}ms...`);
|
|
769
|
-
yield new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
770
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));
|
|
771
719
|
}
|
|
772
|
-
}
|
|
720
|
+
}
|
|
773
721
|
}
|
|
774
|
-
createAccountDataStreamListener(handler) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
ws
|
|
787
|
-
resolve(ws);
|
|
788
|
-
});
|
|
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);
|
|
789
735
|
});
|
|
790
736
|
});
|
|
791
737
|
}
|
|
792
|
-
createMarketDataStreamListener(handler) {
|
|
793
|
-
return
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
ws
|
|
800
|
-
resolve(ws);
|
|
801
|
-
});
|
|
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);
|
|
802
746
|
});
|
|
803
747
|
});
|
|
804
748
|
}
|
|
805
|
-
logout() {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
this.isConnected = false;
|
|
816
|
-
(_a = this.onLogout) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
817
|
-
});
|
|
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?.();
|
|
818
759
|
}
|
|
819
|
-
dispose() {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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;
|
|
830
776
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
window.removeEventListener('online', this.onlineHandler);
|
|
835
|
-
this.onlineHandler = undefined;
|
|
836
|
-
}
|
|
837
|
-
if (this.offlineHandler) {
|
|
838
|
-
window.removeEventListener('offline', this.offlineHandler);
|
|
839
|
-
this.offlineHandler = undefined;
|
|
840
|
-
}
|
|
777
|
+
if (this.offlineHandler) {
|
|
778
|
+
window.removeEventListener('offline', this.offlineHandler);
|
|
779
|
+
this.offlineHandler = undefined;
|
|
841
780
|
}
|
|
842
|
-
|
|
843
|
-
|
|
781
|
+
}
|
|
782
|
+
this.isConnected = false;
|
|
844
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
|
+
};
|
|
845
801
|
/**
|
|
846
802
|
* @description
|
|
847
803
|
* Get the transaction builder instance
|
|
@@ -859,6 +815,4 @@ class BluefinProSdk {
|
|
|
859
815
|
return this.bfSigner;
|
|
860
816
|
}
|
|
861
817
|
}
|
|
862
|
-
exports.BluefinProSdk = BluefinProSdk;
|
|
863
|
-
BluefinProSdk.TOKEN_REFRESH_THRESHOLD_PERCENTAGE = 0.8;
|
|
864
818
|
//# sourceMappingURL=sdk.js.map
|