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