@0xmonaco/core 0.8.4 → 0.8.7-develop.34bd452
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/api/auth/api.d.ts +38 -51
- package/dist/api/auth/api.js +55 -62
- package/dist/api/base.d.ts +16 -10
- package/dist/api/base.js +39 -16
- package/dist/api/margin-accounts/api.d.ts +3 -1
- package/dist/api/margin-accounts/api.js +39 -2
- package/dist/api/orderbook/api.js +2 -1
- package/dist/api/perp/routes.d.ts +3 -0
- package/dist/api/perp/routes.js +2 -0
- package/dist/api/trading/api.js +6 -3
- package/dist/api/websocket/types.d.ts +5 -5
- package/dist/api/websocket/websocket.js +43 -22
- package/dist/crypto/session.d.ts +40 -0
- package/dist/crypto/session.js +60 -0
- package/dist/sdk.d.ts +24 -17
- package/dist/sdk.js +68 -53
- package/package.json +5 -3
package/dist/api/auth/api.d.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* const backendAuth = await authAPI.authenticateBackend(secretKey);
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
import type { AuthAPI, AuthState, BackendAuthResponse, ChallengeResponse,
|
|
31
|
+
import type { AuthAPI, AuthState, BackendAuthResponse, ChallengeResponse, SessionCredentials, SessionRefreshResponse } from "@0xmonaco/types";
|
|
32
32
|
import type { Chain, WalletClient } from "viem";
|
|
33
33
|
import { BaseAPI } from "../base";
|
|
34
34
|
export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
|
|
@@ -51,20 +51,24 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
|
|
|
51
51
|
* Complete authentication flow for frontend applications.
|
|
52
52
|
*
|
|
53
53
|
* This method handles the entire authentication process:
|
|
54
|
-
* 1.
|
|
55
|
-
* 2.
|
|
56
|
-
* 3.
|
|
54
|
+
* 1. Generates a fresh ed25519 session keypair locally
|
|
55
|
+
* 2. Creates a challenge that commits to the session public key
|
|
56
|
+
* 3. Signs the challenge message with the wallet
|
|
57
|
+
* 4. Verifies the signature, registering the session public key
|
|
58
|
+
*
|
|
59
|
+
* The returned {@link AuthState} carries the session keypair; sign subsequent
|
|
60
|
+
* requests with the private key (the wallet is not used again until the
|
|
61
|
+
* session expires).
|
|
57
62
|
*
|
|
58
63
|
* @param clientId - Client ID of the application
|
|
59
|
-
* @returns Promise resolving to the
|
|
64
|
+
* @returns Promise resolving to the authentication state (with the session keypair)
|
|
60
65
|
* @throws {APIError} When authentication fails
|
|
61
66
|
* @throws {InvalidConfigError} When wallet account is not available
|
|
62
67
|
*
|
|
63
68
|
* @example
|
|
64
69
|
* ```typescript
|
|
65
|
-
* // Complete authentication in one call
|
|
66
70
|
* const authResult = await authAPI.authenticate("my-app-client-id");
|
|
67
|
-
* console.log(`
|
|
71
|
+
* console.log(`Session public key: ${authResult.sessionPublicKey}`);
|
|
68
72
|
* console.log(`User ID: ${authResult.user.id}`);
|
|
69
73
|
* ```
|
|
70
74
|
*/
|
|
@@ -109,7 +113,7 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
|
|
|
109
113
|
* console.log(`Nonce: ${challenge.nonce}`);
|
|
110
114
|
* ```
|
|
111
115
|
*/
|
|
112
|
-
createChallenge(address: string, clientId: string): Promise<ChallengeResponse>;
|
|
116
|
+
createChallenge(address: string, clientId: string, sessionPublicKey: string): Promise<ChallengeResponse>;
|
|
113
117
|
/**
|
|
114
118
|
* Verifies a signature for frontend authentication.
|
|
115
119
|
*
|
|
@@ -117,33 +121,24 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
|
|
|
117
121
|
* authenticated API access. This is the second step in the authentication flow.
|
|
118
122
|
*
|
|
119
123
|
* @param address - Wallet address of the user
|
|
120
|
-
* @param signature -
|
|
124
|
+
* @param signature - Wallet signature of the challenge message
|
|
121
125
|
* @param nonce - Nonce from the challenge response
|
|
122
126
|
* @param clientId - Client ID of the application
|
|
123
|
-
* @
|
|
127
|
+
* @param session - The locally-generated session keypair (hex-encoded)
|
|
128
|
+
* @returns Promise resolving to the authentication state (with the session keypair)
|
|
124
129
|
* @throws {APIError} When signature verification fails
|
|
125
130
|
*
|
|
126
131
|
* @example
|
|
127
132
|
* ```typescript
|
|
128
|
-
*
|
|
129
|
-
* const
|
|
130
|
-
*
|
|
131
|
-
* // User signs the challenge message with their wallet
|
|
133
|
+
* const keypair = generateSessionKeypair();
|
|
134
|
+
* const session = { publicKey: publicKeyHex(keypair), privateKey: privateKeyHex(keypair) };
|
|
135
|
+
* const challenge = await authAPI.createChallenge(address, clientId, session.publicKey);
|
|
132
136
|
* const signature = await wallet.signMessage(challenge.message);
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* const authResult = await authAPI.verifySignature(
|
|
136
|
-
* address,
|
|
137
|
-
* signature,
|
|
138
|
-
* challenge.nonce,
|
|
139
|
-
* clientId
|
|
140
|
-
* );
|
|
141
|
-
*
|
|
142
|
-
* console.log(`Access token: ${authResult.accessToken}`);
|
|
143
|
-
* console.log(`User ID: ${authResult.user.id}`);
|
|
137
|
+
* const authResult = await authAPI.verifySignature(address, signature, challenge.nonce, clientId, session);
|
|
138
|
+
* console.log(`Session public key: ${authResult.sessionPublicKey}`);
|
|
144
139
|
* ```
|
|
145
140
|
*/
|
|
146
|
-
verifySignature(address: string, signature: string, nonce: string, clientId: string): Promise<AuthState>;
|
|
141
|
+
verifySignature(address: string, signature: string, nonce: string, clientId: string, session: SessionCredentials): Promise<AuthState>;
|
|
147
142
|
/**
|
|
148
143
|
* Authenticates a backend service using a secret key.
|
|
149
144
|
*
|
|
@@ -163,44 +158,36 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
|
|
|
163
158
|
*/
|
|
164
159
|
authenticateBackend(secretKey: string): Promise<BackendAuthResponse>;
|
|
165
160
|
/**
|
|
166
|
-
*
|
|
161
|
+
* Extends the current session's expiry.
|
|
167
162
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
163
|
+
* The request is signed with the active session key (set via
|
|
164
|
+
* {@link setSessionKeypair}); the server bumps the session's `expires_at`.
|
|
165
|
+
* No new credential is issued — the same keypair keeps working.
|
|
171
166
|
*
|
|
172
|
-
* @
|
|
173
|
-
* @
|
|
174
|
-
* @throws {APIError} When token refresh fails
|
|
167
|
+
* @returns Promise resolving to the new expiry
|
|
168
|
+
* @throws {APIError} When refresh fails (e.g. session expired or revoked)
|
|
175
169
|
*
|
|
176
170
|
* @example
|
|
177
171
|
* ```typescript
|
|
178
|
-
* const
|
|
179
|
-
* console.log(`
|
|
180
|
-
* console.log(`Expires at: ${new Date(newTokens.expiresAt * 1000)}`);
|
|
172
|
+
* const { expiresAt } = await authAPI.refreshSession();
|
|
173
|
+
* console.log(`Session now expires at: ${new Date(expiresAt * 1000)}`);
|
|
181
174
|
* ```
|
|
182
175
|
*/
|
|
183
|
-
|
|
176
|
+
refreshSession(): Promise<SessionRefreshResponse>;
|
|
184
177
|
/**
|
|
185
|
-
* Revokes the current session
|
|
186
|
-
*
|
|
187
|
-
* Invalidates the refresh token associated with the current access token,
|
|
188
|
-
* preventing it from being used to obtain new access tokens. This is useful
|
|
189
|
-
* for logout functionality or when a token has been compromised.
|
|
178
|
+
* Revokes the current session.
|
|
190
179
|
*
|
|
191
|
-
* The
|
|
192
|
-
*
|
|
180
|
+
* The request is signed with the active session key; the server deletes the
|
|
181
|
+
* matching session row. Used for logout or when a key may be compromised.
|
|
193
182
|
*
|
|
194
|
-
* @returns Promise resolving when the
|
|
195
|
-
* @throws {APIError} When
|
|
183
|
+
* @returns Promise resolving when the session is revoked
|
|
184
|
+
* @throws {APIError} When revocation fails
|
|
196
185
|
*
|
|
197
186
|
* @example
|
|
198
187
|
* ```typescript
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* await authAPI.revokeToken();
|
|
202
|
-
* console.log("Token revoked successfully");
|
|
188
|
+
* await authAPI.revokeSession();
|
|
189
|
+
* console.log("Session revoked successfully");
|
|
203
190
|
* ```
|
|
204
191
|
*/
|
|
205
|
-
|
|
192
|
+
revokeSession(): Promise<void>;
|
|
206
193
|
}
|
package/dist/api/auth/api.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* const backendAuth = await authAPI.authenticateBackend(secretKey);
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
+
import { generateSessionKeypair, privateKeyHex, publicKeyHex } from "../../crypto/session";
|
|
31
32
|
import { InvalidConfigError } from "../../errors";
|
|
32
33
|
import { BaseAPI } from "../base";
|
|
33
34
|
export class AuthAPIImpl extends BaseAPI {
|
|
@@ -56,20 +57,24 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
56
57
|
* Complete authentication flow for frontend applications.
|
|
57
58
|
*
|
|
58
59
|
* This method handles the entire authentication process:
|
|
59
|
-
* 1.
|
|
60
|
-
* 2.
|
|
61
|
-
* 3.
|
|
60
|
+
* 1. Generates a fresh ed25519 session keypair locally
|
|
61
|
+
* 2. Creates a challenge that commits to the session public key
|
|
62
|
+
* 3. Signs the challenge message with the wallet
|
|
63
|
+
* 4. Verifies the signature, registering the session public key
|
|
64
|
+
*
|
|
65
|
+
* The returned {@link AuthState} carries the session keypair; sign subsequent
|
|
66
|
+
* requests with the private key (the wallet is not used again until the
|
|
67
|
+
* session expires).
|
|
62
68
|
*
|
|
63
69
|
* @param clientId - Client ID of the application
|
|
64
|
-
* @returns Promise resolving to the
|
|
70
|
+
* @returns Promise resolving to the authentication state (with the session keypair)
|
|
65
71
|
* @throws {APIError} When authentication fails
|
|
66
72
|
* @throws {InvalidConfigError} When wallet account is not available
|
|
67
73
|
*
|
|
68
74
|
* @example
|
|
69
75
|
* ```typescript
|
|
70
|
-
* // Complete authentication in one call
|
|
71
76
|
* const authResult = await authAPI.authenticate("my-app-client-id");
|
|
72
|
-
* console.log(`
|
|
77
|
+
* console.log(`Session public key: ${authResult.sessionPublicKey}`);
|
|
73
78
|
* console.log(`User ID: ${authResult.user.id}`);
|
|
74
79
|
* ```
|
|
75
80
|
*/
|
|
@@ -81,12 +86,19 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
81
86
|
if (!account) {
|
|
82
87
|
throw new InvalidConfigError("No account available in wallet client", "account");
|
|
83
88
|
}
|
|
84
|
-
// 1.
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
// 1. Generate a fresh session keypair locally — the server never sees the
|
|
90
|
+
// private key.
|
|
91
|
+
const keypair = generateSessionKeypair();
|
|
92
|
+
const session = {
|
|
93
|
+
publicKey: publicKeyHex(keypair),
|
|
94
|
+
privateKey: privateKeyHex(keypair),
|
|
95
|
+
};
|
|
96
|
+
// 2. Create challenge (binds the session public key into the signed message)
|
|
97
|
+
const challenge = await this.createChallenge(account.address, clientId, session.publicKey);
|
|
98
|
+
// 3. Sign the challenge message with the wallet
|
|
87
99
|
const signature = await this.signChallenge(challenge.message);
|
|
88
|
-
//
|
|
89
|
-
return await this.verifySignature(account.address, signature, challenge.nonce, clientId);
|
|
100
|
+
// 4. Verify signature, registering the session public key
|
|
101
|
+
return await this.verifySignature(account.address, signature, challenge.nonce, clientId, session);
|
|
90
102
|
}
|
|
91
103
|
/**
|
|
92
104
|
* Signs a challenge message using the wallet client.
|
|
@@ -141,13 +153,14 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
141
153
|
* console.log(`Nonce: ${challenge.nonce}`);
|
|
142
154
|
* ```
|
|
143
155
|
*/
|
|
144
|
-
async createChallenge(address, clientId) {
|
|
156
|
+
async createChallenge(address, clientId, sessionPublicKey) {
|
|
145
157
|
const responseBody = await this.makePublicRequest("/api/v1/auth/challenge", {
|
|
146
158
|
method: "POST",
|
|
147
159
|
body: JSON.stringify({
|
|
148
160
|
address,
|
|
149
161
|
client_id: clientId,
|
|
150
162
|
chain_id: this.chain.id,
|
|
163
|
+
session_public_key: sessionPublicKey,
|
|
151
164
|
}),
|
|
152
165
|
});
|
|
153
166
|
return {
|
|
@@ -163,33 +176,24 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
163
176
|
* authenticated API access. This is the second step in the authentication flow.
|
|
164
177
|
*
|
|
165
178
|
* @param address - Wallet address of the user
|
|
166
|
-
* @param signature -
|
|
179
|
+
* @param signature - Wallet signature of the challenge message
|
|
167
180
|
* @param nonce - Nonce from the challenge response
|
|
168
181
|
* @param clientId - Client ID of the application
|
|
169
|
-
* @
|
|
182
|
+
* @param session - The locally-generated session keypair (hex-encoded)
|
|
183
|
+
* @returns Promise resolving to the authentication state (with the session keypair)
|
|
170
184
|
* @throws {APIError} When signature verification fails
|
|
171
185
|
*
|
|
172
186
|
* @example
|
|
173
187
|
* ```typescript
|
|
174
|
-
*
|
|
175
|
-
* const
|
|
176
|
-
*
|
|
177
|
-
* // User signs the challenge message with their wallet
|
|
188
|
+
* const keypair = generateSessionKeypair();
|
|
189
|
+
* const session = { publicKey: publicKeyHex(keypair), privateKey: privateKeyHex(keypair) };
|
|
190
|
+
* const challenge = await authAPI.createChallenge(address, clientId, session.publicKey);
|
|
178
191
|
* const signature = await wallet.signMessage(challenge.message);
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* const authResult = await authAPI.verifySignature(
|
|
182
|
-
* address,
|
|
183
|
-
* signature,
|
|
184
|
-
* challenge.nonce,
|
|
185
|
-
* clientId
|
|
186
|
-
* );
|
|
187
|
-
*
|
|
188
|
-
* console.log(`Access token: ${authResult.accessToken}`);
|
|
189
|
-
* console.log(`User ID: ${authResult.user.id}`);
|
|
192
|
+
* const authResult = await authAPI.verifySignature(address, signature, challenge.nonce, clientId, session);
|
|
193
|
+
* console.log(`Session public key: ${authResult.sessionPublicKey}`);
|
|
190
194
|
* ```
|
|
191
195
|
*/
|
|
192
|
-
async verifySignature(address, signature, nonce, clientId) {
|
|
196
|
+
async verifySignature(address, signature, nonce, clientId, session) {
|
|
193
197
|
const responseBody = await this.makePublicRequest("/api/v1/auth/verify", {
|
|
194
198
|
method: "POST",
|
|
195
199
|
body: JSON.stringify({
|
|
@@ -198,11 +202,12 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
198
202
|
nonce,
|
|
199
203
|
client_id: clientId,
|
|
200
204
|
chain_id: this.chain.id,
|
|
205
|
+
session_public_key: session.publicKey,
|
|
201
206
|
}),
|
|
202
207
|
});
|
|
203
208
|
return {
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
sessionPublicKey: session.publicKey,
|
|
210
|
+
sessionPrivateKey: session.privateKey,
|
|
206
211
|
expiresAt: responseBody.expires_at,
|
|
207
212
|
user: {
|
|
208
213
|
id: responseBody.user.id,
|
|
@@ -247,57 +252,45 @@ export class AuthAPIImpl extends BaseAPI {
|
|
|
247
252
|
};
|
|
248
253
|
}
|
|
249
254
|
/**
|
|
250
|
-
*
|
|
255
|
+
* Extends the current session's expiry.
|
|
251
256
|
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
257
|
+
* The request is signed with the active session key (set via
|
|
258
|
+
* {@link setSessionKeypair}); the server bumps the session's `expires_at`.
|
|
259
|
+
* No new credential is issued — the same keypair keeps working.
|
|
255
260
|
*
|
|
256
|
-
* @
|
|
257
|
-
* @
|
|
258
|
-
* @throws {APIError} When token refresh fails
|
|
261
|
+
* @returns Promise resolving to the new expiry
|
|
262
|
+
* @throws {APIError} When refresh fails (e.g. session expired or revoked)
|
|
259
263
|
*
|
|
260
264
|
* @example
|
|
261
265
|
* ```typescript
|
|
262
|
-
* const
|
|
263
|
-
* console.log(`
|
|
264
|
-
* console.log(`Expires at: ${new Date(newTokens.expiresAt * 1000)}`);
|
|
266
|
+
* const { expiresAt } = await authAPI.refreshSession();
|
|
267
|
+
* console.log(`Session now expires at: ${new Date(expiresAt * 1000)}`);
|
|
265
268
|
* ```
|
|
266
269
|
*/
|
|
267
|
-
async
|
|
268
|
-
const responseBody = await this.
|
|
270
|
+
async refreshSession() {
|
|
271
|
+
const responseBody = await this.makeAuthenticatedRequest("/api/v1/auth/refresh", {
|
|
269
272
|
method: "POST",
|
|
270
|
-
body: JSON.stringify({
|
|
271
|
-
refresh_token: refreshToken,
|
|
272
|
-
}),
|
|
273
273
|
});
|
|
274
274
|
return {
|
|
275
|
-
accessToken: responseBody.access_token,
|
|
276
275
|
expiresAt: responseBody.expires_at,
|
|
277
276
|
};
|
|
278
277
|
}
|
|
279
278
|
/**
|
|
280
|
-
* Revokes the current session
|
|
281
|
-
*
|
|
282
|
-
* Invalidates the refresh token associated with the current access token,
|
|
283
|
-
* preventing it from being used to obtain new access tokens. This is useful
|
|
284
|
-
* for logout functionality or when a token has been compromised.
|
|
279
|
+
* Revokes the current session.
|
|
285
280
|
*
|
|
286
|
-
* The
|
|
287
|
-
*
|
|
281
|
+
* The request is signed with the active session key; the server deletes the
|
|
282
|
+
* matching session row. Used for logout or when a key may be compromised.
|
|
288
283
|
*
|
|
289
|
-
* @returns Promise resolving when the
|
|
290
|
-
* @throws {APIError} When
|
|
284
|
+
* @returns Promise resolving when the session is revoked
|
|
285
|
+
* @throws {APIError} When revocation fails
|
|
291
286
|
*
|
|
292
287
|
* @example
|
|
293
288
|
* ```typescript
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* await authAPI.revokeToken();
|
|
297
|
-
* console.log("Token revoked successfully");
|
|
289
|
+
* await authAPI.revokeSession();
|
|
290
|
+
* console.log("Session revoked successfully");
|
|
298
291
|
* ```
|
|
299
292
|
*/
|
|
300
|
-
async
|
|
293
|
+
async revokeSession() {
|
|
301
294
|
await this.makeAuthenticatedRequest("/api/v1/auth/revoke", {
|
|
302
295
|
method: "POST",
|
|
303
296
|
});
|
package/dist/api/base.d.ts
CHANGED
|
@@ -20,13 +20,16 @@
|
|
|
20
20
|
* }
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
+
import type { SessionCredentials } from "@0xmonaco/types";
|
|
24
|
+
import { type SessionKeypair } from "../crypto/session";
|
|
23
25
|
export interface RetryOptions {
|
|
24
26
|
maxRetries?: number;
|
|
25
27
|
baseDelayMs?: number;
|
|
26
28
|
}
|
|
27
29
|
export declare abstract class BaseAPI {
|
|
28
30
|
protected readonly apiUrl: string;
|
|
29
|
-
|
|
31
|
+
/** Active session keypair (raw bytes) used to sign authenticated requests. */
|
|
32
|
+
protected sessionKeypair?: SessionKeypair;
|
|
30
33
|
protected retryOptions: Required<RetryOptions>;
|
|
31
34
|
/**
|
|
32
35
|
* Creates a new BaseAPI instance.
|
|
@@ -36,17 +39,11 @@ export declare abstract class BaseAPI {
|
|
|
36
39
|
*/
|
|
37
40
|
constructor(apiUrl: string, retryOptions?: RetryOptions);
|
|
38
41
|
/**
|
|
39
|
-
* Set the
|
|
42
|
+
* Set (or clear) the session keypair used to sign authenticated requests.
|
|
40
43
|
*
|
|
41
|
-
* @param
|
|
44
|
+
* @param credentials - Hex-encoded session keypair, or `undefined` to clear.
|
|
42
45
|
*/
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get the current access token.
|
|
46
|
-
*
|
|
47
|
-
* @returns The current access token or undefined if not set
|
|
48
|
-
*/
|
|
49
|
-
protected getAccessToken(): string | undefined;
|
|
46
|
+
setSessionKeypair(credentials: SessionCredentials | undefined): void;
|
|
50
47
|
/**
|
|
51
48
|
* Parse request body for error logging
|
|
52
49
|
*
|
|
@@ -93,6 +90,15 @@ export declare abstract class BaseAPI {
|
|
|
93
90
|
* ```
|
|
94
91
|
*/
|
|
95
92
|
protected makeAuthenticatedRequest<T>(endpoint: string, options?: RequestInit): Promise<T>;
|
|
93
|
+
/**
|
|
94
|
+
* Compute the per-request ed25519 signature headers.
|
|
95
|
+
*
|
|
96
|
+
* The signed payload is `METHOD\nPATH_WITH_QUERY\nTIMESTAMP_MS\nSHA256_BODY_HEX`,
|
|
97
|
+
* matching the server (`handlers::auth::compose_signing_string`). `endpoint`
|
|
98
|
+
* is the path-with-query exactly as sent; the body hash covers the raw bytes
|
|
99
|
+
* so a request can't be replayed with a different body.
|
|
100
|
+
*/
|
|
101
|
+
private buildSignatureHeaders;
|
|
96
102
|
/**
|
|
97
103
|
* Makes an unauthenticated API request.
|
|
98
104
|
*
|
package/dist/api/base.js
CHANGED
|
@@ -20,11 +20,14 @@
|
|
|
20
20
|
* }
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
+
import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils";
|
|
23
24
|
import { StatusCodes } from "http-status-codes";
|
|
25
|
+
import { composeSigningString, keypairFromHex, sha256Hex, signMessage } from "../crypto/session";
|
|
24
26
|
import { APIError } from "../errors";
|
|
25
27
|
export class BaseAPI {
|
|
26
28
|
apiUrl;
|
|
27
|
-
|
|
29
|
+
/** Active session keypair (raw bytes) used to sign authenticated requests. */
|
|
30
|
+
sessionKeypair;
|
|
28
31
|
retryOptions;
|
|
29
32
|
/**
|
|
30
33
|
* Creates a new BaseAPI instance.
|
|
@@ -40,20 +43,12 @@ export class BaseAPI {
|
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
/**
|
|
43
|
-
* Set the
|
|
46
|
+
* Set (or clear) the session keypair used to sign authenticated requests.
|
|
44
47
|
*
|
|
45
|
-
* @param
|
|
48
|
+
* @param credentials - Hex-encoded session keypair, or `undefined` to clear.
|
|
46
49
|
*/
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Get the current access token.
|
|
52
|
-
*
|
|
53
|
-
* @returns The current access token or undefined if not set
|
|
54
|
-
*/
|
|
55
|
-
getAccessToken() {
|
|
56
|
-
return this.accessToken;
|
|
50
|
+
setSessionKeypair(credentials) {
|
|
51
|
+
this.sessionKeypair = credentials ? keypairFromHex(credentials.publicKey, credentials.privateKey) : undefined;
|
|
57
52
|
}
|
|
58
53
|
/**
|
|
59
54
|
* Parse request body for error logging
|
|
@@ -229,23 +224,51 @@ export class BaseAPI {
|
|
|
229
224
|
* ```
|
|
230
225
|
*/
|
|
231
226
|
async makeAuthenticatedRequest(endpoint, options = {}) {
|
|
232
|
-
if (!this.
|
|
233
|
-
throw new APIError("
|
|
227
|
+
if (!this.sessionKeypair) {
|
|
228
|
+
throw new APIError("Session keypair not set. Authenticate (login) first.", {
|
|
234
229
|
endpoint: `${this.apiUrl}${endpoint}`,
|
|
235
230
|
statusCode: StatusCodes.UNAUTHORIZED,
|
|
236
231
|
});
|
|
237
232
|
}
|
|
238
233
|
const url = `${this.apiUrl}${endpoint}`;
|
|
239
234
|
const requestBody = this.parseRequestBody(options.body);
|
|
235
|
+
const signatureHeaders = this.buildSignatureHeaders(endpoint, options);
|
|
240
236
|
return this.executeRequest(url, endpoint, {
|
|
241
237
|
...options,
|
|
242
238
|
headers: {
|
|
243
239
|
"Content-Type": "application/json",
|
|
244
|
-
|
|
240
|
+
...signatureHeaders,
|
|
245
241
|
...options.headers,
|
|
246
242
|
},
|
|
247
243
|
}, requestBody);
|
|
248
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Compute the per-request ed25519 signature headers.
|
|
247
|
+
*
|
|
248
|
+
* The signed payload is `METHOD\nPATH_WITH_QUERY\nTIMESTAMP_MS\nSHA256_BODY_HEX`,
|
|
249
|
+
* matching the server (`handlers::auth::compose_signing_string`). `endpoint`
|
|
250
|
+
* is the path-with-query exactly as sent; the body hash covers the raw bytes
|
|
251
|
+
* so a request can't be replayed with a different body.
|
|
252
|
+
*/
|
|
253
|
+
buildSignatureHeaders(endpoint, options) {
|
|
254
|
+
if (!this.sessionKeypair) {
|
|
255
|
+
throw new APIError("Session keypair not set. Authenticate (login) first.", {
|
|
256
|
+
endpoint: `${this.apiUrl}${endpoint}`,
|
|
257
|
+
statusCode: StatusCodes.UNAUTHORIZED,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const method = (options.method ?? "GET").toUpperCase();
|
|
261
|
+
const timestampMs = Date.now();
|
|
262
|
+
const bodyBytes = typeof options.body === "string" ? utf8ToBytes(options.body) : new Uint8Array(0);
|
|
263
|
+
const bodyHash = sha256Hex(bodyBytes);
|
|
264
|
+
const signingString = composeSigningString(method, endpoint, timestampMs, bodyHash);
|
|
265
|
+
const signature = signMessage(this.sessionKeypair.privateKey, signingString);
|
|
266
|
+
return {
|
|
267
|
+
"X-Monaco-PublicKey": bytesToHex(this.sessionKeypair.publicKey),
|
|
268
|
+
"X-Monaco-Timestamp": String(timestampMs),
|
|
269
|
+
"X-Monaco-Signature": signature,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
249
272
|
/**
|
|
250
273
|
* Makes an unauthenticated API request.
|
|
251
274
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CreateMarginAccountRequest, CreateMarginAccountResponse, GetAvailableCollateralParams, GetAvailableCollateralResponse, GetMarginAccountMovementsParams, GetMarginAccountMovementsResponse, ListMarginAccountsParams, ListMarginAccountsResponse, MarginAccountSummary, MarginAccountsAPI, SimulateOrderRiskRequest, SimulateOrderRiskResponse, TransferCollateralRequest, TransferCollateralResponse } from "@0xmonaco/types";
|
|
1
|
+
import type { CreateMarginAccountRequest, CreateMarginAccountResponse, GetAvailableCollateralParams, GetAvailableCollateralResponse, GetMarginAccountMovementsParams, GetMarginAccountMovementsResponse, ListMarginAccountsParams, ListMarginAccountsResponse, MarginAccountSummary, MarginAccountsAPI, SimulateOrderRiskRequest, SimulateOrderRiskResponse, TransferCollateralRequest, TransferCollateralResponse, TransferCollateralToAutoMarginAccountRequest } from "@0xmonaco/types";
|
|
2
2
|
import { BaseAPI } from "../base";
|
|
3
3
|
export declare class MarginAccountsAPIImpl extends BaseAPI implements MarginAccountsAPI {
|
|
4
4
|
listMarginAccounts(params?: ListMarginAccountsParams): Promise<ListMarginAccountsResponse>;
|
|
@@ -6,7 +6,9 @@ export declare class MarginAccountsAPIImpl extends BaseAPI implements MarginAcco
|
|
|
6
6
|
getMarginAccountSummary(marginAccountId: string): Promise<MarginAccountSummary>;
|
|
7
7
|
getAvailableCollateral(params?: GetAvailableCollateralParams): Promise<GetAvailableCollateralResponse>;
|
|
8
8
|
transferCollateralToMarginAccount(marginAccountId: string, request: TransferCollateralRequest): Promise<TransferCollateralResponse>;
|
|
9
|
+
transferCollateralToAutoMarginAccount(request: TransferCollateralToAutoMarginAccountRequest): Promise<TransferCollateralResponse>;
|
|
9
10
|
transferCollateralFromMarginAccount(marginAccountId: string, request: TransferCollateralRequest): Promise<TransferCollateralResponse>;
|
|
10
11
|
getMarginAccountMovements(marginAccountId: string, params?: GetMarginAccountMovementsParams): Promise<GetMarginAccountMovementsResponse>;
|
|
11
12
|
simulateOrderRisk(marginAccountId: string, request: SimulateOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
|
|
13
|
+
simulateAutoMarginOrderRisk(request: SimulateOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
|
|
12
14
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateMarginAccountSchema, GetAvailableCollateralSchema, GetMarginAccountMovementsSchema, GetMarginAccountSummarySchema, ListMarginAccountsSchema, SimulateOrderRiskSchema, TransferCollateralSchema, validate, } from "@0xmonaco/types";
|
|
1
|
+
import { CreateMarginAccountSchema, GetAvailableCollateralSchema, GetMarginAccountMovementsSchema, GetMarginAccountSummarySchema, ListMarginAccountsSchema, SimulateAutoMarginOrderRiskSchema, SimulateOrderRiskSchema, TransferCollateralSchema, TransferCollateralToAutoMarginAccountSchema, validate, } from "@0xmonaco/types";
|
|
2
2
|
import { BaseAPI } from "../base";
|
|
3
3
|
import { perpRoutes } from "../perp";
|
|
4
4
|
export class MarginAccountsAPIImpl extends BaseAPI {
|
|
@@ -6,7 +6,14 @@ export class MarginAccountsAPIImpl extends BaseAPI {
|
|
|
6
6
|
if (params) {
|
|
7
7
|
validate(ListMarginAccountsSchema, params);
|
|
8
8
|
}
|
|
9
|
-
return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.list(params
|
|
9
|
+
return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.list(params
|
|
10
|
+
? {
|
|
11
|
+
page: params.page,
|
|
12
|
+
page_size: params.page_size,
|
|
13
|
+
state: params.state,
|
|
14
|
+
trading_pair_id: params.tradingPairId,
|
|
15
|
+
}
|
|
16
|
+
: undefined));
|
|
10
17
|
}
|
|
11
18
|
async createMarginAccount(request) {
|
|
12
19
|
validate(CreateMarginAccountSchema, request);
|
|
@@ -36,6 +43,18 @@ export class MarginAccountsAPIImpl extends BaseAPI {
|
|
|
36
43
|
}),
|
|
37
44
|
});
|
|
38
45
|
}
|
|
46
|
+
async transferCollateralToAutoMarginAccount(request) {
|
|
47
|
+
validate(TransferCollateralToAutoMarginAccountSchema, { request });
|
|
48
|
+
return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferInAuto(), {
|
|
49
|
+
method: "POST",
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
asset: request.asset,
|
|
52
|
+
amount: request.amount,
|
|
53
|
+
trading_pair_id: request.tradingPairId,
|
|
54
|
+
strategy_key: request.strategyKey,
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
39
58
|
async transferCollateralFromMarginAccount(marginAccountId, request) {
|
|
40
59
|
validate(TransferCollateralSchema, { marginAccountId, request });
|
|
41
60
|
return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferOut(marginAccountId), {
|
|
@@ -56,6 +75,24 @@ export class MarginAccountsAPIImpl extends BaseAPI {
|
|
|
56
75
|
method: "POST",
|
|
57
76
|
body: JSON.stringify({
|
|
58
77
|
trading_pair_id: request.tradingPairId,
|
|
78
|
+
strategy_key: request.strategyKey,
|
|
79
|
+
side: request.side,
|
|
80
|
+
position_side: request.positionSide,
|
|
81
|
+
order_type: request.orderType,
|
|
82
|
+
price: request.price,
|
|
83
|
+
quantity: request.quantity,
|
|
84
|
+
leverage: request.leverage,
|
|
85
|
+
reduce_only: request.reduceOnly,
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async simulateAutoMarginOrderRisk(request) {
|
|
90
|
+
validate(SimulateAutoMarginOrderRiskSchema, { request });
|
|
91
|
+
return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.simulateOrderRiskAuto(), {
|
|
92
|
+
method: "POST",
|
|
93
|
+
body: JSON.stringify({
|
|
94
|
+
trading_pair_id: request.tradingPairId,
|
|
95
|
+
strategy_key: request.strategyKey,
|
|
59
96
|
side: request.side,
|
|
60
97
|
position_side: request.positionSide,
|
|
61
98
|
order_type: request.orderType,
|
|
@@ -12,7 +12,8 @@ export class OrderbookAPIImpl extends BaseAPI {
|
|
|
12
12
|
params.set("denomination", denomination.toLowerCase());
|
|
13
13
|
const response = await this.makePublicRequest(`/api/v1/orderbook/${encodeURIComponent(tradingPairId)}?${params.toString()}`);
|
|
14
14
|
return {
|
|
15
|
-
|
|
15
|
+
// `trading_pair_id` is the pair UUID; `symbol` is the display string.
|
|
16
|
+
tradingPairId: response.trading_pair_id,
|
|
16
17
|
tradingMode: response.trading_mode,
|
|
17
18
|
bids: response.data.bids.map((level) => ({
|
|
18
19
|
price: level.price,
|
|
@@ -108,6 +108,7 @@ export declare const perpRoutes: {
|
|
|
108
108
|
page?: number;
|
|
109
109
|
page_size?: number;
|
|
110
110
|
state?: string;
|
|
111
|
+
trading_pair_id?: string;
|
|
111
112
|
}) => string;
|
|
112
113
|
readonly create: () => string;
|
|
113
114
|
readonly summary: (marginAccountId: string) => string;
|
|
@@ -115,6 +116,7 @@ export declare const perpRoutes: {
|
|
|
115
116
|
asset?: string;
|
|
116
117
|
}) => string;
|
|
117
118
|
readonly transferIn: (marginAccountId: string) => string;
|
|
119
|
+
readonly transferInAuto: () => string;
|
|
118
120
|
readonly transferOut: (marginAccountId: string) => string;
|
|
119
121
|
readonly movements: (marginAccountId: string, params?: {
|
|
120
122
|
movement_type?: string;
|
|
@@ -122,6 +124,7 @@ export declare const perpRoutes: {
|
|
|
122
124
|
page_size?: number;
|
|
123
125
|
}) => string;
|
|
124
126
|
readonly simulateOrderRisk: (marginAccountId: string) => string;
|
|
127
|
+
readonly simulateOrderRiskAuto: () => string;
|
|
125
128
|
};
|
|
126
129
|
readonly streams: {
|
|
127
130
|
readonly orderbook: () => string;
|
package/dist/api/perp/routes.js
CHANGED
|
@@ -71,9 +71,11 @@ export const perpRoutes = {
|
|
|
71
71
|
summary: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}`,
|
|
72
72
|
availableCollateral: (params) => withQuery(`${API_V1}/margin/collateral/available`, params),
|
|
73
73
|
transferIn: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/collateral/transfer-in`,
|
|
74
|
+
transferInAuto: () => `${API_V1}/margin/collateral/transfer-in`,
|
|
74
75
|
transferOut: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/collateral/transfer-out`,
|
|
75
76
|
movements: (marginAccountId, params) => withQuery(`${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/movements`, params),
|
|
76
77
|
simulateOrderRisk: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/simulate-order-risk`,
|
|
78
|
+
simulateOrderRiskAuto: () => `${API_V1}/margin/simulate-order-risk`,
|
|
77
79
|
},
|
|
78
80
|
streams: {
|
|
79
81
|
orderbook: () => `${API_V1}/streaming/orderbook`,
|
package/dist/api/trading/api.js
CHANGED
|
@@ -493,8 +493,11 @@ export class TradingAPIImpl extends BaseAPI {
|
|
|
493
493
|
* ```
|
|
494
494
|
*/
|
|
495
495
|
async getOrder(orderId) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
496
|
+
// The REST endpoint returns the order fields at the top level (a flat
|
|
497
|
+
// `Order`), not wrapped in `{ order, status }`. Wrap it here so the SDK
|
|
498
|
+
// honors its declared `GetOrderResponse` contract. A non-2xx response
|
|
499
|
+
// throws in `makeAuthenticatedRequest`, so reaching here means success.
|
|
500
|
+
const order = await this.makeAuthenticatedRequest(perpRoutes.orders.get(orderId), { method: "GET" });
|
|
501
|
+
return { order, status: "SUCCESS" };
|
|
499
502
|
}
|
|
500
503
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ConditionalOrderEvent, Interval, OHLCVEvent, OrderbookEvent, OrderbookQuotationMode, OrderEvent, TradeEvent, TradingMode, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
|
|
1
|
+
import type { ConditionalOrderEvent, Interval, OHLCVEvent, OrderbookEvent, OrderbookQuotationMode, OrderEvent, SessionCredentials, TradeEvent, TradingMode, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
|
|
2
2
|
export type StatusHandler = (status: WebSocketStatus) => void;
|
|
3
3
|
export type MessageHandler<T> = (data: T) => void;
|
|
4
4
|
export interface MonacoWebSocketOptions {
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/** Session keypair for authenticated channels (orders, balances, etc.) */
|
|
6
|
+
session?: SessionCredentials;
|
|
7
7
|
/** Enable auto-reconnect on disconnect (default: true) */
|
|
8
8
|
autoReconnect?: boolean;
|
|
9
9
|
/** Maximum reconnection attempts (default: 5) */
|
|
@@ -20,8 +20,8 @@ export interface MonacoWebSocket {
|
|
|
20
20
|
isConnected: () => boolean;
|
|
21
21
|
/** Get current websocket connection status */
|
|
22
22
|
getStatus: () => WebSocketStatus;
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/** Set (or clear) the session keypair used to authenticate the connection */
|
|
24
|
+
setSessionKeypair: (credentials: SessionCredentials | undefined) => void;
|
|
25
25
|
/** Subscribe to order events (requires authentication) */
|
|
26
26
|
orders: (tradingPairId: string, tradingMode: TradingMode, handler: MessageHandler<OrderEvent>) => () => void;
|
|
27
27
|
/** Subscribe to orderbook events (public) */
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { hexToBytes } from "@noble/hashes/utils";
|
|
2
|
+
import { composeWsSigningString, signMessage } from "../../crypto/session";
|
|
1
3
|
import { ALL_MAGNITUDES } from "../../utils";
|
|
2
4
|
import { keysToCamelCase } from "./utils";
|
|
3
5
|
// Connection constants
|
|
@@ -107,7 +109,7 @@ function parseConditionalOrderEvent(rawData) {
|
|
|
107
109
|
*/
|
|
108
110
|
export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
109
111
|
let ws = null;
|
|
110
|
-
let
|
|
112
|
+
let session = options.session;
|
|
111
113
|
let reconnectAttempts = 0;
|
|
112
114
|
let reconnectTimer = null;
|
|
113
115
|
let heartbeatTimer = null;
|
|
@@ -127,11 +129,24 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
127
129
|
return "disconnected";
|
|
128
130
|
}
|
|
129
131
|
};
|
|
130
|
-
const getUrl = () =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
const getUrl = () => baseUrl;
|
|
133
|
+
/**
|
|
134
|
+
* Send the signed auth handshake as the first message after the socket
|
|
135
|
+
* opens. The client signs `WS-AUTH\n<pubkey>\n<ts>` with the session private
|
|
136
|
+
* key; the server binds the connection to the session. No-op if no session
|
|
137
|
+
* is set (public channels work unauthenticated).
|
|
138
|
+
*/
|
|
139
|
+
const sendAuthenticate = () => {
|
|
140
|
+
if (!session || ws?.readyState !== WebSocket.OPEN)
|
|
141
|
+
return;
|
|
142
|
+
const timestamp = Date.now();
|
|
143
|
+
const signature = signMessage(hexToBytes(session.privateKey), composeWsSigningString(session.publicKey, timestamp));
|
|
144
|
+
ws.send(JSON.stringify({
|
|
145
|
+
type: "Authenticate",
|
|
146
|
+
public_key: session.publicKey,
|
|
147
|
+
timestamp,
|
|
148
|
+
signature,
|
|
149
|
+
}));
|
|
135
150
|
};
|
|
136
151
|
const stopHeartbeat = () => {
|
|
137
152
|
if (heartbeatTimer) {
|
|
@@ -217,6 +232,9 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
217
232
|
}
|
|
218
233
|
reconnectAttempts = 0;
|
|
219
234
|
startHeartbeat();
|
|
235
|
+
// Authenticate before resubscribing so user-specific channels
|
|
236
|
+
// (orders, balances, …) are authorized when the Subscribe arrives.
|
|
237
|
+
sendAuthenticate();
|
|
220
238
|
resubscribeAll();
|
|
221
239
|
options.onStatusChange?.("connected");
|
|
222
240
|
resolve();
|
|
@@ -324,7 +342,9 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
324
342
|
const data = rawData;
|
|
325
343
|
const orderbookData = data.data;
|
|
326
344
|
const event = {
|
|
327
|
-
|
|
345
|
+
// `trading_pair_id` is the pair UUID; `symbol` is the display string
|
|
346
|
+
// (e.g. "AMZN/USDC"). `tradingPairId` must carry the UUID.
|
|
347
|
+
tradingPairId: data.trading_pair_id,
|
|
328
348
|
tradingMode: data.trading_mode,
|
|
329
349
|
bids: (orderbookData?.bids || []).map((level) => ({
|
|
330
350
|
price: level.price,
|
|
@@ -368,7 +388,9 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
368
388
|
const data = rawData;
|
|
369
389
|
const ohlcvData = data.data;
|
|
370
390
|
const event = {
|
|
371
|
-
|
|
391
|
+
// `trading_pair_id` is the pair UUID; `symbol` is the display string.
|
|
392
|
+
// `tradingPairId` must carry the UUID (the symbol lives in `candlestick.s`).
|
|
393
|
+
tradingPairId: data.trading_pair_id,
|
|
372
394
|
tradingMode: data.trading_mode,
|
|
373
395
|
interval: data.interval,
|
|
374
396
|
candlestick: {
|
|
@@ -532,21 +554,20 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
532
554
|
disconnect,
|
|
533
555
|
isConnected: () => ws?.readyState === WebSocket.OPEN,
|
|
534
556
|
getStatus,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
557
|
+
setSessionKeypair: (credentials) => {
|
|
558
|
+
session = credentials ?? undefined;
|
|
559
|
+
// If already connected, (re)send the signed handshake so the connection
|
|
560
|
+
// picks up the new session without tearing down. If not yet connected,
|
|
561
|
+
// onopen will send it. Clearing the session leaves the connection up for
|
|
562
|
+
// public channels; logout calls disconnect() to fully tear down.
|
|
563
|
+
if (session && ws?.readyState === WebSocket.OPEN) {
|
|
564
|
+
sendAuthenticate();
|
|
565
|
+
}
|
|
566
|
+
else if (session && (!ws || ws.readyState === WebSocket.CLOSED)) {
|
|
567
|
+
connect().catch((err) => {
|
|
568
|
+
console.warn("WebSocket: Failed to connect after session update:", err);
|
|
569
|
+
});
|
|
544
570
|
}
|
|
545
|
-
if (!newToken)
|
|
546
|
-
return;
|
|
547
|
-
connect().catch((err) => {
|
|
548
|
-
console.warn("WebSocket: Failed to reconnect after token update:", err);
|
|
549
|
-
});
|
|
550
571
|
},
|
|
551
572
|
orders: subscribeOrders,
|
|
552
573
|
orderbook: subscribeOrderbook,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session keypair crypto for noncustodial request signing.
|
|
3
|
+
*
|
|
4
|
+
* On login the SDK generates a random ed25519 keypair locally. The public key
|
|
5
|
+
* is registered with the server (bound by the wallet's signature on the
|
|
6
|
+
* challenge); every subsequent authenticated request is signed with the
|
|
7
|
+
* private key. The server verifies the signature against the stored public
|
|
8
|
+
* key — it never sees the private key, so it cannot mint requests as the user.
|
|
9
|
+
*/
|
|
10
|
+
/** A locally-generated ed25519 session keypair (raw 32-byte values). */
|
|
11
|
+
export interface SessionKeypair {
|
|
12
|
+
/** 32-byte ed25519 public key */
|
|
13
|
+
publicKey: Uint8Array;
|
|
14
|
+
/** 32-byte ed25519 private key (seed) */
|
|
15
|
+
privateKey: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate a fresh ed25519 session keypair.
|
|
19
|
+
*
|
|
20
|
+
* `@noble/curves` sources entropy from `crypto.getRandomValues`. Environments
|
|
21
|
+
* without it (very old Node without webcrypto, locked-down sandboxes) will
|
|
22
|
+
* throw — we surface a clear error rather than producing a weak key.
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateSessionKeypair(): SessionKeypair;
|
|
25
|
+
export declare function publicKeyHex(keypair: SessionKeypair): string;
|
|
26
|
+
export declare function privateKeyHex(keypair: SessionKeypair): string;
|
|
27
|
+
/** Reconstruct a keypair from its hex-encoded halves (e.g. restored from storage). */
|
|
28
|
+
export declare function keypairFromHex(publicKeyHex: string, privateKeyHex: string): SessionKeypair;
|
|
29
|
+
/** Lowercase-hex sha256 of the given bytes. Use over an empty array for no-body requests. */
|
|
30
|
+
export declare function sha256Hex(data: Uint8Array): string;
|
|
31
|
+
/**
|
|
32
|
+
* Canonical per-request signing string. Mirrors the server
|
|
33
|
+
* (`handlers::auth::compose_signing_string`):
|
|
34
|
+
* `METHOD\nPATH_WITH_QUERY\nTIMESTAMP_MS\nSHA256_BODY_HEX`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function composeSigningString(method: string, pathWithQuery: string, timestampMs: number, bodySha256Hex: string): string;
|
|
37
|
+
/** Canonical WebSocket handshake signing string: `WS-AUTH\n<pubkey-hex>\n<ts>`. */
|
|
38
|
+
export declare function composeWsSigningString(publicKeyHex: string, timestampMs: number): string;
|
|
39
|
+
/** Sign an arbitrary string with the session private key, returning lowercase hex. */
|
|
40
|
+
export declare function signMessage(privateKey: Uint8Array, message: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session keypair crypto for noncustodial request signing.
|
|
3
|
+
*
|
|
4
|
+
* On login the SDK generates a random ed25519 keypair locally. The public key
|
|
5
|
+
* is registered with the server (bound by the wallet's signature on the
|
|
6
|
+
* challenge); every subsequent authenticated request is signed with the
|
|
7
|
+
* private key. The server verifies the signature against the stored public
|
|
8
|
+
* key — it never sees the private key, so it cannot mint requests as the user.
|
|
9
|
+
*/
|
|
10
|
+
import { ed25519 } from "@noble/curves/ed25519";
|
|
11
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
12
|
+
import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/hashes/utils";
|
|
13
|
+
/**
|
|
14
|
+
* Generate a fresh ed25519 session keypair.
|
|
15
|
+
*
|
|
16
|
+
* `@noble/curves` sources entropy from `crypto.getRandomValues`. Environments
|
|
17
|
+
* without it (very old Node without webcrypto, locked-down sandboxes) will
|
|
18
|
+
* throw — we surface a clear error rather than producing a weak key.
|
|
19
|
+
*/
|
|
20
|
+
export function generateSessionKeypair() {
|
|
21
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
22
|
+
throw new Error("Secure randomness (crypto.getRandomValues) is unavailable; cannot generate a session keypair.");
|
|
23
|
+
}
|
|
24
|
+
const privateKey = ed25519.utils.randomPrivateKey();
|
|
25
|
+
const publicKey = ed25519.getPublicKey(privateKey);
|
|
26
|
+
return { publicKey, privateKey };
|
|
27
|
+
}
|
|
28
|
+
export function publicKeyHex(keypair) {
|
|
29
|
+
return bytesToHex(keypair.publicKey);
|
|
30
|
+
}
|
|
31
|
+
export function privateKeyHex(keypair) {
|
|
32
|
+
return bytesToHex(keypair.privateKey);
|
|
33
|
+
}
|
|
34
|
+
/** Reconstruct a keypair from its hex-encoded halves (e.g. restored from storage). */
|
|
35
|
+
export function keypairFromHex(publicKeyHex, privateKeyHex) {
|
|
36
|
+
return {
|
|
37
|
+
publicKey: hexToBytes(publicKeyHex),
|
|
38
|
+
privateKey: hexToBytes(privateKeyHex),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Lowercase-hex sha256 of the given bytes. Use over an empty array for no-body requests. */
|
|
42
|
+
export function sha256Hex(data) {
|
|
43
|
+
return bytesToHex(sha256(data));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Canonical per-request signing string. Mirrors the server
|
|
47
|
+
* (`handlers::auth::compose_signing_string`):
|
|
48
|
+
* `METHOD\nPATH_WITH_QUERY\nTIMESTAMP_MS\nSHA256_BODY_HEX`.
|
|
49
|
+
*/
|
|
50
|
+
export function composeSigningString(method, pathWithQuery, timestampMs, bodySha256Hex) {
|
|
51
|
+
return `${method.toUpperCase()}\n${pathWithQuery}\n${timestampMs}\n${bodySha256Hex}`;
|
|
52
|
+
}
|
|
53
|
+
/** Canonical WebSocket handshake signing string: `WS-AUTH\n<pubkey-hex>\n<ts>`. */
|
|
54
|
+
export function composeWsSigningString(publicKeyHex, timestampMs) {
|
|
55
|
+
return `WS-AUTH\n${publicKeyHex}\n${timestampMs}`;
|
|
56
|
+
}
|
|
57
|
+
/** Sign an arbitrary string with the session private key, returning lowercase hex. */
|
|
58
|
+
export function signMessage(privateKey, message) {
|
|
59
|
+
return bytesToHex(ed25519.sign(utf8ToBytes(message), privateKey));
|
|
60
|
+
}
|
package/dist/sdk.d.ts
CHANGED
|
@@ -21,21 +21,26 @@ export declare class MonacoSDKImpl implements MonacoSDK {
|
|
|
21
21
|
private readonly network;
|
|
22
22
|
private readonly chain;
|
|
23
23
|
/**
|
|
24
|
-
* Propagate the
|
|
24
|
+
* Propagate the session keypair (or `undefined` to clear) to all APIs and
|
|
25
|
+
* the WebSocket client.
|
|
25
26
|
*/
|
|
26
|
-
private
|
|
27
|
+
private propagateSession;
|
|
28
|
+
/** Extract the session keypair from an auth state. */
|
|
29
|
+
private sessionFromAuthState;
|
|
27
30
|
constructor(cfg: SDKConfig);
|
|
28
31
|
/**
|
|
29
32
|
* Authenticate the user
|
|
30
33
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* - `
|
|
34
|
-
*
|
|
34
|
+
* Generates a session keypair, has the wallet authorize it, and returns an
|
|
35
|
+
* AuthState object containing:
|
|
36
|
+
* - `sessionPublicKey` / `sessionPrivateKey`: the session keypair used to
|
|
37
|
+
* sign subsequent requests (the private key is the credential — persist it
|
|
38
|
+
* to survive reloads without re-prompting the wallet)
|
|
39
|
+
* - `expiresAt`: When the session expires
|
|
35
40
|
* - `user`: User information
|
|
36
41
|
*
|
|
37
|
-
* Note: Use `sdk.logout()` to revoke the
|
|
38
|
-
* `sdk.auth.
|
|
42
|
+
* Note: Use `sdk.logout()` to revoke the session and clean up, or call
|
|
43
|
+
* `sdk.auth.revokeSession()` directly to just revoke.
|
|
39
44
|
*
|
|
40
45
|
* @param clientId - The client ID for authentication
|
|
41
46
|
* @param options - Optional configuration
|
|
@@ -49,13 +54,10 @@ export declare class MonacoSDKImpl implements MonacoSDK {
|
|
|
49
54
|
* // Login and auto-connect WebSocket
|
|
50
55
|
* const authState = await sdk.login(clientId, { connectWebSocket: true });
|
|
51
56
|
*
|
|
52
|
-
* // Manual WebSocket connection
|
|
53
|
-
* await sdk.ws.connect();
|
|
54
|
-
*
|
|
55
57
|
* // Later, to revoke:
|
|
56
|
-
* await sdk.auth.
|
|
58
|
+
* await sdk.auth.revokeSession(); // ✅
|
|
57
59
|
* // Or revoke and disconnect WebSocket:
|
|
58
|
-
* await sdk.logout(); // ✅ Calls
|
|
60
|
+
* await sdk.logout(); // ✅ Calls revokeSession internally and disconnects WebSocket
|
|
59
61
|
* ```
|
|
60
62
|
*/
|
|
61
63
|
login(clientId: string, options?: {
|
|
@@ -77,19 +79,24 @@ export declare class MonacoSDKImpl implements MonacoSDK {
|
|
|
77
79
|
/**
|
|
78
80
|
* Log the user out
|
|
79
81
|
*
|
|
80
|
-
* This method revokes the
|
|
82
|
+
* This method revokes the session (if authenticated), disconnects all authenticated
|
|
81
83
|
* WebSocket channels, and clears the local auth state.
|
|
82
|
-
* It internally calls `auth.
|
|
84
|
+
* It internally calls `auth.revokeSession()` to invalidate the session on the server.
|
|
83
85
|
*
|
|
84
86
|
* @example
|
|
85
87
|
* ```typescript
|
|
86
88
|
* await sdk.logout();
|
|
87
|
-
* //
|
|
89
|
+
* // Session is revoked, authenticated WebSockets disconnected, and local state cleared
|
|
88
90
|
* ```
|
|
89
91
|
*/
|
|
90
92
|
logout(): Promise<void>;
|
|
91
93
|
/**
|
|
92
|
-
* Refresh the
|
|
94
|
+
* Refresh the current session, extending its expiry.
|
|
95
|
+
*
|
|
96
|
+
* Signs a refresh request with the active session key and updates the local
|
|
97
|
+
* `expiresAt`. The session keypair is unchanged. If no session is active, or
|
|
98
|
+
* the session has expired/been revoked, this throws.
|
|
99
|
+
*
|
|
93
100
|
* @returns The updated authentication state
|
|
94
101
|
*/
|
|
95
102
|
refreshAuth(): Promise<AuthState>;
|
package/dist/sdk.js
CHANGED
|
@@ -13,6 +13,16 @@ import { TradingAPIImpl } from "./api/trading";
|
|
|
13
13
|
import { VaultAPIImpl } from "./api/vault";
|
|
14
14
|
import { APIError, InvalidConfigError, InvalidStateError } from "./errors";
|
|
15
15
|
import { resolveApiUrl, resolveWsUrl } from "./networks";
|
|
16
|
+
/** Validate a user-supplied URL override, returning it unchanged when valid. */
|
|
17
|
+
function validateUrl(value, field) {
|
|
18
|
+
try {
|
|
19
|
+
new URL(value);
|
|
20
|
+
}
|
|
21
|
+
catch (_e) {
|
|
22
|
+
throw new InvalidConfigError(`${field} must be a valid URL, got: ${value}`, field);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
16
26
|
export class MonacoSDKImpl {
|
|
17
27
|
auth;
|
|
18
28
|
delegatedAgents;
|
|
@@ -33,22 +43,30 @@ export class MonacoSDKImpl {
|
|
|
33
43
|
network;
|
|
34
44
|
chain;
|
|
35
45
|
/**
|
|
36
|
-
* Propagate the
|
|
46
|
+
* Propagate the session keypair (or `undefined` to clear) to all APIs and
|
|
47
|
+
* the WebSocket client.
|
|
37
48
|
*/
|
|
38
|
-
|
|
39
|
-
this.auth.
|
|
40
|
-
this.delegatedAgents.
|
|
41
|
-
this.applications.
|
|
42
|
-
this.fees.
|
|
43
|
-
this.vault.
|
|
44
|
-
this.trading.
|
|
45
|
-
this.market.
|
|
46
|
-
this.marginAccounts.
|
|
47
|
-
this.positions.
|
|
48
|
-
this.profile.
|
|
49
|
-
this.orderbook.
|
|
50
|
-
this.trades.
|
|
51
|
-
this.ws.
|
|
49
|
+
propagateSession(credentials) {
|
|
50
|
+
this.auth.setSessionKeypair(credentials);
|
|
51
|
+
this.delegatedAgents.setSessionKeypair(credentials);
|
|
52
|
+
this.applications.setSessionKeypair(credentials);
|
|
53
|
+
this.fees.setSessionKeypair(credentials);
|
|
54
|
+
this.vault.setSessionKeypair(credentials);
|
|
55
|
+
this.trading.setSessionKeypair(credentials);
|
|
56
|
+
this.market.setSessionKeypair(credentials);
|
|
57
|
+
this.marginAccounts.setSessionKeypair(credentials);
|
|
58
|
+
this.positions.setSessionKeypair(credentials);
|
|
59
|
+
this.profile.setSessionKeypair(credentials);
|
|
60
|
+
this.orderbook.setSessionKeypair(credentials);
|
|
61
|
+
this.trades.setSessionKeypair(credentials);
|
|
62
|
+
this.ws.setSessionKeypair(credentials);
|
|
63
|
+
}
|
|
64
|
+
/** Extract the session keypair from an auth state. */
|
|
65
|
+
sessionFromAuthState(authState) {
|
|
66
|
+
return {
|
|
67
|
+
publicKey: authState.sessionPublicKey,
|
|
68
|
+
privateKey: authState.sessionPrivateKey,
|
|
69
|
+
};
|
|
52
70
|
}
|
|
53
71
|
constructor(cfg) {
|
|
54
72
|
// Validate network - must be a preset
|
|
@@ -67,8 +85,8 @@ export class MonacoSDKImpl {
|
|
|
67
85
|
catch (_e) {
|
|
68
86
|
throw new InvalidConfigError(`seiRpcUrl must be a valid URL, got: ${cfg.seiRpcUrl}`, "seiRpcUrl");
|
|
69
87
|
}
|
|
70
|
-
//
|
|
71
|
-
const wsUrl = resolveWsUrl(this.network);
|
|
88
|
+
// Resolve the WebSocket URL: explicit override (validated) or network preset.
|
|
89
|
+
const wsUrl = cfg.wsUrl ? validateUrl(cfg.wsUrl, "wsUrl") : resolveWsUrl(this.network);
|
|
72
90
|
// Use Sei mainnet chain only for "mainnet" network, testnet for everything else
|
|
73
91
|
const chain = this.network === "mainnet" ? sei : seiTestnet;
|
|
74
92
|
this.chain = chain;
|
|
@@ -78,8 +96,8 @@ export class MonacoSDKImpl {
|
|
|
78
96
|
chain,
|
|
79
97
|
transport,
|
|
80
98
|
});
|
|
81
|
-
// Resolve the API URL (
|
|
82
|
-
const apiUrl = resolveApiUrl(this.network);
|
|
99
|
+
// Resolve the API URL: explicit override (validated) or network preset.
|
|
100
|
+
const apiUrl = cfg.apiUrl ? validateUrl(cfg.apiUrl, "apiUrl") : resolveApiUrl(this.network);
|
|
83
101
|
// Validate wallet client chain if provided
|
|
84
102
|
if (cfg.walletClient) {
|
|
85
103
|
if (cfg.walletClient.chain?.id !== chain.id) {
|
|
@@ -107,14 +125,16 @@ export class MonacoSDKImpl {
|
|
|
107
125
|
/**
|
|
108
126
|
* Authenticate the user
|
|
109
127
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* - `
|
|
113
|
-
*
|
|
128
|
+
* Generates a session keypair, has the wallet authorize it, and returns an
|
|
129
|
+
* AuthState object containing:
|
|
130
|
+
* - `sessionPublicKey` / `sessionPrivateKey`: the session keypair used to
|
|
131
|
+
* sign subsequent requests (the private key is the credential — persist it
|
|
132
|
+
* to survive reloads without re-prompting the wallet)
|
|
133
|
+
* - `expiresAt`: When the session expires
|
|
114
134
|
* - `user`: User information
|
|
115
135
|
*
|
|
116
|
-
* Note: Use `sdk.logout()` to revoke the
|
|
117
|
-
* `sdk.auth.
|
|
136
|
+
* Note: Use `sdk.logout()` to revoke the session and clean up, or call
|
|
137
|
+
* `sdk.auth.revokeSession()` directly to just revoke.
|
|
118
138
|
*
|
|
119
139
|
* @param clientId - The client ID for authentication
|
|
120
140
|
* @param options - Optional configuration
|
|
@@ -128,24 +148,15 @@ export class MonacoSDKImpl {
|
|
|
128
148
|
* // Login and auto-connect WebSocket
|
|
129
149
|
* const authState = await sdk.login(clientId, { connectWebSocket: true });
|
|
130
150
|
*
|
|
131
|
-
* // Manual WebSocket connection
|
|
132
|
-
* await sdk.ws.connect();
|
|
133
|
-
*
|
|
134
151
|
* // Later, to revoke:
|
|
135
|
-
* await sdk.auth.
|
|
152
|
+
* await sdk.auth.revokeSession(); // ✅
|
|
136
153
|
* // Or revoke and disconnect WebSocket:
|
|
137
|
-
* await sdk.logout(); // ✅ Calls
|
|
154
|
+
* await sdk.logout(); // ✅ Calls revokeSession internally and disconnects WebSocket
|
|
138
155
|
* ```
|
|
139
156
|
*/
|
|
140
157
|
async login(clientId, options) {
|
|
141
|
-
|
|
142
|
-
this.authState
|
|
143
|
-
accessToken: response.accessToken,
|
|
144
|
-
refreshToken: response.refreshToken,
|
|
145
|
-
expiresAt: response.expiresAt,
|
|
146
|
-
user: response.user,
|
|
147
|
-
};
|
|
148
|
-
this.propagateAccessToken(this.authState.accessToken);
|
|
158
|
+
this.authState = await this.auth.authenticate(clientId);
|
|
159
|
+
this.propagateSession(this.sessionFromAuthState(this.authState));
|
|
149
160
|
// Auto-connect WebSocket if requested
|
|
150
161
|
if (options?.connectWebSocket) {
|
|
151
162
|
await this.ws.connect();
|
|
@@ -168,59 +179,63 @@ export class MonacoSDKImpl {
|
|
|
168
179
|
*/
|
|
169
180
|
setAuthState(authState) {
|
|
170
181
|
this.authState = authState;
|
|
171
|
-
this.
|
|
182
|
+
this.propagateSession(this.sessionFromAuthState(authState));
|
|
172
183
|
}
|
|
173
184
|
/**
|
|
174
185
|
* Log the user out
|
|
175
186
|
*
|
|
176
|
-
* This method revokes the
|
|
187
|
+
* This method revokes the session (if authenticated), disconnects all authenticated
|
|
177
188
|
* WebSocket channels, and clears the local auth state.
|
|
178
|
-
* It internally calls `auth.
|
|
189
|
+
* It internally calls `auth.revokeSession()` to invalidate the session on the server.
|
|
179
190
|
*
|
|
180
191
|
* @example
|
|
181
192
|
* ```typescript
|
|
182
193
|
* await sdk.logout();
|
|
183
|
-
* //
|
|
194
|
+
* // Session is revoked, authenticated WebSockets disconnected, and local state cleared
|
|
184
195
|
* ```
|
|
185
196
|
*/
|
|
186
197
|
async logout() {
|
|
187
|
-
if (this.authState
|
|
198
|
+
if (this.authState) {
|
|
188
199
|
try {
|
|
189
|
-
await this.auth.
|
|
200
|
+
await this.auth.revokeSession();
|
|
190
201
|
}
|
|
191
202
|
catch (error) {
|
|
192
203
|
// Log but don't throw - we want to clear the local state regardless
|
|
193
|
-
console.warn("Failed to revoke
|
|
204
|
+
console.warn("Failed to revoke session on logout:", error);
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
207
|
this.authState = undefined;
|
|
197
|
-
this.
|
|
208
|
+
this.propagateSession(undefined);
|
|
198
209
|
this.ws.disconnect();
|
|
199
210
|
}
|
|
200
211
|
/**
|
|
201
|
-
* Refresh the
|
|
212
|
+
* Refresh the current session, extending its expiry.
|
|
213
|
+
*
|
|
214
|
+
* Signs a refresh request with the active session key and updates the local
|
|
215
|
+
* `expiresAt`. The session keypair is unchanged. If no session is active, or
|
|
216
|
+
* the session has expired/been revoked, this throws.
|
|
217
|
+
*
|
|
202
218
|
* @returns The updated authentication state
|
|
203
219
|
*/
|
|
204
220
|
async refreshAuth() {
|
|
205
|
-
if (!this.authState
|
|
206
|
-
throw new APIError("No
|
|
221
|
+
if (!this.authState) {
|
|
222
|
+
throw new APIError("No active session to refresh", {
|
|
207
223
|
endpoint: "auth/refresh",
|
|
208
224
|
statusCode: StatusCodes.UNAUTHORIZED,
|
|
209
225
|
});
|
|
210
226
|
}
|
|
211
227
|
try {
|
|
212
|
-
const response = await this.auth.
|
|
228
|
+
const response = await this.auth.refreshSession();
|
|
213
229
|
this.authState = {
|
|
214
230
|
...this.authState,
|
|
215
|
-
accessToken: response.accessToken,
|
|
216
231
|
expiresAt: response.expiresAt,
|
|
217
232
|
};
|
|
218
|
-
this.propagateAccessToken(this.authState.accessToken);
|
|
219
233
|
return this.authState;
|
|
220
234
|
}
|
|
221
235
|
catch (error) {
|
|
222
|
-
// If refresh fails,
|
|
236
|
+
// If refresh fails, the session is no longer usable.
|
|
223
237
|
this.authState = undefined;
|
|
238
|
+
this.propagateSession(undefined);
|
|
224
239
|
throw error;
|
|
225
240
|
}
|
|
226
241
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xmonaco/core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7-develop.34bd452",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
"viem": "^2.45.2"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@0xmonaco/contracts": "0.8.
|
|
27
|
-
"@0xmonaco/types": "0.8.
|
|
26
|
+
"@0xmonaco/contracts": "0.8.7-develop.34bd452",
|
|
27
|
+
"@0xmonaco/types": "0.8.7-develop.34bd452",
|
|
28
|
+
"@noble/curves": "^1.9.1",
|
|
29
|
+
"@noble/hashes": "^1.8.0",
|
|
28
30
|
"http-status-codes": "^2.3.0"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|